Welcome

Welcome to TotalCross. This tutorial assumes that you know how to write Java programs. If not, I suggest that you take a basic course in Java at http://java.sun.com/docs/books/tutorial/java/index.html
However, you’ll have to forget the JDK API and use the TotalCross API. Make a good read of the API javadocs: they will show you the differences between the JDK and the TotalCross API.
Some conventions used in this tutorial:
  • When a code sample has ... (ellipses) between two parts, it means that some part of that code has been omitted.
  • Measurements are always in pixels, unless otherwise specified.
This tutorial follows the Object Oriented naming convention: a class containing methods (~functions) and properties or fields (~variables).
TC refers to TotalCross.
TCVM refers to the TotalCross virtual machine.
The term “main class” always refers to the class that extends totalcross.ui.MainWindow or implements totalcross.MainClass.
The term “%TOTALCROSS_HOME%” refers to the directory where the TotalCross SDK is located. The default location is C:\TotalCross3SDK. But you can change it during installation.
Please read the Copyright terms in Appendix A↓. By reading this text, you’re agreeing with them.

1 TotalCross Overview

Thank you for your interest in TotalCross. If you don’t know where to start, here’s a quick overview of the platform:

Supported Platforms

  • Android 2.3.3 and above (API level 10).
  • iOS 5.0 and above.
  • Windows XP and above.
  • Linux 32 bits (we only assure that it works on Debian distribution).
  • Browser as Java applet (JDK 1.1 and above). Note that in this case you must enable some permission so that you can use files and, consequently, Litebase.
  • Java SE.
  • Windows Phone 8.

Virtual Machine Features

The TotalCross Virtual Machine (TCVM) is a shared library written from scratch, and has the following features:
  • It interprets a proprietary set of opcodes instead of Java Bytecodes.
  • It is a register-based VM, not stack-based as Java, which results in better performance.
  • It has support for real multi-threading. Note that the TotalCross API does not supports concurrency, which must be implemented by your own.
  • The TotalCross class (tclass) files stores internal information in little endian, since its the most widely used format of actual microprocessors.
  • The tclass files are highly optimized to save space. For instance, the constant pool (where strings, constants, and identifiers are stored) is shared among all deployed classes, and each class entry is compressed using zlib.
  • Supports headless applications (like daemon applications, without user interface): just implement the interface totalcross.MainClass and this class will be loaded by the TCVM. The appStarting() and appEnding() methods are called and the application exits.
  • Supports the method finalize(), ran every time the garbage collector (gc) finishes its job. There’s a limitation: no objects can be created inside a finally method, otherwise the method will silently abort itself. Optionally, to improve GC’s performance, you can define in your class a public non-static field named dontFinalize that, if present and set to true, will skip the finalize call. In most cases, finalize() is used to ensure that a class that holds system resources (like file or socket) and should be closed to release these resources is always closed, either because the programmer forgot to do it himself or because the program was halted by an exception.
    Note that you must define the field dontFinalize and set it to true when the close method is run for the first time. Otherwise the gc will try to finalize an object that was already closed by the programmer, which may cause trouble. Doing so also speeds up the gc.
The TotalCross VM also has a drawback:
  • It does not support the float type, only double. This option was adopted because all actual PDA processors have a math co-processor, and also because the vast majority of mobile applications are not scientific programs. During our research, we found that float types are two times faster than double, but this small performance difference does not make up for the overhead needed to add float type support to the virtual machine. The change from float to double will be done by the translator to let legacy applications work, however, you should change your application to use double, since there’s no benefit by using float.

Thread support

TotalCross supports preemptive threads using the native thread mechanism of each supported platform. On Android, iOS, and Linux, it uses pthread, and on Windows, it uses the quite well documented thread api.
The API does not support concurrency. If your program needs to access the same object from many threads, you must use the synchronized keyword. The support for synchronized is limited: it does not support synchronized methods, neither classes, neither standard objects. You must use the synchronized(object), and the only object type that can be used as parameter is the totalcross.util.concurrent.Lock. If you use synchronized(this), the tc.Deploy will abort during deploy; if you use synchronized with an object from any other class besides the Lock class, a RuntimeException will be thrown when your program runs in the TotalCross virtual machine. Moreover, using the synchronized keyword before a method will be useless: it will be ignored by the VM. Note that these problems will not occur when running on Java desktop, only when running on TCVM. Here’s a sample that shows how to use it:
\lstinputlisting[label=samplecode,caption=A sample]companion_resources/listings/TestConcurrent.java
In the sample above, commenting out the line marked with *****, the log list box will be filled randomly by the threads. With the lock, it will be filled in sequence, because each thread will gain the lock once, and the other threads will have to wait the main loop of the lock owner finish before starting their loops. There’s no limit in the number of locks used.
Generally speaking, you can create a thread to listen to a socket or a file or even a Litebase table in background, but be aware that if you try to access the same resource by different threads your application might just blow up. We also don’t recommend running the user interface in a background thread, due to system event concurrency. Threads should be used for I/O and other tasks, but not for showing user interface screens that could receive events.

Graphics, Palette and Color

TotalCross has a graphics engine written from scratch, and some important performance-tailored decisions were taken.
Regardless of the device’s color depth, the screen and images are stored in a 24 bpp RGB array. All drawings are made into a single off screen, which is then converted on the fly to the device’s screen color depth when the updateScreen() method is called. Note that since 2011, no devices with 8 bpp are released to the market; all of them use at least 16 bpp (65536 colors).
The Graphics class supports real clipping, which allowed us to support containers that automatically show scrollbars if components are placed beyond its limits.
TotalCross also supports screen rotation and collapsible input area. If the user interface is implemented using only relative coordinates, it will automatically reposition itself whenever the screen resolution is changed.
Note that aControl.setRect(getClientRect()) should never be used, otherwise the automatic repositioning will not work. Instead, aControl.setRect(LEFT, TOP, FILL, FILL) should be used to produce the same result without affecting the repositioning. If you really have to use getClientRect(), you must also override the reposition() method to support screen rotation. (see the WorldWatch sample).
Colors are represented by int values in the 0xRRGGBB format. A null color is represented by the value -1.

Images

Images in TotalCross supports transparency (also known as alpha-channel). The best way to show images is to generate a PNG image from a vectorized image through Photoshop or any other good editor. Prefer creating a big image (for example, 96x96), then decrease its size at runtime using Image.getSmoothScaledInstance.

Inheritance and Delegation event models

TotalCross supports both Inheritance (Java 1.0) and Delegation (Java 1.1) event models. However, the usage of the later one is not recommended. The Inheritance model will make your code smaller and faster, but there are some situations that require the usage of the Delegation model.

Security

TotalCross applications are currently impossible to be decompiled, because, as mentioned before, TotalCross uses a proprietary set of opcodes instead of Java Bytecodes. The translation between Java Bytecodes to TotalCross opcodes is done automatically when the application is deployed.
However, this also means that you cannot retrieve your application’s source files from the deployed application, so don’t forget to backup your source files!

Getting Started

Be sure to read the API javadocs at %TOTALCROSS_HOME%/docs/html/index.html.
The javadocs and this reference manual are complementary, and the javadocs are updated more often than this text. So make sure to read them for more information.
There are some examples in the %TOTALCROSS_HOME%/src/java/tc/samples directories. These samples covers the usage of a great portion of TotalCross’ class library, so it’s a good source of information from where you can start your applications.
Our official forum (http://forum.totalcross.net) is a good place to seek for help from fellow TotalCross users and share your knowledge.

Bug Reporting

If you’d like to report a bug or problem within the TotalCross SDK – including, but not limited to:
  • Documentation problems, grammar errors, or typos.
  • TotalCross platform bug.
  • Sample bug.
You should first take a look at the TotalCross changelog
(http://www.superwaba.com.br/en/changelogs.asp) and check if your bug was already fixed on a later version of the TotalCross SDK.
You may then use the forum to report the bug, however, its not guaranteed that we will fix the bug. If you want the bug fixed in a few days and receive a fixed virtual machine, you must purchase a support plan (http://www.superwaba.com.br/en/suporte.asp) at the site.
When opening an issue, it’s very important to give all the details so that its correction will be faster. Here’s a rough template of a bug report for TotalCross:
  • TotalCross version, e.g. 1.0 beta 4.
  • Platform used and OS version, e.g. JDK 1.8, Windows XP, Windows Phone 8
  • Devices used, e.g. Android, iPhone, Java Standard Edition (desktop).
  • Device model.
  • Bug description.
  • How to reproduce the bug?
For documentation problems and such, just pointing the resource and the problem is enough.
The purchased support can also be used to solve doubts about using TotalCross.

2 The TotalCross SDK

The TotalCross SDK Java files are covered by the LGPL version 3. By installing the SDK you are agreeing with this. The TotalCross virtual machine follows another license.
The VM provided in the SDK is a demo version. It will work for 80 non-contiguous hours, regardless of the platform. The remaining time is updated every 5 seconds.
On Android and iOS, it’s only necessary to first uninstall and then reinstall TotalCross (or your application together with it), so that you will have more 80 hours to test. This is NOT considered a license violation.
On Win32 (Windows, XP, Vista, Seven, or 8), this can only be done by reinstalling Windows.
In order to deploy applications, the JDK 1.8 is recommended. It’s a good idea to install the latest version of Java 1.8.
In most situations, you will install several programs that will share the same TCVM. For instance, you can install 10 or more samples and a single virtual machine. However, if you’re deploying an application to your costumer, you can package the VM with your application, using the tc.Deploy’s /p option. In this case, a single package is needed.

Directory structure

The SDK has the following structure:
%TOTALCROSS_HOME%
-dist
  • +samples
    • +vm
+docs
+etc
+src
Inside dist, we have:
  • tc.jar : This file contains all totalcross.* base classes and deploy tools, and it should be added to the classpath of your chosen IDE. Note that TCFont.tcz is needed to run an application on Java. It also contains Litebase classes.
  • tcweb.jar : This file contains all totalcross.* base classes and fonts, to be used if you want to run the application in a browser. This one is greater in size than tc.jar because of the fonts. That is, it is not necessary to add TCFont.tcz. It also contains Litebase classes.
  • samples: Installation files for all samples. Litebase samples are also inside, in the folder litebase. Lets take the TotalCrossAPI sample, located at TotalCrossSDK\dist\
    samples\TotalCrossAPI
    . Inside there’s an install folder, and below the following folders:
    • applet: contains some .html files (representing most used devices) and the application’s jar. Using a multi-tabbed browser, you should close it every time you open one of the samples .html, otherwise errors may occur. You should also give special permission for file usage so that it is possible to manipulate files and Litebase.
    • win32: there are two files, an .exe and a .tcz, which must be copied to the same folder where the TotalCross VM for Win32 is located, or to a sub folder. The executable will search for the VM on these locations.
    If your desktop has the SDK installed, you may run the application from any place, as long as the environment variables set during the installation are not changed and the SDK installation folder is not moved. If your application can’t found the SDK (or the right SDK if you have more than one installed), make sure that the environment variable TOTALCROSS3_HOME is pointing to the desired TotalCross installation folder. On some systems (Windows 8, for instance), TOTALCROSS3_HOME must be set both in user and system environment variables. If there is no TOTALCROSS3_HOME, TotalCross will search for the variable used in older TotalCross versions, which is TOTALCROSS_HOME.
    • wp8: a .xap file which includes the sample, TotalCross, and Litebase. Note that to install the sample you must enable your phone to allow development mode.
    • linux: a .tcz file and a binary file. They must be copied to where the VM is, or a folder below.
    • android: an .apk file that contains the binary and the .tcz files.
    Note: There is no iOS folder since to install an application on iOS you MUST have an Apple key. It will be explained later on this manual.
  • vm: virtual machine files that which will interpret your TotalCross code on the device and Litebase library. Below are instructions of how to install them in each platform:
    • TCBase.tcz, TCFont.tcz, TCUI.tcz, and LitebaseLib.tcz. These files are necessary to run TotalCross and Litebase on Windows 32 and Linux.
    • iOS: contains the TotalCross.ipa file which must be used to generate the .ipa file for your application and the samples if you have an Apple key. It already includes Litebase.
    • win32: have the files TCVM.dll and Litebase.dll on the same folder of the application or install TotalCross properly in your operational system.
    • wp8: a TotalCross.xap that must be used to generate the .xap of your application. It already includes Litebase.
    • android: just install the file TotalCross.apk (adb install -r TotalCross.apk), which already includes Litebase.
    • linux: executables (.deb) for TotalCross and Litebase to be used on linuxes.
Inside docs, we have:
  • TotalCross Companion.pdf.
  • Litebase Companion.pdf.
  • The folder html, which contains the generated javadocs for TotalCross and Litebase. Open the files index.html to see the javadocs start page.
  • An ant build sample (build.xml) to deploy a TotalCross application.
Inside etc, we have the following folders:
  • images: contains images used by the SDK.
  • launchers: contains files used by tc.Deploy.
  • security: contains signature files.
  • tools: contains tools used by tc.Deploy. Inside this folder, you may use only the folder with some Android tools used to install the application or debug on the device.
Inside src:
  • tc/samples: the sources files of all TotalCross samples.
  • lb/samples: the sources files of all Litebase samples.
  • totalcross: the sources files of the TotalCross API used on the desktop.

Running the Samples

The TotalCross SDK comes with various sample applications along with their source code. The samples are simple and most of them address a specific feature. They were designed to show you the TotalCross main features and to provide a starting point for your own applications.
The built samples can be found on %TOTALCROSS_HOME%/dist/samples. Just follow the installation instructions from chapter 6 and run the sample on your target platform.

3 Building and deploying your first TotalCross application

TotalCross applications are developed almost exactly like Java applications, so anyone familiar with Java should be able to start small TotalCross applications in a few minutes.
However, this similarity is often also source of problems, so before writing any code, we must first be aware of the differences between Java and TotalCross development.
  • The entry point of a TotalCross application is always a class that extends the class
    MainWindow
    (directly or indirectly) or implements the interface MainClass. It is not possible to directly run TotalCross applications in Java, in this case, you must use the launcher provided with the SDK. (see Chapter 4 for details about totalcross.Launcher).
  • For your convenience, the TotalCross package totalcross.lang is directly mapped to the Java package java.lang. Therefore, you may refer to classes from this package, such as Object or String, like you would in any Java application, without requiring any explicit import.
    Unfortunately this feature can be misleading, because the opposite is NOT true. The
    java.lang
    package does have members (interfaces, classes, exceptions, or errors) that are not available in totalcross.lang – and some of the members that are available, may not implement all the methods provided by the homonymous member. Some examples are the Integer class and the method String.replaceFirst(), which are not implemented by TotalCross.
    This is one of the most common pitfalls when developing TotalCross applications using a Java IDE, because its content assist will show classes and methods that are not implemented by TotalCross, and therefore, not available on the device.
    To avoid this problem, the TotalCross deployer verifies the class files, reporting any invalid references found. The deployer will throw an exception if you try to deploy an application with references to classes not available in totalcross.lang.
  • You should also check TotalCross Javadoc to see if the desired totalcross.lang reference is available so that you won’t have to rewrite parts of your program during the deployment process.
  • You CANNOT use nor import totalcross.lang package in desktop. When tc.Deploy is called, all references to java.lang are replaced by totalcross.lang automatically.
  • To use collection classes, the approch is similar for java.lang classes. You must use java.util classes and tc.Deploy will convert them to the correspondent totalcross.util classes. Notice that in this case some fields and methods found in the Java classes might also be missing in TotalCross classes. Therefore, in this case you must also check the javadocs.
  • TotalCross is compatible only with the Java 1.8 class file format or below, therefore the class files MUST be generated targeting the JDK 1.8 and the source files must be compatible with JDK 1.8.
    If you’re compiling using javac from command line (or an ANT file), add this to your command line: -target 1.8 -source 1.8.
    If you’re using a Java IDE, look for its Java Compiler settings and set its compliance level to 1.8. But please notice that it might happen that the .class generated by an IDE such as Eclipse is not fully compatible with tc.Deploy and an unexpected problem might happen due to this fact. If this is the case, try using javac instead (if your program has a lot of classes, use an ANT file to facilitate the compilation process).
    Again, the deployer will throw an exception if it detects an invalid class file format.

Building a sample application

For our first application, let’s make a typical “Hello World” application using just your preferred plain text editor.
First, make sure you have a JDK installed (preferably the JDK 8) and the environment variables are set. There should be a JAVA_HOME variable set with the installation path of your JDK, like C:\Program Files\Java\jdk1.8.0_25.
Note that some Java versions have problem when there is a space in the folder name, such as Program Files or Arquivos de Programas. If this is the case, just uninstall Java and reinstall it in a folder without space in its name.
And another variable named PATH, which should contain a list of paths, separated by semicolons, with the path to the JDK’s bin directory, like %JAVA_HOME%\bin. If you don’t do this, you will get the error: [preverify] JAR file creation failed with error -1.
You may also add the path to the TotalCross .jar file (located in TotalCrossSDK/dist/tc. jar) to your CLASSPATH environment variable (or create one if the variable does not exists).
The usage of the CLASSPATH variable is usually not advised, because it may cause conflicts when different classes share the same package structure. Creating an ANT build file and using the command line option -classpath is a better choice.
Open your text editor and write the code below, saving the file as HelloWorld.java:
\lstinputlisting[label=samplecode,caption=A sample]companion_resources/listings/HelloWorld.java
Don’t worry about understanding the code, this will be covered later.
Now open a command shell at the directory where the HelloWorld.java was saved, and execute the following command:
javac -target 1.8 -source 1.8 HelloWorld.java
Remember, if you preferred to not use the CLASSPATH variable, you must ALWAYS specify the path to the tc.jar file using the -classpath option, like this:
javac -classpath C:\TotalCrossSDK\dist\tc.jar -target 1.8 -source 1.8 HelloWorld.java
Otherwise, the Java compiler won’t be able to find the TotalCross classes used by the application.
If successful, the Java compiler should finish silently, creating a new file named HelloWorld. class.

Running with the Launcher (on JDK)

Using the launcher, we can run our application and take a look at the result. Using a command shell, execute the following command:
java -classpath .;%CLASSPATH% totalcross.Launcher HelloWorld
Here we are executing the Java application totalcross.Launcher, passing the name of our main class (case sensitive) as argument.
In this case, the usage of the option -classpath is needed because the HelloWorld.class is not in a valid package directory structure, so we must add the current folder to the classpath.
The following window should appear on your screen:
figure companion_resources/images/companion_HelloWorld_JDK.png
Congratulations, you just started your first TotalCross application running on JDK!

Deploying and running on Win32

The launcher provided us a quick way to look at our application’s appearance, but that’s still a Java application. To actually deploy our application to all platforms supported by TotalCross, go back to the command shell and execute this command:
java tc.Deploy HelloWorld -all
Remember to use the -classpath option with the tc.jar path if you’re not using the CLASSPATH environment variable.
This time, we are executing the program tc.Deploy, passing the name of our main class as the first argument, and the platforms to deploy as the second argument – in this case all platforms. (see Chapter 5 for more information about the Deployment process).
A new folder named install will be created, and inside it you should find several folders, each one containing the installer (or the executable) for each supported platform.
We should be able now to run the Win32 version of our application, so let’s move to ./install/ win32 and run the executable HelloWorld.exe, which should look like this:
figure companion_resources/images/companion_HelloWorld_Win32.png
The default screen size is 240x320. You can change the screen size and position by editing the .exe shortcut and adding the parameter /scr x,y,width,height. The possible values are: -1 to use the default and -2 to center on screen. Ex: "/scr -2,100,320,-1" will open a window horizontally centered at y=100, w=320, h=320 (default is 0,0,240,320).
On the next chapter we’ll see installation instructions for each platform. Practice by installing our application on different devices and see how it looks like on each one.

4 The Launcher

The TotalCross Launcher is a Java application that allows you to run a TotalCross application over the installed JDK, providing you a quick way to run and test your application.
Some people think that running the application on the desktop under an IDE (such as Eclipse or Netbeans) will use the TotalCross virtual machine. This is not true: the actual virtual machine used is the one provided in the Java Development Kit installed (or java.exe) on the desktop.
You may also pass arguments to the launcher to simulate different resolutions and styles, and have an idea of how your application is going to look like on a particular device.
The basic format for using the launcher is:
totalcross.Launcher [optional arguments] <main class>
The optional arguments can be any combination of the following (not case sensitive):

Screen resolution and color depth

  • /scr <W>x<H>: sets the width and height.
  • /scr <W>x<H>x<bpp>: sets the width, height and color depth.
  • /scr Win32: Windows 32 (same of /scr 240x320x24).
  • /scr iPhone: iPhone (same of /scr 320x480x24).
  • /scr android: Android (same of /scr 320x480x24).

Color depth

  • /bpp 8: emulates 8 bits per pixel screens. (256 colors). No used anymore on modern devices.
  • /bpp 16: emulates 16 bits per pixel screens. (64K colors).
  • /bpp 24: emulates 24 bits per pixel screens. (16M colors).
  • /bpp 32: emulates 32 bits per pixel screens. (16M colors without transparency).

User interface style

  • /uiStyle Flat: Flat user interface style.
  • /uiStyle Vista: Vista user interface style.
  • /uiStyle Android: Android user interface style.

Device characteristics

  • /penlessDevice: acts as a device that has no touch screen. Note that all currently supported devices have touch screen.
  • /geofocus: uses geographical focus (also activates penlessDevice).
  • /fingerTouch: simulates the use of fingers (since a finger is less precise than a pen, uses an algorithm to find the control near the finger and also activates drag and flick).
  • /unmovableSip: specifies that the Soft Input Panel (SIP) is unmovable, and simulates the screen shift that’s made when an Edit or MultiEdit gains focus.
  • /virtualKeyboard: specifies that the device does not have a physical keyboard (or it has but the keyboard is closed).

Others

  • /pos x,y: sets the opening position of the application.
  • /scale <0.1 to 4>: scales the screen, magnifying the contents (if greater than 1) or shrinking (if between 0 and 1).
  • /dataPath <path>: sets where the PDB and media files are stored. This is also the default path for Litebase table files.
  • /cmdLine <...>: the rest of the arguments (except the last one) are passed as the command line to the application being launched.
  • /showmousepos: shows the mouse position (only when running on JavaSE).
The last argument passed to the launcher is always the qualified name of the main class of the application to be launched (for example, tc.samples.api.TotalCrossAPI).
When running the application, the emulator shows some function keys that can be used to emulate a device key. For example, F6 opens the application menu, F9 tests the screen rotation using the launcher and F11 opens the keyboard (or calendar) in an Edit field.

5 Deployment Process

To deploy your TotalCross application, you must use the Java application named tc.Deploy, packaged with the tc.jar located at %TOTALCROSS_HOME%/dist.
To run a Java application from a jar file, you must specify the source .jar to the Java classpath, e.g. java -classpath %TOTALCROSS_HOME%\dist\tc.jar tc.Deploy […].
The tc.Deploy program will, starting in the working directory, create folders with the installation files for each platform. A .tcz file is outputted with all classes and its dependencies, already translated to the target opcodes, and compressed with ZLib to save space. We recommend that the original classes are compiled with debug information, because line numbers will be included in the .tcz and will be available in the stack trace (all other debug information is discarded in the default compilation). .bmp and .gif files are automatically converted to 24bpp .png files (JPeg are kept in its original format – read more about images below). The .tcz file is not compliant with the .zip format, although ZLib is used, because we need to store more information in the .tcz file. Note that, since we translate the programs to our opcodes, there’s no need to run obfuscators in the class file, because obfuscation is already done by the converter. Actually, .tcz files are 1/4 of the original class size, 50% due to our tclass file format and 50% more due to the ZLib compression.

Parameters

The basic format is:
tc.Deploy <application/library to deploy> <target platforms> [other options]
Where:
  1. <application/library to deploy> must be one of the following:
    • The main class file name (the .class, not the .java). You can pass the full path to the main class file instead of its fully qualified name (App x my.sample.App).
      For example, to deploy my.sample.App at
      C:/myprogram/classes/my/sample/App.class
      :
      cd C:/myprogram/classes
      tc.Deploy my/sample/App.class

      or
      tc.Deploy c:/myprogram/classes/my/sample/App.class
      The correct package is automatically detected from the class file.
    • The folder that contains a single class that implements MainClass or directly extends one of the following classes: MainWindow, TestSuite or GameEngineMainWindow.
      This option will not work if your main class extends a class which in turn extends one of the classes listed above.
      For instance:
      Assuming App is the main class, “MainWindow App” will work, but “MainWindow AnotherClass App” will not work.
      This option will also fail if the given folder contains more than one valid main class.
      The deployer automatically detects and includes Class.forName() references. Note that the parameter must be a string literal. Class.forName() will not work yet if the parameter is a variable containing the class name.
    • A .jar or .zip file containing all the required files, like tc.Deploy App.jar or tc.Deploy App.zip (a .jar file is basically a .zip file with a new extension). To create a .jar file, open the command prompt, change to the class files folder (usually /bin or /classes), and type: jar cvf App.jar.
      Please notice this is the only option that does not require a main class to be used, therefore, it’s the only valid option to create libraries (which usually does not have a main class).
      To create a library, you must end your .jar name with lib or Lib, such as xxxlib.jar or xxxLib.jar.
      However, if you use this option to deploy an application, the .jar/.zip file must have the same name of the main class. (e.g. if your main class is App.class, the resulting .jar/.zip file must be either App.jar or App.zip).
  2. <platforms to deploy> must be any combination of the following:
    • -wp8 Creates .xap file.
    • -win32 Creates an executable file to run the application in Win32.
    • -applet or -html Creates the HTML files and a .jar file.
    • -android Creates the .apk file for Android.
    • -linux Creates the binary and the .tcz files for Linux.
    • -iOS or -iPhone Creates an .ipa for iOS.
    • -all Single command to create all of the above.
  3. [other options] may be any combination of the following options:
    • /a ApId : Assigns the application id; can only be used for libraries or passing a .tcz file. Under normal situations, set the Settings.applicationId.
    • /autostart : automatically starts the application after a boot is completed. Currently works for Android only.
    • /c cmd : Specify a command line to be passed to the application.
    • /i platforms : Pass a list of comma-separated platforms to let the generated file be installed. Currently works on Android and Windows Phone 8. The possible options are: /i android, /i android,wp8, and /i wp8. On WP8, this parameter also automatically starts the application. It is necessary to have the Windows Phone SDK installed to be able to use it.
    • /m path : Specifies a path to the mobileprovision and certificate store to deploy an .ipa file for iOS.
    • /n name : Overrides the name of the .tcz file with the given one. Only works when deploying a .jar or a .tcz file.
    • /o path : Overrides the output folder (defaults to the current folder) to the given one.
    • /p type : Packages the VM (and Litebase) with the application, creating a single installation file. The type parameter can be one of the following: demo, demo+ litebase, release, release+litebase (where demo/release are the virtual machine types you want to include: the time-limited demonstration, or the release that requires activation).
    • /r key: Signs the .tcz file with the given registration key (used when you purchase a VM license).
    • /t: Only tests if the class is ok to be deployed; no .tcz files nor installation files are generated.
    • /v: Verbose output for information messages.
    • /w: If an error occurs, waits for the ENTER key to be pressed before the window closes.
    • /x list: Comma-separated list of class names that must be excluded (in a starts-with manner). Use this to exclude classes included in an already created library.

Options retrieved from the static initializer

You may have noticed that there’s no need to pass any parameters for the application, like creator id or the icon’s title. All these are inferred from the default constructor and the static initializer of the main class.
The following patterns are used:

Icon Title

Default value Main class’ name.
Code inferred super(“My application”, border_type);
Resulting property "My application" is used as the icon’s title. You may change the application’s title to something else if desired: super("My icon title", border_type); setTitle("My app title");

Application Title

Default value null
Code inferred super("My application" + " " + Settings.appVersion, border_type);
Resulting property "My application" is used as the icon’s title, but the application title would be something like “My application 1.3”.

Application ID

Default value An ID is created based on the name of the main class.
Code inferred static {Settings.applicationId = "Crtr";}
Resulting property "Crtr" will be the creator id (in TotalCross it is called application id, which is the correct name that Android, when using a single package, application folder).

Application Version

Default value null
Code inferred static {Settings.appVersion = "1.3";}
Resulting property "1.3" will be used to set the version of the application in the stubs.

Company Information

Default value null
Code inferred static {
  • Settings.companyInfo = "My company information";
    Settings.companyContact = “me@company.com”;
    }
Resulting property "My company information" will be used as the company’s information. You may also use this in an “about” box in your program.

Application Information

Default value null
Code inferred static {
  • Settings.appCategory = “my products”;
    Settings.appLocation = “www.mycompany.com”;
    Settings.appDescription = “This is my program”;
    }
Resulting property The appCategory is used on the iOS platforms.
The appLocation is used on the iOS platforms.
The appDescription is used on the iOS platforms.
If you specify these outside the static initializer, like in the constructor, default values will be assumed.
There are other options that are set by the programmer in the static initializer:
  • Settings.fullScreen: makes it full screen at startup.
  • Settings.fullScreenPlatforms: selects which platforms the fullScreen member will be applied to.
  • Settings.activationServerURI and activationServerNamespace: defines the middleware server that will be used to restrict the activations. Read more in the Activation Server chapter.

Specifying the Icon

The easiest way to create an icon is to provide a file whose name ends with appicon.gif of any SQUARE size (80x80 preferable) and any palette, which will be automatically converted to the target icon sizes. Put the file in the src folder.
If the appearance of the icons does not look good, you can create some .bmp and .png files for each platform:
Windows 32
  • icon32x32x8.bmp
iOS
  • icon29x29.png
    icon50x50.png
    icon57x57.png
    icon58x58.png
    icon72x72.png
    icon100x100.png
    icon114x114.png
    icon144x144.png
Android
  • icon72x72.png
Be careful with the palette of the .bmp files, never use the MS Paint program; instead, get the .bmp files that are in the etc/images folder and edit them in a software that keeps the original palette, like Photoshop and PaintShopPro. Use alpha-channel on PNG for better appearance.
On iOS, if you don’t provide all icon sizes for the different iOS devices, the deployer will get the icon image with the greatest resolution and scale it to match the icon size of the missing necessary sizes.

Adding other files to the package

Optionally, you can create a .pkg file with additional files that will be added to the .apk, to the .ipa, etc. The file must have a set of [G] and/or [L] lines followed by the file name (which is searched in the classpath and in current folder). Files prefixed with [G] were placed in the same folder of the TCVM in the past. Nowadays, using [G] is the same of using [L], to put the file in the application’s folder. If the file ends with a slash, indicating that it’s a path, all files inside that path will be added.
The possible .pkg files are: iphone.pkg (also used for iPad and iPod), android.pkg, linux.pkg, wp8.pkg, and win32.pkg – for each platform, or all.pkg for all platforms. The files must be .tczs. All adicional needed files must be listed in the .pkg file and the main program should be deployed as if it used only one file.
Example: suppose program A.jar needs two libraries named BLib.jar and CLib.jar. Then you must deploy each one to create a .tcz and create a .pkg with the following lines:
[L] BLib.tcz
[L] CLib.tcz
Finally, you deploy your application doing, for instance:
java tc.Deploy A.jar -all
See other options running tc.Deploy without parameters.

6 Installing your application

iOS (iPhone/iPad/iPod)

Deploying as IPA for enterprise distribution

After joining Apple’s enterprise development program, you’ll be able to create a certificate to distribute in-house applications. Applications signed with this certificate may be distributed only for employees of the company that owns the certificate, and may not be distributed for third-party companies.
First, download and install openssl for your OS. (For Win32)
Now create a config file with your certificate request info, like this:
[ req ]
distinguished_name = req_distinguished_name
prompt = no
[ req_distinguished_name ]
emailAddress = john@webmail.com
commonName = John Doe
countryName = BR
stateOrProvinceName = Rio de Janeiro
localityName = Rio de Janeiro
organizationName = John’s Company
Now run the following command on the shell:
openssl req -nodes -newkey rsa:2048 -keyout request.key -out
request.csr -config config
If successful, there will be two new files on the folder: request.key and request.csr.
Now go to the iOS Provisioning Portal and request a new distribution certificate. When prompted for a certificate request, use the file request.csr.
Uploading the certificate request may fail on some browsers, but it should work on Safari, Chrome, or Firefox. Try using a different browser if the certificate request is not accepted.
Download the newly created certificate (which name is by default ios_distribution.cer) and run the following command:
openssl x509 -in ios_distribution.cer -inform DER -out
ios_distribution.pem -outform PEM
This command will create the file ios_distribution.pem, now run:
openssl pkcs12 -export -inkey request.key -in ios_distribution.pem
-out ios_distribution.p12
When prompted for a password, just hit enter and leave it empty.
Now you must go back to the iOS Provisioning Portal to create a new mobile provision.
From the iOS Provisioning Portal, go to "App IDs" to create an id for your application. Make sure to double check everything when creating ids because once created they cannot be removed! There’s no limit to the number of app ids you can have, but you don’t want to end up with dozens of unused ids cluttering your page.
For this first time, we suggest you to use the wildcard app id that should come configured by default (if you don’t have it, just create one using * as Bundle Identifier).
Now move on to Provisioning, switch to the Distribution tab and click on "New Profile". In "Distribution Method" choose "In House", and use the same distribution certificate and app ids you already created. Name it and after submitting use the download option in the Distribution tab to download it to the same folder where you have your certificate and keystore.
The mobile provision allows only the application with the specified id to run on the device. If you create the mobile provision using the wildcard app id, it will allow any application signed by you to work on the device, making it very useful for testing your applications without creating additional ids that can’t be removed later.
After completing this steps you should have the following files:
config used to create a request certificate, may be deleted now.
request.key RSA key used to sign your certificate request, it is no longer necessary but you should keep it as it is required to create ios_distribution.p12.
request.csr certificate request sent to the iOS Provisioning Portal, may be deleted now.
ios_distribution.cer certificate downloaded from the iOS Provisioning Portal. Required to deploy the application, but it may be redownloaded if lost.
ios_distribution.pem intermediary file used to create ios_distribution.p12, may be deleted now.
ios_distribution.p12 key store that contains the keys used to generate the certificate. Required to deploy the application and must not be password protected.
<user-defined name>.mobileprovision provisioning file that allows your application to be installed on the device.
When deploying your application you must use the argument /m with the path to the certificate and key store that will be used to sign your iOS application.
java tc.Deploy ..... -iphone (or -all) /m <cert-and-keystore-path>

Deploying as IPA for Apple Store

The process is similar to the above one. However, you should use an appropriate key to publish your application on the Apple Store. You should not use a certificate for distributing in-house applications.
You should also provide a splash.png image whose size must be 640x1136. The image should be with this size and name. This image Otherwise, Apple will reject your application.

Updating an application on the Apple Store

  1. Go to iTunes Connect (https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa)
  2. Click on Manage Your Apps
  3. Select an application
  4. Click on Add version (it will go to "Prepare for upload" state)
  5. Click on the button at the top "Ready for upload". Answer the questions until coming back to the screen and the status become "Waiting for upload"
  6. Open the Application Loader
  7. Click on “Deliver your app”
  8. If you indicate that you don’t have an application to send, repeat the steps 1 to 5
  9. In the Application Information screen, click on Choose and select the .ipa genereated with the distribution certificate
  10. Click on send.

OTA (Over the air updates) with Totalcross and iOS

It is possible to push down over the air updates of your Totalcross application.
To do this you need the following:
  • Have a packaged .ipa of your TotalCross application
  • Have “iPhone Configuration Utility” installed
  • A web server that will host:
    • A .plist file used by iOS to load your application OTA
    • Your application .ipa file
The .plist file is a special type of file that points to the .ipa file of your application on the web server. It is used by Safari on iOS to install the application. It looks like the below. Just copy this into a file and save it with a .plist extension.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
	<dict> 
		<key>items</key> 
		<array> 
		<dict> 
		<key>assets</key> 
		<array>
		<dict> 
		<key>kind</key> 
		<string>software-package</string> 
		<key>url</key>
		<string>http://yourserver/yourapplication.ipa
		</string> 		
		</dict> 
		</array> 
		<key>metadata</key>
		<dict>
		<key>bundle-identifier</key>
		<string>application identifier</string>
		<key>bundle-version</key>
		<string>1.0</string>
		<key>kind</key>
		<string>software</string>
		<key>title</key>
		<string>application name</string>
		</dict> 
		</dict> 
		</array> 
	</dict> 
</plist>
Then you need to edit the .plist file in the following way:
  • Edit the URL <string> element to point to your .ipa file as available via your web server
  • Edit the bundle-identifier <string> element to the identifier of your application
  • To get the bundle-identifier you can use the “iPhone Configuration Utility”
  • You need to load your .ipa file into the application, then click on the "applications" section under "Library" and it will display the identifier there.
  • Finally change the key <string> element to your application name.
One last step and you will be able to install your application over the air. Create a web page test.html on the web server that contains an anchor element as follows:
<a href="itms-services://?action=download-manifest&url=http://yourserver/yourfile.plist">Application Name>>></a>
At this point if you have setup everything correctly you will be able to manually update / install your application by pointing the iOS Safari browser to test.html on you hosted web server. When you click on the link iOS will popup a screen asking if you would like to install the application.
So now you can manually update your TotalCross application. If you want to be able to do automatic updates via TotalCross all you need to do is execute the following once your application detects a new version is available
Vm.exec("url","itms-services://?action=download-manifest&url=http://yourserv er/yourfile.plist")
This will launch Safari and execute the same process as doing an update manually.
Of course your application has to have a mechanism to detect when a new version is available and be able to update itself, but that is outside the scope of these instructions.
So there you have it, how to do OTA updates on iOS without needing the App Store.
Note that after iOS 7.1, the plist file must be server over an https connection.

Debugging

On iOS one can get the debugConsole.txt and other program files by installing a program called DiskAid. It is only necessary to open the program, connect the device, go to the application folder and them access its files. Using this program one can even copy the device files to the computer. Before stalling DiskAid, it is desirable to install iTunes. Moreover, some configurations must be done in order to show a TotalCross Application. Just go to DiskAid-->Preferences and check the options to show all files, applications, etc. Your application is inside Apps. Just double-click it to see the application folders. The application files are located in the folder Documents.

Windows Phone 8

Manual installation

When you generate a .xap of your application, which includes TotalCross and Litebase, you can install it yourself if you enable development mode on your device. Follow the instructions in
http://msdn.microsoft.com/en-us/library/windowsphone/develop/ff769508(v=vs.105).aspx in order to register your phone for development. To do this, firstly you MUST have a computer running a Windows 8. Then, you need to download and install Windows Phone SDK 8.0, which can be downloaded from http://www.microsoft.com/en-us/download/details.aspx?id=35471. Notice that to generate a .xap you only need to pass -wp8 as a parameter to tc.Deploy. There is no pre-requisite to generate it. After installing the Windows Phone SDK 8.0, it’s only necessary to open Application Deployment, choose the target (the WP8 device), and select the desired application .xap.
Phones registered for development can only have one TotalCross application installed at the same time.

Windows Store

It is possible to install a TotalCross application from the Windows Store. To do that, your application must follow some rules in order to be accepted in the store. Here is a link with detailed information:
http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj247547(v=vs.105).aspx.

Debugging

You can fetch DebugConsole.txt and other application files using the Isolated Storage Explorer command-line tool for Windows Phone. This is inside the Windows Phone SDK. You can also use it to put files in the application folder on the device. To know how to use it, read the following tutorial: http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh286408(v=vs.105).aspx.

Android

Installing the device

After you purchase the device, you have to install the USB driver, which can be downloaded from here:
http://developer.android.com/sdk/oem-usb.html
If a device that is not recognized, search for “adb driver <device name>” in your preferred search engine.
Note: There is a program called PdaNet for Android that can recognize almost all Androids. This can be downloaded from http://pdanet.co/.

Deployment

How to deploy your application to Android:
java tc.Deploy ..... -android (or -all)
A directory named install\android will be created with a single .apk inside. This .apk contains your application’s TCZ and the launcher. You can install additional files by creating an android.pkg (or all.pkg). The deployer will search for a file named icon72x72.png as a special icon for it (remember to use alpha channel in the PNG for better appearance); if it doesn’t find the file, it will resize the file ended with appicon.gif (already used in other platforms) to 72x72 and use it. The first time that TotalCross, Litebase, and your application run, it will take longer because the .tcz (and possibly other libraries) will be being decompressed and installed in the application’s data folder.
IMPORTANT: On Android you can only read/write from Settings.appPath (which is /data/ data/totalcross.app.<your application name here> or /data/data/
totalcross.app<application id>
if using single package). Trying to access a file or a path elsewhere will fail with permission error. You can’t even list the files from elsewhere.

Installing on the Device

The ADB (Android Debugging Bridge) is the tool that is used to install applications in the device, and also to get the log from it. This tool is released with the TotalCross SDK and is located at %TOTALCROSS_HOME%\etc\tools\android. If you plan to use it from the command-line, add it to the PATH environment variable of your computer.
Be sure that the Android device is connected to USB and that the driver was installed. To be able to find if the Android device is ok, type (from cmd):
adb devices
If the device is ok, it will be listed (like this):
List of devices attached
HT9A3LV00045 device
The number above is the serial number of the device.
To install the file on the device, you can use:
adb install -r xxx.apk
Note: if you’re running on Windows and installed TotalCrossSDK.exe, the setup.bat file at the root has created a registry entry that adds an “Install APK” file to the right-button click in Windows Explorer. This greatly helps installing any application in the device. So, just open Windows Explorer, select the APK file, right-click your mouse button, and select “Install APK”. The file will then be uploaded to the device. Note that the registry key adds this menu option to all file types, so, be careful to use it only with .apk files.
So, if you want to test the TotalCrossAPI.apk, from the command line, use:
adb install -r TotalCrossAPI.apk
Remember to install the TotalCross and Litebase .apk (if your application is not deployed as a single package) as:
adb install -r %TOTALCROSS_HOME%\dist\vm\android\TotalCross.apk
You can look at other adb commands just typing:
adb
Note that from Android version 4.3, you can’t downgrade a program installed on the device. Therefore, if your currently installed application version (as in Settings.appVersion) is greater than the version of the same application to be replaced, you won’t be able to reinstall it. You must first uninstall the newer program version and them install the old program version.

Debugging

The Vm.debug() outputs to a file named DebugConsole.txt at the application’s folder. You can alternatively set the output to the ADB logger by calling Vm.debug(Vm.ALTERNATIVE_DEBUG). Open the command prompt and type:
adb logcat TotalCross:I *:S
This is the same of using the .bat file located at:
%TOTALCROSS_HOME%/etc/tools/android/debug_console.bat
. It greatly helps debugging.
If, for some reason, the application crashes, do this imediately after it:
adb bugreport >bug.txt
Then zip this 1MB file and send to us.

More on ADB

On some devices it is not easy to access the debugging or using other commands using ADB. Moreover, since an Android device can be built from different enterprises, their settings and configuration menu will look differently from each other.
For instance, the following steps leads you to get USB debugging turned on the Galaxy S4, OS 4.3. On other Androids, it may be different.
From within the GS4 you have to enable the "unknown sources" option for the installation of applications, though the "allow debug" checkbox doesn’t seem to be included anymore. It should be in Developer Options. If your device is running Android 4.2, “Developer Options” is hidden by default. To enable it:
  1. Go into Settings > About Device
  2. Tap on the Build Number entry seven times (a status pop up will appear to help you keep count)
  3. Press BACK, and “Developer Options” should be in the “Settings” list
Note: When you connect a device running Android 4.2.2 or higher to your computer, the system shows a dialog asking whether to accept an RSA key that allows debugging through this computer. This security mechanism protects user devices because it ensures that USB debugging and other adb commands cannot be executed unless you’re able to unlock the device and acknowledge the dialog.
After that, when issuing the following ADB command, you will get as result:
C:\Users\User\Documents\workspace42>adb devices 
* daemon not running. starting it now on port 5037 * 
* daemon started successfully *
List of devices attached 
0e9def03 offline
Note above that the device status is still listed as "offline". Give your device a minute or so and it will pop up a dialog asking to accept an RSA key. Confirm that this is OK. Now, when you reissue the adb devices command, you should see your device in the list, and its no longer offline, so you can now use adb as expected.

7 Using the Release Vms

When you decide to release the application to your client, you must purchase TotalCross+Litebase licenses. See the correct approach going to the TotalCross site and clicking the buy item at the horizontal menu. Once we detect the payment, we will then create an activation key and enable it at the site. Login in the account and press the “Check activation keys and licenses”.
The TotalCross site has two types of accounts: one for the user that downloads the SDK, and one for the user that purchases licenses. They can even have the same e-mail, however, the passwords are necessarily different. The account that is used to download the SDK cannot see the activation VMs, however, the account that is used to download the activation VMs can see both. So, if you login using the SDK user and password, you will NOT be able to download the VMs, neither see the “Check activation keys and licenses” submenu.
If you have lots of costumers, you can create new activation keys and transfer credits between them. All this can be done at the site. Note that an activation key cannot be deleted.
More information can be found at the TotalCross site, under the Products menu.

Signing your application

Once you get access to the activation key, you must sign your application with it, using the deploy’s /r option. Just do something like:
java tc.Deploy … /r <activation key>

Activating your application

After you login with the right account, download the TotalCross VMS package. This package is complimentary to the TotalCross SDK: it only contains special virtual machines that will authenticate to our server (hosted at Rackspace – Boston USA) at the first time that the application runs.
The VM uses the port 80 and connects a to webservice located at www.superwaba.net server. So, if you’re behind a firewall or a proxy, remember to allow connections to this site.
The first time that the application runs on the device, it generates a file named tcreq.pdb at the application’s folder, containing the IMEI and the serial number. Then it connects to the server and, if succeed, replaces the file with a tcsuc.pdb.
If the device is hard-reseted (looses everything), a new connection to the server will be made once the program runs again. However, a new license will NOT be consumed, since the device was already activated once.
If you upgrade the device’s firmware, the serial number may change. In this case, when a new activation is done, it will consume a new license, since the information that is used to track the device was changed.
The license is per-device. If the device is stolen, broken, or the costumer replaces it by another one, a new license is consumed.
There’s a special contract that can be made with us that will allow you to replace a device and return the license. Contact us at the site if you desire to be covered by this special agreement.
Some devices may not have an IMEI nor a serial number accessible by software. On these devices, a hard-reset will result in the consumption of one more lisence if there is no backup of the activation file.
If you sign your application with two activation keys, the license will be discounted again.

Manual Activation

If you don’t have Internet connection from the device, you can do a manual activation:
  1. Try to activate the device once.
  2. Copy the tcreq.pdb from the application’s folder on the device.
  3. Run the manual activation application, located at:
    -%TOTALCROSS_HOME%
    • -dist
      • -samples
        • ManualActivation\install\win32\ManualActivation.exe
    (a linux version is available at install\linux folder).
    If a message stating that the tcvm.dll was not found, copy the tcvm.dll, tcbase.tcz, TCUI.tcz, and tcfont.tcz for Windows 32 to this folder and try again.
  4. Select the tcreq.pdb file from within the program.
  5. Our server will be contacted and will write back a tcsuc.pdb file.
  6. Replace the tcreq.pdb by the tcsuc.pdb at the same folder in the device. On Android, you will have to root your device to copy a file to it. On iOS, you can use DiskAind to copy files from/to the device.
The ManualActivation also accepts a tcreq.pdb from the command line.

Using a third server to control licenses

Suppose that you release your signed application to the customer and he starts spreading the application to other people. We created a way to allow a company to control which costumers will use the activated VM.
You can instruct the VM to call a custom web service created by yourself. This web service will process the login/password to allow or deny the activation, and then will call our web service which will return data to the application running at the device.
Follow these steps:
  1. Download the web service
    (http://www.totalcross.com/activation/Activation_sample.rar) sample.
  2. In the folder “Sample Webservice” there’s a class named ActivationService, which is an Axis web service created in Java (like the sample in this companion), but which also uses TotalCross classes.
    • You can host the web service in any kind of server, such as Tomcat, .NET and Ruby.
  3. In the folder "Sample Application", there’s a sample application named ActivationTest. Change the Settings.activationServerURI field (at your application’s static initializer!) to point to your web service, and make the deploy using the build_sdk.bat (demo version) or build_vms.bat (release version). In build_vms.bat, change the KEY to your activation key. Execute the application to see the results.
  4. You can change the file activation.html as you wish, but the property name of the confirmation button must be SubmitActivation.
  5. Besides the activation.html, there are three optional files: activation_success. html (which is shown if the activation succeeds), activation_error.html (shown if the server rejects the activation), and activation_nointernet.html (a test is made before calling the web service to make sure that there’s Internet, if there’s not, this page is shown). This way you can customize all messages in your own language.
  6. From your web service, you can check the data that was received and you may return an error message to interrupt the activation.
    So, this is a summary of what you have to do in your application:
    • Specify the address of the web service using Settings.activationServerURI (and optionally also the namespace using Settings.
      activationServerNamespace
      ). This must be done inside the static initializer of your main class.
    • Optionally, if you want to create a custom screen to show at startup, follow the instructions described previously.
    • Note that its impossible to run any code from your application before the activation. The only possible way to show a custom screen is from the .html.
    • Your web service must obey the given interface:
      public static String[] activate(String request, String[] keys, String[] values) throws Exception
      • The request parameter cannot be changed and must be passed to our web service.
      • The keys and values contains some basic information used by the activation, such as the device model, the serial number, the IMEI, and also any other parameters defined in the .html.
      After you apply your own logic to confirm if this is a valid user (like checking the login and password), you must connect to our web service, sending the request string as parameter.
      The return of this method must be:
      • If error: an array with a single element with the error message that will be displayed to the user (like new String[]{“Invalid password”}).
      • If success: a two dimensional array, where the first element is a message that will be displayed to the user, and the second element is the answer received from our server.
Attention: the activation server that this web service uses is our production server, so, one credit will be consumed each time that the activation succeeds in a computer that was never activated. In other words, be aware that your tests will consume a license.
You can use this piece of code to control if a device has already been activated so that you can control the devices your customers use. It assumes the following strings as parameters: imei, nserial, and platform. You can use other parameters as filters to the query, such as a customer unique identifier ID. This example is specially important when activating desktop machines, since it has no hardware serial number (its value is created by TotalCross using some machine device identifiers).
String filterImeiSerial;        
if (imei == null) imei = "";
if (nserial == null || nserial.equalsIgnoreCase("unknown")) nserial = ""; 
// On win32 the verification is harder, the serial number must be splitted and checked with like if its pieces already exist.
if (plataform.equalsIgnoreCase("Win32") && !nserial.equals(""))
{
	String[] nn = nserial.split("-");
	filterImeiSerial = " and (nserial like ’%"+nn[0]+"%’";
	for (int i = 1; i < nn.length; i++)
		filterImeiSerial += " or nserial like ’%"+nn[i]+"%’";
	filterImeiSerial += ")";
}
else if (!imei.equals("") && !nserial.equals(""))
	filterImeiSerial = " and (imei=’"+imei+"’ and  nserial=’"+nserial+"’)";
else if (!imei.equals(""))
	filterImeiSerial = " and imei=’"+imei+"’";
else 
	filterImeiSerial = " and nserial=’"+nserial+"’";
​
rs = bd.executeQuery("select * from device where filterImeiSerial = "+filterImeiSerial); 

8 TotalCross 3.1 Differences

First of all, the support for Windows CE and Windows Mobile platforms are back since they are still used specially because of collectors.
Additionally, it is now possible to install a TotalCross application from the Windows Store.
Moreover, now it is possible to use some Java 8 features that were not supported before. To use java 8, just change the ant files, IDE settings, or javac command line to use target 1.8 and source 1.8. If you use a feature not supported yet, an exception will be thrown during the deploying process.
New features that are now supported and some of them are detailed below:
  • JSON;
  • enuns;
  • generics;
  • for each;
  • varargs;
  • switch with strings;
  • clone;
  • many collection classes, interfaces, and exceptions (without serialization yet);
  • more interfaces, classes and exceptions in java.lang, and
  • many more methods in String and StringBuffer.

Collections

The following exceptions, interfaces, abstract classes, and classes are now supported. Please check the javadoc to see which methods and fields are allowed.
  • Exceptions:
    • ConcurrentModificationException
    • EmptyStackException
    • NoSuchElementException
  • Interfaces:
    • Collection
    • Comparator
    • Deque
    • Enumeration
    • Iterator
    • List
    • ListIterator
    • NavigableMap
    • NavigableSet
    • Map
    • Queue
    • RandomAccess
    • Set
    • SortedMap
    • SortedSet
  • Abstract classes:
    • AbstractCollection
    • AbstractList
    • AbstractMap
    • AbstractQueue
    • AbstractSequentialList
    • AbstractSet
    • Dictionary
    • EnumSet
  • Classes:
    • ArrayList
    • Arrays
    • BitSet
    • Collections
    • EnumMap
    • HashMap
    • HashSet
    • HashTable
    • IdentityHashMap
    • LinkedHashMap
    • LinkedHashSet
    • LinkedList
    • PriorityQueue
    • Stack
    • TreeMap
    • TreeSet
    • Vector
Note that now there are two Vector and two Hashtable classes. Two from totalcross.util and the others from java.util. To use the ones from the Collections API, use java.util.Vector and java.util.Hashtable.

Additions in java.lang

  • Exceptions:
    • CloneNotSuportedException
    • UnsupportedOperationException
  • Interfaces:
    • Cloneable
    • Comparable
    • Iterable
  • Object.clone()
  • Float.compare(), Float.floatToIntBits(), and Float.floatToRawIntBits()
  • Double.compare(), Double.doubleToLongBits(), and Double.doubleToRawLongBits()
  • String now extends Comparable<String>, that is, it is now a Comparable object.

Additions in String and StringBuffer

  • String
    • String(StringBuffer buffer)
    • boolean contains(String part)
    • boolean contentEquals(StringBuffer buffer)
    • int compareToIgnoreCase(String str)
    • boolean regionMatches(int toffset, String other, int ooffset, int len)
    • boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len)
    • boolean matches(String regex)
    • replaceAll(String regex, String replacement)
    • String[] split(String regex)
    • String valueOf(char[] data)
    • String valueOf(char[] data, int offset, int count)
    • boolean isEmpty()
    • String replace (String target, String replacement)
  • StringBuffer
    • StringBuffer append(StringBuffer stringBuffer)
    • StringBuffer deleteCharAt(int index)
    • StringBuffer replace(int start, int end, String str)
    • String substring(int beginIndex)
    • String substring(int beginIndex, int endIndex)
    • StringBuffer insert(int offset, char[] str, int str_offset, int len)
    • StringBuffer insert(int offset, Object obj)
    • StringBuffer insert(int offset, String str)
    • StringBuffer insert(int offset, char[] data)
    • StringBuffer insert(int offset, boolean bool)
    • StringBuffer insert(int offset, char ch)
    • StringBuffer insert(int offset, int inum)
    • StringBuffer insert(int offset, long lnum)
    • StringBuffer insert(int offset, double dnum)
    • int indexOf(String str)
    • int indexOf(String str, int fromIndex)
    • int lastIndexOf(String str)
    • int lastIndexOf(String str, int fromIndex)
    • boolean regionMatches(int toffset, String other)

9 TotalCross 3.0 Differences

Platforms

In TotalCross 3.0 we dropped support for Windows CE and Windows Mobile platforms, since these platforms are discontinued and replaced by Windows Phones.
If you want to support these platforms, you must download TotalCross 2.0, which still supports these platforms. Note that Litebase 2.7 works for TotalCross 1.7 (and up), and TotalCross 2.0 (and up). TotalCross 3.0 (and up) uses Litebase 2.8. Litebase differences for TotalCross 3.0 are just some small path changes to run on WP8.
What we recommend is that you use TotalCross 2.0 only for those two platforms, and use TotalCross 3.0 for all other platforms. Many SDKs can coexist in the same machine, just rename them.
What we recommend is that you use TotalCross 1.7 only for those two platforms, and use TotalCross 2 for all other platforms. Both SDKs can coexist in the same machine, just rename them.
So:
  1. Developing for all currently supported platforms: use TotalCross3/dist/tc.jar
  2. Deploying for Android, iOS, Windows 32, Linux, Windows Phone 8, and browser: use
    TotalCross3/dist/tc.jar
  3. Deploying for Windows CE: use TotalCross2/dist/tc.jar
  4. Deploying for Palm OS and Blackberry: use TotalCross1.7/dist/tc.jar
The great main improvement in TotalCross 3.0 is the support of the new and rapidly growing Windows Phone 8. We opt to not give support to Windows Phone 7 since its market share is small and Microsoft decided to develop WP8 just some time later.

Installation Package

Now it is only needed to download and install one .exe for TotalCross and Litebase SDK and VM. Make sure to change the paths used in your deploying process to reflect this change.

Graphics

For Windows Phone 8, DirectX is used instead of the old scheme of bitmaps. In the furure releases, DirectX will also be used for Windows 32 in order to improve graphics performance. However, older Windows 32 releases won’t be supported anymore.
On iOS 7 or above use the field Windows.taskbarColor in order to paint the iOS floating task bar. It defaults to the application’s background color.

Font

The only change is that now there is no size limit to the font. However, fonts whose size is greater than 160 will be blurred.

Reflection

Now the reflection API of Java 1.1 is full implemented. To know more about it, please check javadoc or take a look at this link explaining it:
http://www.tns.lcs.mit.edu/manuals/java-1.1.1/guide/reflection/index.html

SQLite and TDBC

Now there is another database option in TotalCross. SQLite was included in the platorm, so that TotalCross will use the same SQLite version for all supported platforms. If it used the one available on the device when it has one, there could be differences in the API or table format. But remember that Litebase eases the task of synchronizing the device tables with tables on server databases such as SQLServer or Oracle by using the class RowIterator. If you want to do something similar using SQLite, you will have to do it from scratch.
The currently SQLite version used is 3.8. If you intend to use it, please take a look at its documentation at http://www.sqlite.org/.
The access to SQLite is through TDBC, a JDBC driver for TotalCross. The TDBC implementation was based on JDBC 2.1. We did not implement everything. Below is a list of JDBC classes and interfaces that were and were not implemented:
Feature Was it implemented?
Array interface no
CallableStatement interface no
DatabaseMetaData interface no
Ref interface no
SQLData interface no
SQLInput interface no
SQLOutput interface no
Struct interface no
Blob interface yes
Connection interface All the methods except for getMetaData(), getTypeMap(), prepareCall(), and setTypeMap()
Driver interface yes
PreparedStatement interface All the methods except for setArray(), setAsciiStream(), setBinaryStream(), setCharacterStream(), setClob(), setFloat(), setObject(), setRef(), and setUnicodeStream()
ResultSet interface All the methods except for deleteRow(), getArray(), getAsciiStream(), getBinaryStream(), getCaracterStream(), getClob(), getObject(), getRef(), getUnicodeStream(), insertRow(), moveToInsertRow(), and update()
ResultSetMetaData interface yes
Statement interface All the methods except for setEscapeProcessing() and setMaxFieldSize()
DriverPropertyInfo class no
SQLPermission class no
Date class yes
DriverManager class yes
Time class yes
Timestamp class yes
Types class yes
Take a look at JDBC specification 2.1 at http://www.tlu.ee/~matsak/oracle/jdbc2_1-spec.pdf. You can check TDBC javadoc in the package totalcross.db.sqlite. In the future, other databases might be added to TotalCross using JDBC specification. It is also intended to provide Litebase access using the JDBC API.

10 TotalCross 2 Differences

Platforms

In TotalCross 2 we dropped support for Palm OS and BlackBerry platforms, since these platforms are discontinued or replaced in the market by Android and iOS.
If you want to support these platforms, you must download TotalCross 1.7, which still supports these platforms.
What we recommend is that you use TotalCross 1.7 only for these two platforms.
Applications created with TotalCross 2 can be hosted at Apple’s Appstore, using the developer distibution certificate, so we dropped support for Cydia in TotalCross 2. Due to this, all iOS samples are not included in the SDK, because to install them you need a certificate. We intend to put some samples at Apple´s Appstore.

Graphics

TotalCross 2 was created to improve graphics perfomance. We made a full rewrite of the native Graphics API to use Open GL.
In TotalCross 1, the screen is drawn in a native bitmap, and then the bitmap is drawn on the screen using the device’s graphics API. Everything, like lines, filled rectangles, and images are rendered by software.
In TotalCross 2, we use Open GL, where everything is rendered by the hardware graphics processor. So, when you load an image, it is moved inside the video memory, and then all draws of the image are instantaneous.
Open GL is used on Android and iOS. For Java SE and Windows 32, it uses the old scheme of bitmaps.
Besides the speed performance that Open GL provides, there are some drawbacks. The main one is that Open GL requires that the whole screen is painted on each screen update. For example, in TotalCross 1, when an Edit’s cursor is blinking, only the cursor is painted at each screen update, while in TotalCross 2, the whole screen is painted each time the cursor blinks.
Another consequence of this requirement is that you cannot update the screen in a thread. So, in TotalCross 1, the spinner class was able to draw only the spinner in a thread; this is impossible in TotalCross 2, which makes the spinner useless under some circunstances.
An advantage of Open GL is that it supports alpha channel by hardware. Due to this, we removed support for the Image’s transparent color. So, all Image methods that would receive a transparent color were deprecated (like rotate, scale, etc), and you must use images with alpha-channel.
In Open GL, the getPixels() operation is veeeeryyyy sloooowwww, and can cut all the performance gain that you have with Open GL, so we removed support and deprecated for all graphics methods that requires the read of the screen pixels (the methods are still there but their effects are ignored):
  • Graphics.useAA (for smooth antialiased lines)
  • Graphics.drawOp for controls that use DRAW_SPRITE, call the Image.
    setTransparentColor()
    method.
  • Graphics.drawHighlightFrame(),
  • Graphics.eraseRect()
  • Graphics.getVistaColors()
  • Graphics.fillCursor()
  • Graphics.drawCursor()
  • Graphics.drawDottedCursor()
  • Graphics.getPixel()
We also deprecated these:
  • Image.NO_TRANSPARENT_COLOR (removed)
  • Image.useAlpha (now all images use alpha channel)
  • TabbedContainer: the constructor that receives a transparent color
  • Window.highResPrepared
  • Settings.useNewFont
And added:
  • Image.applyChanges(): in TotalCross 2, moves the image inside the video memory; in TotalCross 1, does nothing. Note that you do not have to call this method.

Font

The original font of TotalCross was a Tahoma font generated inside a Windows CE device. When we moved it to Open GL, the results were very poor, so we decided to change the font to the Android’s default font and created the font inside an Android 4.1 device. This font is used in both TotalCross 1.7 and 2.0. We also changed the minimum size to 7 and the maximum size to 48. This new font is a bit narrower than the previous one, so it may not cause impacts in the user interface.

Litebase

Please notice that all indices files must be erased in order to use Litebase tables created with version 2.67 with Litebase 2.7 and vice-versa. Moreover, tables using cryptography created in version 2.7 can’t be used with version 2.67.

11 Litebase to SQLite Conversion

Since TotalCross now supports SQLite using TDBC, some programmers might want to migrate a program that uses Litebase to use SQLite. Here is a guide to help the conversion process:
  1. The supported SQL commands are described in the link http://sqlite.org/lang.html.
  2. Use totalcross.db.sqlite.ui.DBListBox instead of litebase.ui.DBListBox.
  3. The class totalcross.db.sqlite.SQLiteUtil methods will make the process much easier. Take a look at them.
  4. There is no way to go back when navigating in a ResultSet using something such as ResultSet.prev(). You can only navigate forward (using ResultSet.next()), never backward. There are also no first(), last(), beforeFirst(), afterLast(),
    absolute()
    , or relative(). All result set pointers are before the first result set record when created. If it is necessary to go backwards in a result set, you must close and recreate it.
  5. When fetching a DATETIME field with ResultSet.getString(), notice that Litebase will return the field in the format YYYY/MM/DD HH:MM:SS whereas SQLite returns in the format YYYYMMDDHHMMSSmmm.
  6. The class which deals with connections is called Connection, not LitebaseConnection.
  7. There is no multiple languages in SQLite messages. All messages from database exceptions are only in English.
  8. litebase.DriverException must be changed to java.sql.SQLException. It won’t work if you use totalcross.sql.SQLException!
  9. LitebaseConnection.getInstance(XXXX) must be changed to DriverManager.
    getConnection("jdbc:sqlite:XXXX\\YYYY.db")
    , where XXXX is the desired path and YYYY is the database name.
  10. LitebaseConnection.closeAll() must be changed to Connection.close().
  11. LitebaseConnection.execute/executeUpdate() must be changed to Connection.
    createStatement().execute
    /executeUpdate().
  12. All methods throws SQLException.
  13. In Litebase, in order to not create a table if it exists, you should do something such as
    if (!conn.exists("table_name"))
       conn.execute("create table table_name...");
    
    In SQLite, there is a single command that does it:
    create table if not exists table_name
    .
  14. There is also a command to only drop a table if it exists:
    drop table if exists table_name
    .
  15. Use SQLiteUtil.getColCount() to get the number of columns of a ResultSet.
  16. There is no way to get the total number of rows of a ResultSet using SQLite.
  17. Use SQLiteUtil.tableExists() instead of LitebaseConnection.exists().
  18. Use ResultSet.getMetaData() instead of ResultSet.getResultSetMetaData().
  19. All data returned in the SQLite metadata methods are in uppercase, whereas in Litebase they are always in lowercase.
  20. There is no cryptography in the free SQLite version, the one included in TotalCross.
  21. Change nocase to collate nocase in order to make a field case insensitive. You can also use collate nocase to make a comparison case insensitive. However, the first option is preferable if the column will always be used in a case insensitive way because it is faster.
  22. Change from ResultSetMetaData.XXXX_TYPE to Types.XXXX.
  23. Change from ResultSet.getBlob() to ResultSet.getBytes() when dealing with a blob column.
  24. Change from ResultSet.getStrings() to one of the methods SQLUtil.
    getStrings()
    to fetch all rows from the current result set pointer. To fetch only some rows, you must implement it yourself, or use the keywords LIMIT and OFFSET in your SQLite select statement.
  25. Change from LitebaseConnection.listAllTables() to SQLUtil.
    ListAllTables()
    .
  26. To create a memory database, use DriverManager.
    getConnection("jdbc:sqlite::memory:")
    . By default, the database is created in the application folder (/device).
  27. Change from LitebaseConnection.getRowCount() to SQLiteUtil.
    getRowCount()
    .
  28. There is nothing similar to RowIterator. You must simulate the updates in the table manually, creating new columns or moving data to another table and then select it. Just remember that the space used by a deleted row might be reused if SQLite needs it.
  29. Change from LitebaseConnection.purge() to Connection.
    execute("vacuum;")
    . In SQLite you do a “purge” in all tables in the database, not in a specific table such as in Litebase.
  30. In SQLite the PreparedStatement index begin with 1, not 0.
  31. There is no size definition when creating a blob column.
  32. Use Time.getSQLString() or Date.getSQLString() to format time and date in the SQL format, respectively.
  33. There is no type checking in SQLite. You can put whatever you want in any column. If you don’t want to insert data in a column with wrong data type, your program must ensure that it behaves as expected.
  34. All string data inserted is stored in unicode. It’s not possible to create an ascii database in order to save space.
  35. It is not possible to erase all indices in a single SQL statement such as the one used in Litebase: drop index * on table_name. Moreover, to drop a specific index, you must drop it using its name, using drop index IDX_NAME, not drop index on
    table_name(column_name)
    .
  36. You can’t alter a table to add or drop a primary key in SQLite.
  37. Important rules about date and time in SQLite:
  • DATE type: must be in the form: YYYY-MM-DD
  • TIME type: must be in the form: YYYY-MM-DD HH:MM:SS.MMM
    When using BETWEEN or any other date/time comparation, the arguments MUST MATCH the type form. So, in select borndate from test where borndate between
    ’2014-12-10’ and ’2014-12-14’
  • If borndate is a DATE, the comparison will succeed.
  • If borndate is a TIME, the comparison will fail. To make it work, use select borndate from test where borndate between ’2014-12-10 00:00:00.000’ and
    ’2014-12-14 00:00:00.000’
    .

12 SuperWaba to TotalCross Conversion

In order to make the conversion easier, we created a program, tc.tools.SW2TC (included in tc.jar), which recursively reads a set of folders and change the names of the old SuperWaba packages, classes and methods to the new ones.
This program does not create a backup, it simply overwrites the original files, so you should backup your files before running the program.
This converter takes care of about 99% of the problems, remaining only 1% for you to manually update. Also, the program is just a token replacer, therefore, it cannot find that in "xxx.yyy", xxx is a ListBox nor a hotdog, so it cannot replace tokens based on the type. Always refer to this table
(http://www.superwaba.com.br/pt/sw2tc.asp), where we summarize the package, class, and method changes.

General Tips

Some important tips to finish the conversion after running the SW2TC:
  • SW2TC cannot change some method names, refer to the javadocs for the new name of these methods.
  • The return type of some methods were changed, most of them used to return a boolean or int value to indicate success or failure and now return void. Errors handling is done with exceptions.
  • Many methods now throw checked exceptions, which must be handled accordingly. In SuperWaba, there were almost no validation on the arguments passed to the methods; in TotalCross, nearly all methods perform argument validation, throwing the appropriate exception for each case (in the future, this will be extended to all arguments and methods). To easily display the exception to the user, you can use the static method MessageBox.showException. Read the tip described in the next section.
ALWAYS handle the exceptions thrown. Ignoring exceptions or simply logging the error without handling the exception is just wrong. Also remember that unchecked exceptions are usually caused by bad programming and are not supposed to be caught, unless you’re using an unchecked exception instead of testing a condition.
E.g., instead of:
[label = samplecode, caption = Asample]intsum = 0;for(inti = 0;i lt;values.length;i +  + )sum +  = values[i];
You may use:
[label = samplecode, caption = Asample]intsum = 0;tryfor(int = 0;;i +  + )sum +  = values[i];catch(ArrayIndexOutOfBoundsExceptione) ⁄  ⁄ donothing, itonlymeanswefinishedreadingthearray.
Please notice we are not discussing the performance of each approach, just showing an example.
  • Some packages and classes were removed or changed, like the datergf package and the IntVector(DataStream) constructor. Most of them can be easily replaced by other classes or methods in TotalCross. But if needed, you may grab their code from SuperWaba, convert to TotalCross and add to your project.
  • Color is now an int, in the format 0xRRGGBB. Change all Color to int. Instead of comparing or initializing the color to null, use -1.
Instead of:
[label = samplecode, caption = Asample]Colorc1 = ...;Colorc2 = c1.brighter();
Use:
[label = samplecode, caption = Asample]intc1 = ...;intc2 = Color.brighter(c1);
  • The method isOpen() was removed from all streams. Their constructors now throw an IOException (or a subclass of it, like FileNotFoundException) if the operation fails.
  • In SuperWaba some classes had a lastError field that had a platform dependent error code; in TotalCross this field no longer exists, the exception message contains a platform dependent error message identifying the error. This error message is provided by the underlying OS and may not be as helpful as expected, but it’s still better than an error value. If the OS is unable to find a given error code on its error table, the exception message will have only the error code.
  • The methods onStart() and onAdd() were respectively renamed to initUI() and
    onAddAgain()
    . The SW2TC should perform these changes, but if the screen appears blank for some reason, check if the method onStart() was not renamed.
  • The WINDOW_MOVED event was removed. It was usually used to update the myg (my Graphics) object. Now the correct approach is to do a getGraphics() in all places myg was used.
  • Settings.onDevice is now called onJavaSE (Standard Edition), but notice it has the opposite meaning! Be sure to search for this field and make sure it is being used correctly. For instance:
    if (Settings.onDevice) replaced for if(!Settings.onJavaSE)
  • All classes that extend Container must explicitly set focusTraversable = true in the constructor, otherwise the class will not be able to receive focus using the keyboard in penless devices. This flag defaults to true in SuperWaba, but to false in TotalCross.

Correct way to handle Exceptions

We decided to write this to explain to programmers how to deal with exceptions. It does not apply only to TotalCross, but also to all other languages that make use of exceptions. One of the main differences between SuperWaba and TotalCross is the dispatch of exceptions by the API. Careless programmers tend to code this way:
try
{
	File f = new File(...);
	...
}
catch (Exception e){}
In other words, just ignore the exception that is being thrown. A programmer that codes this way should be fired for carelessness. A variation of this approach is:
try
{
	...
}
catch (Exception e)
{
	Vm.debug("An exception was thrown");
}
Here the programmer informs itself that the exception was thrown but completely ignores crucial information that can help to resolve the problem. And he will see the exception only if he opens the DebugConsole.txt.
The second error is to handle exceptions in the same method it was thrown, like in this sample:
class Report
{
	File f;
​
	void createReport()
	{
		createFile();
		runReport();
	}
	void createFile()
	{
	try
	{
		f = new File("report.txt", File.CREATE_EMPTY);
	}
	catch (Exception e)
	{
		e.printStackTrace();
	}
}
What’s the problem here? Simple: the createReport() method is unable to confirm that the file was created or failed.
So, what’s the correct way? The correct implementation is always throw the exception to the caller, and this to its caller, and so on, until you reach a root method that cannot pass the exception along, like the initUI() and onEvent() methods (since these methods are overriden and you cannot change the method’s signature).
Therefore, the correct implementation is:
class Report
{
	File f;
​
	void createReport() throws IOException
	{
		createFile();
		runReport();
	}
	void createFile() throws IOException
	{
		f = new File("report.txt", File.CREATE_EMPTY);
	}
}
Implementing this way, in the root method, we would do:
try 
{
	createReport();
} 
catch (Exception e)
{
	MessageBox.showException(e,true);
} 
Note that this approach is not yet perfect, because the createFile() method is throwing Exception, while new File() throws three exceptions: IllegalArgumentIOException, FileNotFoundException and IOException. Hence, the correct is that each exception be declared on its own in the throws clause of the method, and that your program handle them in the correct way. For example, IllegalArgumentIOException occurs most times by a coding error and should never occur in production code. However, there may exist some exceptions that are less serious than others and can be ignored or handled locally, letting others to be passed along to the caller. Under situations that the handling is the same for all exceptions, there is no problem in catching them in a single catch (Exception e) block.
Finally, what’s the correct way to handle the exception? The most common is call
printStackTrace(). This is a classic and BAD coding, because the stack trace is logged in the debug console (the same output of Vm.debug()), and thus the programmer will NOT know that the exception was thrown unless he exits the program and looks at the output file (note that in Eclipse or any other IDE the printStackTrace() shows up in the console panel, not in the file). The ideal is that the exceptions are informed in real time, even if this can cause some bad impressions in your costumer.
TotalCross has a method in the MessageBox class that shows the exception in a dialog. Its format is shown in the previous example:
try
{
	...
}
catch (Exception e)
{
	MessageBox.showException(e,true);
}
The first parameter is the exception itself, and the second parameter indicates if
printStackTrace() should be called. This method shows the stack trace on screen, along with the exception’s class name and a description message, if any. You can change the title through the MessageBox.showExceptionTitle member.
The use of exceptions is one of the most powerful features of Java, and can greatly simplify finding errors, if it is used correctly.
TotalCross has checked and unchecked exceptions. The checked exceptions are Exception subclasses and if they might happen in a code block, it must be surrounded by a try-catch. The unchecked exceptions are RuntimeException subclasses and do not need to be treated. The first kind of exceptions are usually errors that occur in a program, whereas the second one are usually a programming bug.
Below there are all TotalCross possible exceptions (except for Litebase exceptions) and their description:
  • Throwable Base class of all Exceptions.
    • Error General Error class.
      • NoClassDefFoundError Thrown when a class was not found during the parse of xxx.class.
      • NoSuchFieldError Thrown when a field was not found.
      • NoSuchMethodError Thrown when a method was not found.
      • OutOfMemoryError Thrown when no more memory exists to continue the execution of the last requested operation. You should exit the program immediately.
    • Exception General exception class.
      • AddressException Thrown by the Address constructor if the given string address is invalid.
      • AuthenticationException When an authentication fails.
      • CryptoException Thrown when something wrong occurs at the cryptographic algorithms.
        • NoSuchAlgorithmException This exception is thrown when a particular cryptographic algorithm is requested but is not available in the environment.
      • ElementNotFoundException Thrown when an element of a Vector or a
        Hashtable
        is not found.
      • FTPConnectionClosedException This exception is thrown when a FTP connection is closed, maybe explicitly or by timeout.
      • ImageException This class is used to throw an exception when an image could not be loaded for any reason.
      • InstantiationException Thrown when an abstract class or an interface is trying to be Class.newInstance()’d.
      • IntHashtable.DuplicatedKeyException Exception thrown when
        allowDuplicateKeys
        is set to false in an IntHashtable and one tries to add a repeated key.
      • InvalidDateException Represents a date that is invalid.
      • InvalidNumberException Thrown when you try to convert a string that does not represents a valid number.
      • InvocationTargetException Thrown when using reflection that wraps an exception thrown by an invoked method or constructor.
      • IOException Base class of all input/output exceptions.
        • EOFException Signals that an end of file or end of stream has been reached unexpectedly during input. This exception is mainly used by data input streams to signal end of stream. Note that many other input operations return a special value on end of stream rather than throwing an exception.
        • FileNotFoundException Thrown when a file was not found.
        • IllegalArgumentIOException Illegal argument passed to an IO method.
        • SocketTimeoutException Thrown when a socket times out in a connect, read, or write operation.
        • UnknownHostException Thrown when you try to connect to a host that was not found.
        • ZipException Signals that a Zip exception of some sort has occurred.
        • ZLibException May be dispatched by zLib routines.
      • MessagingException Thrown when a write operation fails when sending a
        Message
        , or when an unexpected code is received from the remote host.
      • NotSupportedByDeviceException Thrown when something isn’t supported on the given device when using PIM.
      • ReaderException General exception class thrown when something goes wrong during decoding of a barcode.
        • ChecksumException Thrown when a barcode was successfully detected and decoded, but was not returned because its checksum feature failed.
        • FormatException Thrown when a barcode was successfully detected, but some aspect of the content did not conform to the barcode’s format rules.
        • NotFoundException Thrown when a barcode was not found in the image.
      • SecurityException Used in reflection and is actually never thrown.
      • SOAPException Thrown when a problem when using SOAP occurs.
      • SQLException Base TDBC exception.
        • BatchUpdateException Counts the successful updates in each statement in a batch that was successfully updated prior to an error.
        • SQLWarning A TDBC warning.
      • SyntaxException Exception thrown by the XmlTokenizer when a syntax error is found.
      • WriterException Base class which covers the range of exceptions which may occur when encoding a barcode.
      • XmlRpcException Used to alert the user of xml-rpc errors.
      • ZLibException This exception may be dispatched by zlib routines.
    • RuntimeException General runtime exception class.
      • AppExitException This exception is thrown when Event.handleOneEvent() encounters an event that requires the application to exit. The main purpose of this event is to unwind the stack all the way back to the main event loop so that the application can properly exit. It is mostly used to exit the VM when a Window is open with the popup() method.
      • ArithmeticException Thrown when an arithmetic problem occurs, usually a division by zero.
      • ArrayStoreException Thrown when the source and target arrays are not compatible when using Vm.arrayCopy().
      • AssertionFailedError Exception dispatched when an assertion fails.
      • ClassCastException Thrown when a class is casted to an incompatible class.
      • ClassNotFoundException Thrown when a class is not found. Were all required tcz/pdb files installed?
      • GameEngineException An exception related to a game funcionality in the game engine.
      • IllegalAccessException Thrown when you try to load a private class using Class.newInstance().
      • IllegalArgumentException Thrown when an illegal argument was passed to a method.
        • NumberFormatException Thrown when an invalid number was passed to valueOf().
        • PatternSyntaxException Thrown when Pattern constructor’s argument doesn’t conform the Perl5 regular expression syntax.
      • IllegalStateException Signals that a method has been invoked at an illegal or inappropriate time.
      • IndexOutOfBoundsException Thrown when an index is used outside of the defined bounds.
        • ArrayIndexOutOfBoundsException Thrown when you try to access an array index that is < 0 or >= array.length.
        • StringIndexOutOfBoundsException Thrown when you try to create a String using the constructor that receives a byte array, an offset and the length.
      • IntHashtable.DuplicatedKeyException Tthrown when IntHashtable.
        allowDuplicateKeys
        is set to false and one tries to insert a duplicated key in the IntHashtable.
      • NegativeArraySizeException Thrown when you try to create an array with negative size.
      • NullPointerException Thrown when a null object has being used in the last operation.

13 Exit Codes

When the VM exits, it returns an exit code. Obviously, this chapter only concerns TotalCross applications running on Windows 32, iOS, Android, and Windows Phone 8, since on Java SE the TotalCross VM is not used.
The codes and their description are:
  • 100 If it was not possible to initialize the application current context.
  • 101 If it was not possible to load TCBase.tcz. TCBase.tcz was not found or is corrupted.
  • 102 If it was not possible to initialize the event handler.
  • 103 If it was not possible to load TCUI.tcz. TCUI.tcz was not found or is corrupted.
  • 104 If it was not possible to load the application .tcz. One of the most common errors.
  • 105 If the Settings class could not be loaded.
  • 106 If it is not possible to run multiple instances of the application and the application is already running. This only happens on Windows 32. One of the most common errors.
  • 107 It it was not possible to initialize the graphics before the MainWindow settings is loaded. One of the most common errors.
  • 108 If it was not possible to retrive the settings.
  • 109 If it was not possible to initialize the graphics after the MainWindow settings is loaded.
  • 111 If the class ras.ActivationClient can’ t be loaded or an exception occurs when loading it during the activation check. Its most probable cause is the corruption of the TCBase.
    tcz
    file.
  • 112 If the method getInstance() can’t be loaded from the class ras.ActivationClient during the activation check. Its most probable cause is the corruption of the TCBase.tcz file.
  • 113 If the method getInstance() during the activation check throws an exception.
  • 114 If the method isActivatedSilent() can’t be loaded from the class ras.
    ActivationClient
    during the activation process. Its most probable cause is the corruption of the TCBase.tcz file.
  • 115 If an error occurs when loading the TotalCross libraries.
  • 116 If the main class could not be loaded (has thrown an exception, was not found or is corrupted).

Part II. USER INTERFACE

Overview

The following chapters will introduce you to the main aspects of user interface in TotalCross, including graphical interface controls and events support.

14 User Interface styles

List of types

TotalCross has five look-and-feel types of user interfaces:
  • Flat – Defines a FLAT user interface style that looks like the Pocket PC look-and-feel. It’s basically the old WinCE style with flat controls.
  • Vista – Defines a Windows Vista user interface style. It’s like the Flat style with a shaded background. It looks better on 16bpp+.
  • Android – Defines an Android user interface style. Its like the Vista but with round borders. Its almost identical to the controls used by the Android platform.
The style can be switched to by using MainWindow.setUIStyle(int type), and it is platform-idenpendent (can be used in any device of any platform). Here is an example of use:
public class Foo extends MainWindow
{
	public Foo()
	{
		super("Hi bar", TAB_ONLY_BORDER);
		setUIStyle(Settings.Android);
	}
}
The decision of which user interface to use must be made in the main window’s constructor. If nothing is changed, Vista with a blue background is used. Changing the user interface outside the main window’s constructor can crash your application. If you try to change it more than once, a RuntimeException will be thrown. The other possible values to type are: Settings.Flat, and Settings.Vista.
In the pictures below you can see snapshots of the of the old UIGadgets sample running on JDK using different resolutions and user interface styles. After each picture you’ll find the command line options provided to the launcher on each case:


figure companion_resources/images/companion_UIGadgets_Android.png figure companion_resources/images/companion_UIGadgets_iPhone.png
/scr android /uiStyle android:

Android (320x480x16)
/scr iPhone: iPhone (320x480x24)
figure companion_resources/images/companion_UIGadgets_Win32.png
/scr Win32: Windows 32 (240x320x24)


figure companion_resources/images/companion_UIGadgets_uiStyleFlat.png figure companion_resources/images/companion_UIGadgets_uiStyleVista.png
/uiStyle Flat: Flat user interface style /uiStyle Vista: Vista user interface style
figure companion_resources/images/companion_UIGadgets_bbStorm.png
/penlessDevice: Acts as a device without touch screen. (notice the highlighted control)

Support for devices without pen

The iOS and Android platforms introduced the use of finger instead of pen (however, some new Android works better with pens than with finger, such as some devices with barcode scanner). A finger is much less precise than a pen, thus an algorithm is used to locate the closest control. These platforms also introduced the concept of drag and flick: drag means that you placed your finger in the screen and moved it while pressed, and flick means that you suddenly lifted the finger, to continue the scroll even while your finger is up. The scroll then slows down until a complete stop or if you press the finger again.
TotalCross 1.3 added support for this, activated when you set the user interface style to Android or when making Settings.fingerTouch = true.
The following controls supports flicking: button menu, grid, list box, list container, multi edit, scroll container (and its subtypes), tabbed container, and tree.
When fingerTouch is true, controls that can be flicked replace the scroll bar by a scroll position control, which only appears while the control is being scrolled or flicked.

15 Font Support

To keep the user interface consistent across different platforms, TotalCross applications uses its own font types instead of using the device’s font type. The basic font type chosen for TotalCross is the Tahoma font, included on the SDK as the TCFont.tcz file. It contains fonts with no size limit, regular and bold.
Unicode fonts are not included in the default font file!
The base size of the application’s font is defined as Font.NORMAL_SIZE, which is a constant initialized at runtime based on the screen resolution. This way you may select another size by using a relative value, like Font.NORMAL_SIZE+2 (which is equal to Font.BIG_SIZE), and the font will be correctly scaled among different resolutions. The constants Font.MIN_FONT_SIZE and Font.MAX_FONT_SIZE have the minimum and maximum font size, respectively. Their default values are 7 and 80 (48 for Windows 32), respectively, but can be changed.
Finally, Font.TABSIZE is the number of spaces that will drawn when a tab character is found by the VM. You can define the number of spaces that will be drawn setting this field. It defaults to 3, but you can change it at any time.
To create your own font files you may use the FontGenerator (included in the tc.jar). It creates a compressed file with your own set of bitmap fonts, and supports anti-aliased fonts, which makes the font appear rounded and improves legibility. The created font must be based on the TrueType fonts.
Some usage examples (here we assume the tc.jar is in the classpath):
  • Anti-aliased Arial font:
    java tc.tools.FontGenerator Arial /aa
  • Anti-aliased Arial font, but including only the sizes 12 and 14. Reducing the final font file from 158kb to 16kb:
    java tc.tools.FontGenerator Arial /sizes:12,14 /aa /rename:TCFont
  • Anti-aliased Arial font with Unicode characters up to 1279, but including only sizes 20, 24 and 28, and excluding bold fonts:
    java tc.tools.FontGenerator Arial /aa /sizes:20,24,28 /nobold
    /rename:TCFont /u 32-255 256-383 402-402 1024-1279
To see if the font you created is installed on the target device, query its name after the creation. If the font is not found, its name is changed to match the default font. When the parameter /rename: is used with TCFont, the default font is changed to the new created font.
The default font name used is stored in Font.DEFAULT.
Read totalcross.ui.font.Font JavaDoc for more information. Check also totalcross.
ui.font.FontMetrics
.

16 The MainWindow class

Every TotalCross program must have one and only one class that extends totalcross.ui. MainWindow. It is the interface between the VM and the TotalCross program: it receives all events and dispatches them.
import totalcross.ui.*;
​
public class Foo extends MainWindow
{
	public Foo()
	{
		super("Hi handheld world", HORIZONTAL_GRADIENT);
		gradientTitleStartColor = Color.getRGB(88, 130, 239);
		gradientTitleEndColor = Color.getRGB(10, 36, 106);
		titleColor = Color.WHITE;
	}
​
	public void initUI()
	{
		// add controls here
	}
​
	public void onExit()
	{
		// close stuff here
	}
}
The VM first calls the MainWindow default constructor (in this case, Foo()) and after that it enqueues a timer event, which will then call the initUI() method (inherited from the class Container). You must initialize all the user interface in the initUI() method. Initializing the user interface on the constructor might crash the application, and some operations can only be performed after the MainWindow’s initUI() method is reached.
The onExit() method is called when the VM exits under normal circumstances , i.e., if the user closes the application or the programmer ends the application by using exit() or exec(), or even when the application is halted by an unhandled exception. However, under abnormal circumstances where a fatal error occurs, reseting the device, the onExit() method is not called. When this is called, all threads are already killed.
To change the font used by all controls created, you must call the method setDefaultFont() on the first line of the MainWindow’s constructor.
If you’re going to set the MainWindow’s title and/or style, you should use super(title, style) instead of the methods setTitle() and setBorderStyle() to improve performance. However, you can’t use super() and setDefaultFont() together because both must be the on first line of the constructor. That means you’ll have to use setTitle() and setBorderStyle() instead of super(title, style) to be able to use setDefaultFont().
The style can be: Window.NO_BORDER, Window.RECT_BORDER, Window.ROUND_BORDER, Window.TAB_BORDER, or Window.TAB_ONLY_BORDER.

The Application lifecycle

The application on TotalCross has a predefined life cycle, handled by methods defined in MainWindow:
  1. initUI(): called when the application starts.
  2. onMinimize(): called when the user press the home key, or when a call is received, or when the screen turns off. On Android and Windows 32, the application execution is not paused. On Windows Phone 8 and iOS, it is paused.
  3. onResume(): called after the call ends, the screen is turned on again, or the program was exited softly on Android. It is also called again if the user clicks on the application icon on Android and iOS. On WP8, it is necessary to press back in the main menu and click on the opened application. Clicking on its icon in the list of applications will kill the current instance and relaunches it again.
  4. onExit(): called when the system decides that is time to finish the application. If the home key was pressed, this method is called when another TotalCross application is launched on Android if the application is not installed as a single package.
If the user press the home key and then forces the application to stop (by going to the Settings / Applications) on Android and iOS, then all Litebase tables may be corrupted (actually, no data is lost, but a TableNotClosedException will be issued). So, it’s a good thing to call
LitebaseConnection.closeAll()
in your Litebase instances in the onMinimize() method and recover them in the onResume() method. Just remember that all prepared statements, row iterators and result sets will be invalid and can’t be reused. Notice that the application might also be killed by the system if it’s on background.
On WP8, there is no way to kill an application. To kill it, you must restore it pressing back in the main menu and them openning the application again. Then, press back until finishing the application.
It is also a good practice to save all necessary data and application state so that the when the user goes back to the application it won’t need to do a task again, such as entering data again in a form which was lost when the application was closed by the system. Of course there are situations where the user must retype data, such as login and password and when using bank applications.
Remember that applications for Android, iOS, and Windows Phone 8 shouldn’t have an exit button or an exit option in the menu. The application should be designed to be opened all the time and only the system should close it when needed. Again, there are exceptions, for example when the user doesn’t agree with an eula and the application can’t continue running.
Finally, it is a good idea to map the device back key on Android and WP8 to go back to the last window/menu used. Just remember that on WP8 back is back, you just can use it to go back to the last screen used. On the first screen, back should exit the application. The only exception are games, where back during a game play goes to the pause window.
To know more detais about totalcross.ui.MainWindow, read its JavaDocs.

17 Adding Controls

The class totalcross.ui.Control is the base class of all user interface controls, like buttons and labels.
Controls must be added to a Container or one of its subclasses, like MainWindow. This container is referred as the parent of the control. To set the control bounds in the container, you can use the method setRect(x,y,width,height).

Relative positioning

TotalCross lets you place controls in two ways:
The hard and wrong way, by setting the control bounds with numbers.
E.g.: control.setRect(10,10,80,12);
The smart way, by using constants for relative coordinates. The idea of the relative positioning is to let the programmer concentrate mainly on the position and let the sizes be computed automatically. The constants are:

Coordinate X

LEFT places the control at the position 0 horizontal. If the parent is a Window, aligns the control at the left of the window’s client rect.
RIGHT aligns the control at the right. If the parent is a Window, aligns the control at the right of the window’s client rect (relative to the screen).
RIGHT_OF aligns the control at the right of the last control (relative to a control).
CENTER centers horizontally the control in the container (relative to the screen).
CENTER_OF centers horizontally based on the last control’s width (relative to a control).
BEFORE places the control before the last control added.
AFTER places the control after the last control added.
SAME places the control at the same x coordinate of the last control added.
KEEP keeps the last position used. To be used with WILL_RESIZE.

Coordinate Y

TOP places the control at position 0 vertical. If the parent is a Window, aligns the control at the top of the window’s client rect.
BOTTOM places the control at the bottom of the container. If the parent is a Window, aligns the control at the bottom of the window’s client rect.
CENTER centers vertically the control in the container.
CENTER_OF centers vertically based on the last control’s height.
BEFORE places the control before the last control added.
AFTER places the control after the last control added.
SAME places the control at the same y coordinate of the last control added.
BOTTOM_OF aligns the control at the bottom of the last control.
KEEP keeps the last position used. To be used with WILL_RESIZE.

Width

PREFERRED lets the control determine its best width. This is normally computed using the control’s text width in the selected font.
FILL the control’s width will fill the space left until the end of the container. Cannot be used with RIGHT/CENTER.
SAME sets the control’s width with the same width of the last control added.
FIT sets the control’s width to fit between the specified x position and the last control added x position. For example, if you place a label at LEFT, a button at RIGHT and want to place an edit between those two controls, use FIT as the control’s width using the Label as relative control.
SCREENSIZE computes the width as a percentage of the screen’s size (width). The percentage is passed as a number with + sign. E.G.: SCREEN_SIZE+20 will take the width as 20% of the screens size. Note that the size will change when the device is rotated.
WILL_RESIZE sets that the width will be resized later, when all controls were added. You cannot use RIGHT or CENTER for placing controls when using WILL_RESIZE. After all items were added, call the resizeWidth() method.
KEEP keeps the last position used. To be used with WILL_RESIZE.
PARENTSIZE computes the width as a percentage of the parent control size (will use parent’s width). If the parent is unknown, the screen size will be used instead.
SCREENSIZEMIN uses screen’s minimum size between width and height.
SCREENSIZEMAX uses screen’s maximum size between width and height.

Height

PREFERRED lets the control determine its best height. This is normally computed using the control’s text height in the selected font.
FILL the control’s height will fill the space left until the end of the container. Cannot be used with BOTTOM/CENTER.
SAME sets the control’s height with the same height of the last control added.
FIT sets the control’s height to fit between the specified y position and the last control added y position. For example, if you place a button at TOP, a label at BOTTOM, and want to place a list box between both, use FIT as the control’s height using the button as relative control.
SCREENSIZE computes the height as a percentage of the screen’s size (height).
WILL_RESIZE sets that the height will be resized later, when all controls were added. You cannot use BOTTOM or CENTER for placing controls when using WILL_RESIZE. After all items were added, call the resizeHeight() method.
KEEP keeps the last position used. To be used with WILL_RESIZE.
PARENTSIZE computes the height as a percentage of the parent control size (will use parent’s height). If the parent is unknown, the screen size will be used instead.
SCREENSIZEMIN uses screen’s minimum size between width and height.
SCREENSIZEMAX uses screen’s maximum size between width and height.
  • Before calling setRect(), the control must be added to the container and have its characteristics (like font, border type, etc) set. This is needed because the control position is computed based on its parent container and the control’s size is computed based on its characteristics. And all this is computed at setRect().
  • The control’s coordinates are always relative to its parent coordinates.
  • If you use LEFT/TOP/RIGHT/BOTTOM with the first control added to a window, it will use the coordinates of the window’s client rect, which are the container’s area excluding the title and the borders, if any.
  • You cannot use FIT in both width/height. It won’t work as expected because you can’t specify two controls to make it relative to. The width/height is always related to the last control added.
  • See the sources for the controls ColorChooserBox, NumericBox, and TimeBox to learn how to use WILL_RESIZE.
The use of relative positioning is highly recommended. It makes the program portable between different resolutions and fonts.

Making UI adjustments

The constants described above have one important feature: a variable or a number can be used to increment/decrement the result value. Examples: CENTER+2, BOTTOM-5, PREFERRED+4, FILL+2, BEFORE-5. This value cannot be higher than 5, otherwise your interface will not be properly shown in other resolutions. Using something like LEFT+10 or BOTTOM-15 is as bad as using absolute values.
However, these values suffer a problem: a pixel’s size may vary depending on the screen’s density (also known as DPI). So, 4 may be good at a 240x320 screen, but too small in a 600x1024 tablet.
To bypass these limitations, we introduced a way to compute that adjustment based on the current’s font size instead of a fixed pixel value. To enable it, you must set the following property at the application’s static initializer:
Settings.uiAdjustmentsBasedOnFontHeight = true;
After doing this, the adjustment you make will be a percentage of the font’s height. So, AFTER+50 is AFTER+(50% of font’s height), PREFERRED+20 is PREFERRED+(20% of font’s height), and so on.
However, this feature is not supported by all controls. To disable this feature for a particular control the property uiAdjustmentsBasedOnFontHeightIsSupported is set to false in the class constructor.
The controls can be traversed using the 5-way navigation present on most PDAs and Smartphones. It is automatically activated on penless devices, but can be activated on pen devices by setting the property Settings.keyboardFocusTraversable to true. Controls are traversed according to the order of elements in the field Container.tabOrder. The controls are added to the vector in the order as they are added to the container. This may not be optimal; you may assign a new order by adding the controls to the vector:
this.tabOrder = new Vector(new Object[]{ed1,btn1,btn2,ed2});

Useful members

More details and more members can be found in the totalcross.ui.Control JavaDoc.
  • FontMetrics fm The FontMetrics object created from the control’s assigned Font. Always recreated when the font changes.
  • int appId A public variable that can be assigned by the application with any value. For example, the Keyboard class uses this to store the PushButtonGroup index and discover which one of the five had issued the event.
  • Object appObj Same of appId, but it’s an Object instead of an int.
  • int clearValueInt Value used to clear a control that uses an index as the active value. The default is: 0.
  • String clearValueStr Value used to clear a control that uses a string as the active value. The default is an empty string: “”.
  • isHighlighting If true, the user is using the arrows to navigate across the controls, that is the keyboard arrows will be used to highlight the controls until one is selected. If false, the currently focused control is using the keys for its own purposes. If the device is penless and the control is lost, try setting this to true.
    Setting this to true will cause the KeyEvent to be intercepted and handled by the method
    changeHighlighted()
    . When the user press the ACTION (or ENTER) key to use the control, this flag is set to false and the focus will be set to the control, so it be able to use the arrows to navigate inside it. The control must then set this to true when finish using it or press the ACTION button again (which then sets the flag to true).
  • setRect(int x, int y, int width, int height) Sets the control bounds with the given parameters. If relative coordinates are used, they will be relative to the last added control.
  • setRect(Rect r) Same as setRect(r.x, r.y, r.width, r.height).
  • setRect(int x, int y, int width, int height, Control relative)
    Same as the first one, but coordinates will be relative to the given control instead of the last one added.
  • setRect(int x, int y, int width, int height, Control relative,
    boolean screenChanged)
    Same as above, but with the parameter screenChanged, which indicates that a screen change (resize, collapse) occured and the reposition() method is calling this method. Set by the system. If you call this method directly, always pass false to it.
  • add(Control control, int x, int y) Same as:
    add(control);
    control.setRect(x, y, PREFERRED, PREFERRED);
  • add(Control control, int x, int y, Control relative) Same as:
    add(control);
    control.setRect(x, y, PREFERRED, PREFERRED, relative);
  • add(Control control, int x, int y, int width, int height) Same as:
    add(control);
    control.setRect(x, y, width, height);
  • add(Control control, int x, int y, int width, int height,
    Control relative)
    Same as:
    add(control);
    control.setRect(x, y, width, height, relative);
The last four methods are useful when you’re adding controls that do not need to have any of its characteristics (like font or border) changed. Changing the control’s characteristics after those methods will give you unpredictable results. This happens because the control does not reposition/resize itself when you change its characteristics (just as an example, suppose that the default font has 11 in height, and you add the control; then you change the font to one with height 22; the control will not be resized and you’ll see only half of the text).
  • addTimer(int millis) Calls MainWindow.addTimer() to add a timer whose target is this control. Each time the timer ticks, a TIMER event will be posted to the control. The timer does not interrupt the program during its execution at the timer interval, it is scheduled along with application events. The Timer object returned from this method can be passed to removeTimer() to remove the timer. On Windows, the timer has a minimum resolution of 55 ms due to the native Windows system clock resolution of 55 ms. On the other platforms, the minimum timer resolution is 10 ms.
    If the control that holds the timer is removed from screen, the timer is also disabled. Consider using the dispatch-listener event model (addTimerListener()) instead of creating a control just to catch the event (if this is the case).
  • removeTimer(Timer timer) Calls MainWindow.removeTimer() to remove a timer associated with this control. Removes a timer from a control. true is returned if the timer was found and removed and false is returned if the timer could not be found (meaning it was not active).
  • getX(), getY(), getWidth(), getHeight() Returns the control’s bounds.
  • getX2(), getY2() Returns x+width-1 and y+height-1, respectively.
  • getParentWindow() Returns the parent window of this control. This is needed because the control’s parent may be a container, whose parent may be another container, and so on. It returns null if there’s no parent (e. g.: control still not added to any container). If this control is a window, will return itself.
  • repaint() Marks the control for repaint. When you call repaint(), the absolute area of the control regarding its parent window is invalidated (marked for repaint); then, the next time an event (a keypress, a timer, a pen event) occurs, the screen is updated. If you call repaint() and the control isn’t effectively repainted, you can use the repaintNow() method.
    If you want to avoid a method call, you can do Window.needsPaint = true;
  • repaintNow() Redraws the control immediately. If this control is a window, the whole window area is marked for repaint (useful if you’re removing some controls from a container) .
  • setEnabled(boolean enabled) Enables/disables the control to receive events. The control’s appearance is also changed to reflect the state.
  • setBackColor(int back)/getBackColor() Gets or sets the background color for the control.
  • setForeColor(int fore)/getForeColor() Gets or sets the foreground color for the control.
getForeColor() and getBackColor() returns the color depending if the control’s state is enabled or disabled.
  • clear() The clear method in this class is a placeholder for the other controls. It implements an easy way to clear all the controls in the screen. When you call the clear method of a container or a window, it recursively calls the clear method of all child controls. Each control will use clearValueInt() or clearValueStr() (both defined in this class) as the clear value, depending on the characteristics of the control. For example, a ListBox will use the clearValueInt() to set it as the selected index, while an Edit will use the clearValueStr() as the text inside. If you want to change the clear value, just assign a new value to one of these two fields.
  • reposition() Repositions the control after something has been changed. This method is called by the VM to reposition all controls on screen when a rotation occurs. It calls again setRect() with the original parameters.
  • onPaint(Graphics g) This is the method called when the control is going to be painted. You can use the given Graphics object to draw what you need on screen. The default’s control font is already set, and the clipping is also set to the control’s bounds. When this method is called, the Graphics object passed has been translated into the coordinate system of the control and the area behind the control has already been painted.
  • onBoundsChanged() Called after the user invoked the setRect() method. Good to compute positions that are dependant on the bounds.
  • onWindowPaintFinished() Every time _doPaint() finishes paintings all controls, this method is called. You can then do actions needed after the screen has finished the paint. Note that this method is only called on controls that extends window and on the focused control.

18 Events

Events represent all activity that goes on between the user and the application. When the user interacts with a program, the VM creates an event representing the action and communicates these actions to the programs using events.
This chapter is organized into the following sections to provide a quick start on event handling in TotalCross:
Control Events briefly explains the basic types of events handled by controls.
Handling Events explains the event-handling models available in TotalCross.
The Event class describes this class in more detail.
If you’re new to TotalCross and want to get things started quickly, skip the Delegation Event Model in section two – “Handling Events”.
If you’re familiar with TotalCross and wants to know more about events, make sure you understand both event-handling models.

Control Events

There are two basic types of events: pen events and key events. These events are handled by the window, which creates a new event of the proper type (pen event or key event) and forwards it to the target control. These events are handled by the control, which may convert them to other events, defined on the class ControlEvent.
Below we can see how each event defined on ControlEvent is handled by each control:
CURSOR_CHANGED event sent when user calls Edit.setCursorPos().
PRESSED this event is posted by controls when they receive a PEN_DOWN (like check, radio, tab panel), and a PEN_UP (button, list box, push button group, scrollBar) or even a
WINDOW_CLOSED
(comboBox).
FOCUS_IN and FOCUS_OUT posted by the window when a control different from the current one is clicked. The FOCUS_OUT is first posted to the old control, and the event FOCUS_IN is then posted to the new control.
TIMER this is a special event that is posted to the control that has a timer assigned to it; it is called when the timer countdown has reached zero.
WINDOW_CLOSED posted by the unpop method to notify all controls that a window has been closed. Some controls, like combo box, has a pop list assigned to it; when the pop list issues a WINDOW_CLOSED event, the combo box receives it and converts to a PRESSED event.
SIP_CLOSED The event type fot the SIP being closed by the system. Works on Android and iOS. The application cannot see this event since it is interpected by the topmost window.
HIGHLIGHT_IN and HIGHLIGHT_OUT these events are posted by the window when the user changes the focus to another control and the field Settings.
keyboardFocusTraversable
is set to true. The HIGHLIGHT_OUT event is first posted to the old control, and the event HIGHLIGHT_IN is then posted to the new control.
  • The field Settings.keyboardFocusTraversable default value is true for penless devices and false for touchscreen devices. You may set it to true on touchscreen devices to allow navigation through arrow keys.
Take a look at totalcross.ui.event.ControlEvent JavaDoc for more details.
Some controls have its own event class. They are Animation, Grid, and ListContainer. Take a look at totalcross.game.AnimationEvent, totalcross.ui.event.GridEvent, and totalcross.ui.event.ListContainerEvent JavaDocs for more details about their respectively event classes.
Take a look also at totalcross.ui.event.EnabledStateChangeEvent JavaDocs. This class represent an event generated when the enabled status of a control changes. This will occur when the method setEnabled() is issued.

Event Handling

TotalCross implements two event-handling models.

Inheritance Event Model

Similar to the Java 1.0 event model, this approach requires you to subclass UI components and override the event handling method – onEvent(Event e) – in order to catch and process UI events. When a control posts an event (using the postEvent() method) it is propagated sequentially up the UI hierarchy until it is either consumed, or the root of the hierarchy is reached.
If we have a button inside a tab panel in a window, and this button receives an event, the event is propagated up the hierarchy:
Window.onEvent()
TabPanel.onEvent()
Container.onEvent()
Button.onEvent()
After handling the event, you can break the event propagation by setting the Event.consumed field to true.
Here’s a simple example:
public class MyProgram extends MainWindow
{
	Button pushB;
​
	public void initUI()
	{
		add(pushB = new Button("Push me\nPlease"), CENTER, TOP);
	}
​
	public void onEvent(Event event)
	{
		switch (event.type)
		{
			case ControlEvent.PRESSED:
				if (event.target == pushB)
					// handle pushB being pressed
			break;
		}
	}
}
  • Disabled controls can only post TIMER events. The method postEvent() returns immediately if the control is not enabled and the type of the event received is not TIMER. The only exception to this occurs on fingertouch devices, to allow the flick stop or start when a PEN_DOWN occurs in a disabled control.
  • The event propagation stops if the control is moved to another container. For example, when you pop up the keyboard, the target edit is removed from its parent and added to the keyboard window; at this time, the event propagation (of the key event) stops.
  • The MainWindow does not receive an event posted by a control in a popup window. The only exception for this is the WINDOW_CLOSED, which is posted to the window that popped it.
  • A control that is not added to a container (or any parent of its container is not added to a window) will never receive events nor be repainted.
Small programs can handle the events by implementing the onEvent() method in the
MainWindow
class, but it can become confusing if the program gets bigger. An alternative is to create classes that extend Container and swap them on the MainWindow as the user navigates through the program. Each class implementing the onEvent() method will have to handle its own events.

Delegation Event Model

Similar to the event model introduced by Java 1.1, but this approach solves some limitations of the Inheritance Event Model, which are:
  • To be able to handle events, a class must subclass Control and implement the onEvent() method.
  • Since all event types are filtered through the onEvent() method, the method is executed whenever the control receives an event, regardless of its type. If a particular event type is ignored by the whole program, it will never be handled and consumed. Instead, it will keep going up on the hierarchy until it reaches the root, wasting resources in the process. The only way to avoid it – handling all event types, consuming events that are no handled anywhere on the program as soon as they are received – is discouraged and can lead to complex and error-prone code.
  • Events are supposed to be handled only by the control that receives the event. Handling events received by a control in another class is a hard task. An example is the tooltip control, which works by handling the pen events received by the control it refers to.
In this model, an event is propagated from a “source” object to a “listener” object by invoking a method on the listener. and passing in the instance of the event subclass, which defines the event type generated.
A listener is an object that implements a specific EventListener interface. An
EventListener
interface defines one or more methods which are to be invoked by the event source in response to each specific event type handled by the interface.
An event source is an object which originates events. The source defines the set of events it emits by providing a set of add<EventType>Listener(<EventType>Listener) methods which are used to register specific listeners for those events.
Here’s the same example, using an EventListener() instead of overriding onEvent():
public class MyProgram extends MainWindow
{
	Button pushB;
​
	public void initUI()
	{
		add(pushB = new Button("Push me\nPlease"), CENTER, TOP);
		pushB.addPressListener(
			new PressListener()
			{
				public void controlPressed(ControlEvent e)
				{
					// handle pushB being pressed
				}
			}
		);
	}
}
Creating listeners using the new <EventType>Listener, as indicated above, makes your program bigger and should be used with care. If you will add listeners of the same event type to many controls, implement the listener in the class like this:
public class MyProgram extends MainWindow implements PressListener

The Event class

Let’s now look at the details of each event class. TotalCross has ten event classes in the totalcross.
ui
package: the already described ControlEvent, AnimationEvent, GridEvent,
ListContainerEvent
, EnabledStateChangeEvent; and MediaClipEvent, KeyEvent, PenEvent, ScanEvent,
TimerEvent
, and UIRobotEvent. Their parent class is Event, which provides some useful members:
  • int type This is the event number, used to detect which event was posted.
  • Object target This one informs who is receiving the event (e. g.: the VM posts a
    PenEvent.PEN_DOWN
    to a control) or who is posting it (e. g.: the control posts an
    ControlEvent.PRESSED
    ). For user-interface events, this is the control the event is associated with.
  • int timeStamp Contains the timestamp at which the event was created.
Be careful with the timestamp. There is no way to convert it to the current hh:mm:ss:millis. It is just useful for comparison purposes; or to compute the time elapsed since the last event occurred. You can use it to simulate a double-click, by checking the elapsed time between two clicks.
  • boolean consumed When set to true during the event propagation, breaks the propagation chain.
To keep memory usage low, some VM routines reuse PenEvents and KeyEvents objects; so, if you just save a reference to the object instead of the timestamp itself, you may get unexpected results.
The Event class has also a static method called getNextAvailableEventId() to avoid conflicts when your application or library creates a new Event type.
For more detais about the totalcross.ui.event.Event class, take a look at its JavaDoc.
The event classes that have not been described before are:

PenEvent

PenEvent is a pen down, up, move, or drag event. A pen drag occurs when the pen moves while the screen is pressed. For more detais about the totalcross.ui.event.PenEvent class, take a look at its JavaDoc.

KeyEvent

A KeyEvent is a key press event. The key field is the key pressed or entered by other means (grafitti input). This is either a normal character key (if the value is < 70000, values from the ASCII table) or one of the special keys defined in the DeviceKeys interface.
Some of the keys defined in totalcross.sys.SpecialKeys need a special code to be manipulated: they are not handled by the application unless the application requests it. For example, the totalcross.sys.SpecialKeys.UP and totalcross.sys.SpecialKeys.DOWN are automatically sent to the application’s event queue, but the SpecialKeys.KEYBOARD_* and SpecialKeys.HARD*, not. To be able to intercept these keys, you must use the method Vm. interceptSpecialKeys(mask).
The SpecialKeys interface does not have all possible keys mapped. If you have a particular device, you can learn the code of a key by creating a test application and calling Vm.showKeyCodes(boolean on). When this mode is on, each time a key is pressed, the VM shows an alert with the code. Then use this key code in the Vm.interceptSpecialKeys().
For more detais about the totalcross.ui.event.KeyEvent class, take a look at its JavaDoc.

MediaClipEvent

This class represents the events posted by a soundclip control. For more detais about the totalcross.
ui.media.MediaClipEvent
class, take a look at its JavaDoc.

ScanEvent

ScanEvent is an event thrown by the barcode scanner. Used in the Scanner class. For more detais about the totalcross.io.device.scanner.ScanEvent class, take a look at its JavaDoc.

TimerEvent

TimerEvent represents a control’s timer. Timers are created and destroyed using the addTimer() and removeTimer() methods present in the Control class. For more detais about the totalcross.
ui.eventTimerEvent
class, take a look at its JavaDoc.

UIRobotEvent

Event sent when a robot runs. The target is always the MainWindow’s instance. For more detais about the totalcross.unit.UIRobotEvent class, take a look at its JavaDoc.

Listener Interfaces

This subsection lists the interfaces used to add listeners to use the delegation event model. All of them are in the package totalcross.ui.event. For more details about them, take a look at their JavaDocs.
  • EnabledStateChangeListener
  • FocusListener
  • GridListener
  • HighlightListener
  • KeyListener
  • ListContainerListener
  • MouseListener
  • PenListener
  • PressListener
  • TimerListener
  • WindowListener

19 Basic Controls

In this chapter we will learn the details of all basic controls in the package totalcross.ui: button, edit, label, check, radio, radio group controller, combo box, list box, multi edit, grid, and tooltip.
Each control can have its font, foreground/background colors and state (enabled/disabled) set. Their look depends on the look-and-feel set on the MainWindow constructor (Vista is the default).
For each control we’ll see its characteristics, how to create them with the provided constructors, and how to handle their events.
You can see most of the user interface controls available in TotalCross looking at the ui package from the TotalCrossAPI sample.
Remember you must set the characteristics of the controls before calling setRect()/add() to set their bounds.

Button

Buttons are the most used types of controls. They are used to invoke an action or to confirm something, like [ok] or [cancel]. Here are some screenshots from some buttons using the Android style from the old samples UIGadgets and UIControls (now inside the ui package from the TotalCrossAPI sample):
figure companion_resources/images/button_001.png figure companion_resources/images/button_002.png
figure companion_resources/images/button_003.png figure companion_resources/images/button_004.png
figure companion_resources/images/button_005.png figure companion_resources/images/button_006.png
Some characteristics of TotalCross buttons are:
  • It supports multi-lined text, mixed with images.
  • It has five border styles:
    BORDER_NONE Specifies no border for this button.
    BORDER_SIMPLE Specifies a single-lined border for this button.
    BORDER_3D Specifies a 3d border for this button.
    BORDER_3D_GRADIENT_VERTICAL_GRADIENT Specifies a vertical 3d-gradient border for this button. Note that, in this mode, the back and fore colors are set using the
    borderColor3DG
    , topColor3DG, and bottomColor3DG properties.
    BORDER_3D_GRADIENT_HORIZONTAL_GRADIENT Specifies a horizontal 3d-gradient border for this button. Note that, in this mode, the back and fore colors are set using the borderColor3DG, topColor3DG, and bottomColor3DG properties.
    BORDER_GRAY_IMAGE Specifies that the image passed in the constructor is a gray image that will be recolorized and resized to the text’s size. To create the image that will work with this border type, do this:
      1. Get an empty image from somewhere in the web (http://www.aaa-buttons.com/ is a good place; choose a light background image to prevent problems described in step 5).
      2. Do not resize the button.
      3. Convert the button to grayscale (you can download IrfanView (
        http://www.irfanview.com/) and choose menu Image / Convert to Grayscale.
      4. When saving the image (as .png), don’t forget to save the transparent color (IrfanView has an option to save it in the save dialog).
      5. Images with a smooth round border should have the background colorized to the target background color; otherwise, the results will not be perfect.
      Important: you must set the button colors before calling setBorder(). Here’s a sample:
      Button btn = new Button("Barbara\nHazan", new Image("button1.png"), CENTER, 8);
      Itn.setBackColor(backColor);
      btn.borderColor3DG = 0x008800; // used to colorize the image
      btn.setFont(font.asBold());
      btn.setBorder(Button.BORDER_GRAY_IMAGE);
      add(btn,CENTER,CENTER); 
      
  • A button can be made sticky by setting the isSticky field. It keeps pressed until the next click. Note that this disables autoRepeat.
  • If you create an image button and add a text using the setText() method, both of them will appear on the control.
  • If the button has an image, you can change the image that is displayed while the button is pressed by changing the pressedImage field. Below is a sample:
    Image img = btn.getImage().getFrameInstance(0); // gets a copy of the image
    img.applyColor(Color.RED); // colorize as red
    btn.pressedImage = img;
    // another option: btn.pressedImage = btn.getImage().getFadedInstance(Color.RED); 
    
  • If you create an image for the button, make sure that it’s a PNG with alpha-channel (transparency).
  • You can make a button fire more than one event while it is pressed changing the field
    autoRepeat
    . The PRESSED event will be sent while this button is held. The field INITIAL_ DELAY is the initial delay to start the auto-repeat, which defaults to 600 ms, and the field AUTO_DELAY is the frequency in which the PRESSED event will be posted after the INITIAL_ DELAY was reached, which defaults to 150 ms.
  • The methods setImage() (sets the image that is displayed in the button; the
    transparentColor
    property of the Image must be set before calling this method) and setText() don’t resize the button to fit the image.
  • The clear() method is ignored by the Button class.
  • The class Button has the static field commonGap. It defines the internal gap between the button content and its borders. Changing this value affects all buttons created after the new value was set – it does not affect buttons previously created. This is useful when you’re creating the buttons but don’t want to keep calling the setGap() method. Note that this value will be added to the value of gap. A good practice is to set the field for the desired value before you create the buttons (at the beginning of initUI() for instance) and reset it back to zero after you finish (at the end of initUI()). Changing this also affects the size of the ScrollBars created.
  • You can change the color used when the button is pressed using the setPressedColor() method. This changes the color that the button’s background will go (’armed color’) when the button gets a PENDOWN event. The default is the cursor color for the background. In Android user interface style, using a bright color may result in a white background. Use a darker color in this case.
  • You can simulate the button being pressed by calling the method press() and passing true as parameter. Passing false simulates a release. This method does not generate events.
Sample code:
public void initUI()
{
	Button btn1 = new Button("Clients", img, TOP, 1);
	btn1.setBackColor(Color.WHITE);
	add(btn1, LEFT+5, TOP+5);
​
	Button btn2 = new Button("Clients", img, BOTTOM, 1);
	btn2.setBackColor(Color.WHITE);
	add(btn2, AFTER+5, SAME);
​
	Button btn3 = new Button("Button disabled");
	btn3.setEnabled(false);
	add(btn3, LEFT+5, AFTER+5);
​
	Button btn4 = new Button("Button none border");
	btn4.setBorder(Button.BORDER_NONE);
	btn4.setForeColor(Color.RED);
	btn4.setBackColor(Color.WHITE);
	add(btn4, SAME, AFTER+5);
​
	btn5.setBorder(Button.BORDER_3D_GRADIENT);
	btn5.setBackColor(Color.WHITE);
	add(btn5, SAME, AFTER+5);
​
	Button btn6 = new Button("Button 3D Grad Border");
	btn6.setBorder(Button.BORDER_3D_GRADIENT);
	btn6.setForeColor(Color.WHITE);
	add(btn6, SAME, AFTER+5);
}
Every time a button is pressed, it posts a ControlEvent.PRESSED event to the parent container. Below is an example of how to handle this event.
public void onEvent(Event event)
{
	switch (event.type)
	{
		case ControlEvent.PRESSED:
			if (event.target == btn1)
				new MessageBox("HI", "Natasha").popup();
	}
}
To just display images in the screen, use the ImageControl class instead of Button.
For more information, check the totalcross.ui.Button JavaDoc.

Edit

Edit is a control used to get text input when the user types using the keyboard or the grafitti area. It allows you to select text and cut/copy/paste it. Here are some screenshots from some edits using the Android style from the old UIGadgets and UIControls samples (now inside the ui package from the TotalCrossAPI sample):
figure companion_resources/images/edit_001.png figure companion_resources/images/edit_008.png
figure companion_resources/images/edit_002.png figure companion_resources/images/edit_003.png
figure companion_resources/images/edit_004.png figure companion_resources/images/edit_005.png
figure companion_resources/images/edit_006.png figure companion_resources/images/edit_007.png
Here are some other features:
  • There are two constructors: the default (with no arguments) that creates an edit with no constraints, and one constructor that receives a mask string. The mask is used, primarily, to compute the preferred width. If the mask is "", the width will be set to FILL. In order to use the mask to format the input, you must call the setMode() method passing true to the second argument.
  • You can limit the maximum length of characters that can be entered, by using the
    setMaxLength()
    method. If the user tries to enter text above the limit, a beep is sounded and the text is ignored. If you set a text greater than the maximum length, it will be truncated.
  • numbersSet = "0123456789"
    • currencyCharsSet = "0123456789.+-"
    • dateSet = numbersSet + Settings.dateSeparator
  • Those character sets are used in the setMode() method that receives one of the following constants:
    • NORMAL: in this mode any character can be entered. On all platforms the default input method is used.
    • DATE: this mode uses the dateSet() and the totalcross.ui.CalendarBox() is the one popped up.
    • CURRENCY: the currencyCharsSet is used and the totalcross.ui.
      CalculatorBox
      is the one popped up. You can change the calculator to a numeric box for a specific edit by making setKeyboard(Edit.KBD_NUMERIC) or for all edits of the application, by doing Edit.useNumericBoxInsteadOfCalculator = true.
    • PASSWORD: any character can be entered and the totalcross.ui.Keyboard is the one popped up. Only the last character typed is displayed in this mode, all the others are replaced by an asterisk (*).
    • PASSWORD_ALL: same of PASSWORD except that all characters are replaced by an asterisk (*), including the last one.
  • There’s another setMode() method that, besides the constants listed above, also receives a boolean to make the edit use a mask on the input text instead of popping a window. It should be used with the constants DATE and CURRENCY.
  • To retrieve the text shown as is, use getText(). If masking is enabled, the text with the mask is returned. You can return the text without the mask using getTextWithoutMask(). For non-currency mode, only characters whose corresponding mask is ’9’ are returned. To set the text to another one, call setText(). One of the setText() methods has a flag to indicate whether to post a PRESSED event.
  • To return the length of a text without having to call getText(), you can call getLength(). To apply trimming in the string and return its length, call getTrimmedLength(). This method consumes less memory than getText().trim().length().
  • The method setDecimalPlaces() can be used with setMode(CURRENCY, true) to set the number of decimal places the mask will use. The default value is 2. It cannot be used with masked edits; pass the number of decimal places in the mask itself. The only exception is when you want to use the default CURRENCY mask passing a null mask in the constructor; in this situation, you can call setDecimalPlaces() before calling setMode(), and a mask will be constructed with the given number of decimals.
  • The edit can be set as read-only using the method setEditable() and setting it to false. You can also set the field hasCursorWhenNotEditable to false to make the blinking cursor not appear when this edit is not editable.
  • There’s a public member named overwrite which, if set to true, turns off insert mode (i. e., characters will be replaced if written over).
  • The member alignment, which can be set to LEFT, RIGHT or CENTER, lets you control the alignment of the text inside the edit after the control loses the focus. The text is always left-aligned when it has the focus. The only exception is when mode is set to CURRENCY, when the numbers will be typed using right-align.
  • You can invoke the default popup keyboard for the edit using the popupKCC() method (KCC = Keyboard, Calculator, and Calendar).
  • You may specify which keyboard will be popped up, overriding the default behavior, using the setKeyboard() method, passing the constants KBD_NONE (no keyboard will be popped up for this edit), KBD_DEFAULT (the default keyboard for the current mode will be used for this edit) , KBD_KEYBOARD (the Keyboard class or the internal virtual keyboard will be used for this edit), KBD_CALCULATOR (the Calculator will be used for this edit), KBD_CALENDAR (the Calendar will be used for this edit), KBD_NUMERIC (the NumericBox will be used for this edit), or KBD_TIME (the TimeBox will be used for this edit).
    Note that setMode()calls setKeyboard(KBD_DEFAULT), so be sure to set the mode before calling setKeyboard().
  • You may force all characters entered to be automatically converted to upper or to lower case by setting the field capitalise with the constants ALL_UPPER or ALL_LOWER; the constant ALL_NORMAL disables this feature.
  • The field autoSelect makes the edit automatically select the text when it receives the focus. This is always true for penless devices.
  • The method setCursorPos(start, end) can be used to set the selected text of the edit (if start != end). It can be used to set the cursor position, if start equals end. start must be less or equal to end, and both must be >= 0. It can also be used to clear the selected text, calling setCursorPos(-1,0). Note: if you’re setting the cursor position before the edit is drawn for the first time, the edit will not be scrolled if the end position goes beyond the limits. Important! No bound checking is made. Be sure to not call this method with invalid positions! Example:
    ed.setText("1234567890123456");
    ed.setCursorPos(3,14);
    ed.requestFocus(); 
    
  • The method getCursorPos() returns an integer array (int[]) with the start (array index 0) and end (array index 1) positions of the cursor. It can be used to find the selected text with getText(), which will be the substring(start, end). Example:
    int []cursorPos = ed.getCursorPos();
    int start = cursorPos[0];
    int end = cursorPos[1];
    String text = ed.getText();
    if (start != -1) // is the text selected?
    {
    	String selectedText = text.substring(start,end);
    	...
    } 
    
  • When a selection is made or when the user clicks and holds an edit at the same point, a popup menu is shown so the user can cut/copy/paste. You can disable this feature by calling Edit.clipboardDelay = -1. This field can also be used to define the time that the user will have to press to see a popup menu with copy/paste options, which defaults to 1500 ms. It also affects MultiEdit.
  • In the Android user interface style, you can change the color of the edit that has focus using the focusColor field. By default, there’s only a blinking cursor.
  • You can remap a key to be used in the edit (for example, remap F1 to 1, F2 to 2, etc) by calling the mapKeys(String from, String to) method. This method maps the keys in the from character array into the keys in the to character array.
    For example, to enable a ’numeric pad’ on devices that has the 1 in the u character, you can use this:
    ed.mapKeys("uiojklnm!.","1234567890");
    
    To make sure that lowercase characters are also handled, you should also change the capitalise mode:
    ed.capitalise = Edit.ALL_LOWER;
    
    If you want to disable a set of keys, use the setValidChars() method. Note that mapKeys() have precendence over setValidChars().
    The from parameter must have the same length of to. Set it to null to disable mapping.
  • The clear() method uses clearValueStr to set the text of the edit.
Sample code:
public void initUI()
{
	Edit ed1 = new Edit();
	add(ed1, LEFT, TOP+5, FILL, PREFERRED);
​
	Edit ed2 = new Edit("9999999999999");
	ed2.setDecimalPlaces(4);
	ed2.setMode(Edit.CURRENCY,true);
	add(ed2, RIGHT, AFTER+5);
​
	Edit ed3 = new Edit("99/99/9999");
	ed3.setMode(Edit.DATE);
	add(ed3, LEFT, SAME);
​
	Edit ed4 = new Edit("AAAAA");
	ed4.setMode(Edit.PlainORD);
	add(ed4, LEFT, AFTER+5);
	ed4.setText("pass");
​
	Edit ed5 = new Edit(""); // use FILL as preferred width
	ed5.setMaxLength(4);
	ed5.setValidChars("123ABC");
	add(ed5, LEFT, AFTER+5);
​
	Edit ed6 = new Edit("Can’t Edit");
	ed6.setEditable(false);
	add(ed6, LEFT, AFTER+5);
}
The Edit control posts a ControlEvent.PRESSED event when the text changes. You may also want to intercept the event ControlEvent.FOCUS_OUT to validate the edit’s text.
For more information, check the totalcross.ui.Edit JavaDoc.

Label

This control is used to display static text or a marquee. The label in TotalCross can also display multiple lines of text, separated by the character \n. A label never receives focus neither dispatches events. Here is a screenshot from a multi-lined label using the Android style from the old UIGadgets sample:
figure companion_resources/images/label_001.png
Here are some other features:
  • Label has three constructors:
    • Label() Creates an empty label, using FILL as the preferred width.
    • Label(String text) Creates a label displaying the given text (that may be changed at runtime) using left alignment. Supports inverted text, multiple lines and is scrollable by default.
    • Label(String text, int align) Like the above, but you can also set the horizontal alignment using one of the constants LEFT, CENTER, RIGHT, or FILL (justified).
  • The three align constants are inherited from the totalcross.ui.Control class, where they are used in the setRect() method. Furthermore, you can use
    new label(“my text”, Label.CENTER) or new Label(“my text”, CENTER); it will work in the same way.
  • The vertical alignment can be controlled using the vAlign field. Defaults to CENTER.
  • The text is used to compute the preferred size. If you pass an empty string as text, the preferred width will be FILL.
  • You can set/change the label’s text alignment accessing the public member align. The possible values are LEFT, RIGHT, CENTER, or FILL (justifies the text). If align is CENTER and the text is wider than the label, only the right portion will be lost.
  • You can call the set3d() method to make the label have a 3d look, by drawing it with the foreground color and with a color a little brighter than the background color.
  • Labels can look inverted, by swapping the background and the foreground colors. To do this, use the setInvert() method. By calling it twice you can make the text blink.
  • The text can be highlighted using the setHighlighted() method. This method paints the text in all directions with a brighter color, then centered, with the foreground color. The highlight color can be set using setHighlightedColor(). The default color is a brighter foreground color. -1 must be passed to the method to use this default value.
  • Labels can’t be inverted and 3d at the same time. Calling set3d() cancels setInverted() and vice-versa.
  • If the label is a multi-line one, you can use the method scroll(boolean down) to make it scroll up and down, and also the canScroll(boolean down) method to peek if it can be scrolled in the desired direction. A label can scroll if the number of text lines is greater than the actual height. To see a good usage example of this feature, see the totalcross.ui. dialog.MessageBox class. By default, the scroll is done a page at a time; to scroll a line at a time, change the pageScroll field to false. scroll() returns true if success; false if no scroll is possible.
  • By default, the clear() method does not affect the label’s text because clearing a label is usually not a desired action. If you really want to clear it, you must explicitly set the clearValueStr property.
  • The setText() method, used to change the label’s text, does not change the control’s bounds. You must be sure that the label has enough size to hold the text otherwise it will be clipped. If you’re setting the text in CURRENCY mode, the text must be set not formatted (unmasked).
  • By default, this class does not automatic parses the text because this is a slow operation. You may change this behaviour by using the autoSplit field, or calling the split() method directly. This method splits the text to the given width. Remember to set the font (or add the label to its parent) before calling this method. You can also you can use a handy method to parse the text that you want to display: see the totalcross.sys.Convert.insertLineBreak() method.
    Set autoSplit to true to let the label split its text based on the width every time its width changes. If the height is PREFERRED, the label will change its size accordingly. You may change the height again calling setRect().
  • The method setMarqueeText(String text, int delay, int loopCount, int step) shows the label as a horizontal marquee. The text must have a single line (if it has more than one line, only the first line is shown). To stop the marquee, just call
    setText()
    . When a window covers the marquee, it is halted, and resumed when the window is closed.
    delay is the timer delay in ms used to scroll the marquee, where 100 ms is a good value. loopCount is the number of times the text will loop. Set to -1 to loop forever. When the loop count is reached, the text is cleared. Finally, step is the step in pixels in which the text will be scrolled. If step > 0, the scroll will be from left to right; if step < 0, the scroll will be from right to left.
  • If you have problems with labels when a rotation occurs, check the preferredWidthText field.
    By default, the getPreferredWidth() uses the current text to compute the width. However, if you create a label with a predefined text that will be changed later, in the advent of a reposition, the preferred width will be recomputed again using the current text and not the predefined one. For example, if you do:
    Label l = new Label("99");
    l.setText("0");
    
    then the preferred width will be computed based on "0", not in "99". To change this behaviour, assign to this field the text that you want to be used to compute the preferred width.
  • You can add a border to the label by changing the borderColor field. Defaults to -1, which means no border. Note that the border affects the label’s size (width and height are increased by 4), so you must set this field before setting the rect. If you want a bigger gap between border and text, you can use setInsets(int left, int right, int top, int bottom), which defines a space to be placed around the text of the label.
  • The background can be changed from solid (defaults to SOLID_BACKGROUND) to a shade by using the backgroundType field. The other values can be VERTICAL_GRADIENT_ BACKGROUND (specifies a vertical 3d-gradient background for this label) and HORIZONTAL_ GRADIENT_BACKGROUND (specifies a horizontal 3d-gradient background for this label). One color is the background color, and the other color is defined by firstGradientColor (defaults to Color.WHITE) and secondGradientColor (defaults to Color.BLACK) fields.
Sample code:
public void initUI()
{ 
	add(new Label("This is a simple label"), LEFT, TOP);
​
	Label lab1 = new Label("This is a centered label", CENTER);
	add(lab1);
	lab1.setRect(LEFT, AFTER, FILL, PREFERRED);
​
	Label lab2 = new Label("This is another centered label", CENTER);
	add(lab2);
	lab2.setRect(LEFT, AFTER, FILL, PREFERRED);
​
	Label lab3 = new Label("Wow! A wonderful 3D label!");
	lab3.set3d(true);
	add(lab3, LEFT, AFTER);
​
	Label lab4 = new Label("A beautiful inverted label");
	lab4.setInvert(true);
	add(lab4, LEFT, AFTER);
​
	Label lab5 = new Label("This is a very long\ntext and i dont\nwant to waste my\ntime parsing it to\nbe fit it!");
	add(lab5);
	lab5.setRect(CENTER, AFTER, PREFERRED, PREFERRED);
}
For more information, check the totalcross.ui.Label JavaDoc.

Check

Check is a control that has a rectangle with a check inside and a text at right, and is used for items that have an on/off state. When it is on, the check box is marked; otherwise, it is unmarked.
Here are some screenshots from some checks using the Android style from the old UIControls sample (now inside the ui package from the TotalCrossAPI sample):
figure companion_resources/images/check_002.png figure companion_resources/images/check_003.png
figure companion_resources/images/check_001.png
Here is an example showing a check being used followed by some properties:
public class MyProgram extends MainWindow
{
	Check check;
​
	public void initUI()
	{
		add(check = new Check("Check me"), LEFT, AFTER);
	}
​
	public void onEvent(Event event)
 i
		if (event.type == ControlEvent.PRESSED && event.target == check)
		{
			bool checked = check.isChecked();
			// ... handle check being pressed
  • It has only one constructor:
    • Check(String text) Creates a check control displaying the given text.
  • To change the check’s color, which defaults to the foreground color, use the checkColor field.
  • The method isChecked() returns the current checked state of the control.
  • You may use setChecked(boolean checked) to change its state to checked or unchecked.
  • The method setText() can be used to change the displayed text.
  • The method clear() uses the field clearValueInt to set the check state. It sets the control as checked only if clearValueInt is 1, otherwise it sets the control as unchecked.
Sample code:
public void initUI()
{
	Check chk1 = new Check("Checked enabled");
	add(chk1, LEFT, TOP+5);
	chk1.setChecked(true);
​
	Check chk2 = new Check("Unchecked enabled");
	add(chk2, SAME, AFTER+5);
​
	Check chk3 = new Check("Check disabled");
	add(chk3, SAME, AFTER+5);
	chk3.setChecked(true);
	chk3.setEnabled(false);
}
Like the button control, the only event that the check control posts is the ControlEvent. PRESSED. Below we see an example of how to handle a click on the check:
public void onEvent(Event event)
{
	switch (event.type)
	{
			if (event.target == chk1)
			{
				String state = chk1.isChecked()? "checked" : "unchecked";
				new MessageBox("TotalCross", "Checkbox " + state).popup();
			}
		break;
}
For more information, check the totalcross.ui.Check JavaDoc.

Radio and RadioGroupController

Radio is a control that allows the user to choose one of a predefined set of options. They are usually grouped together, and when the user selects one of them, any previously selected radio in the same group becomes deselected.
Here are some screenshots from some checks using the Android style from the old UIControls sample (now inside the ui package from the TotalCrossAPI sample):
figure companion_resources/images/radio_001.png figure companion_resources/images/radio_002.png
figure companion_resources/images/radio_003.png
Here is an example showing how radio is used followed by some properties:
public class MyProgram extends MainWindow
{
	RadioGroupController rgGender;
	public void initUI()
	{
		rgGender = new RadioGroupController();
		add(new Radio("Male", rgGender), LEFT, AFTER);
		add(new Radio("Female", rgGender), AFTER+2, SAME);
		rgGender.setSelectedIndex(radioMale); // activate the specified one.
	}
​
	public void onEvent(Event event)
	{
		if (event.type == ControlEvent.PRESSED && (event.target instanceof Radio) && ((Radio)event.target).getRadioGroup() == rgGender)
		{
			boolean male = rgGender.getSelectedIndex() == 0;
			// ... handle radio Male being pressed 
  • In TotalCross, radio controls are grouped together using the radioGroupController.
  • It has two constructors:
    • Radio(String text) Creates a radio control displaying the given text.
    • Radio(String text, RadioGroupController radioGroup) Creates a radio control displaying the given text and attached to the given radio group.
  • radioGroupController is not a control, therefore you cannot add it to a container and it does not post events since it does not extend Control. It just provides useful methods to handle groups of radio controls, like:
    • add(Radio newMember) Adds a new Radio to the list of Radios this controller handles. This method is called by the Radio’s constructor.
    • remove(Radio oldMember) Removes the given Radio from the list of Radios this controller handles. You must explicitly call this method, if needed.
    • getSelectedIndex() Returns the index of the selected Radio (in the order that the Radios were added to the container), or -1 if none.
    • setSelectedIndex(int index) Used to select a Radio by its index and deselects the other one. The parameter index is a zero-based index of the Radio to be set. -1 must be passed to disable all of them.
    • getSelectedItem() Returns the Radio selected, or null if none.
    • setSelectedItem(Radio who) Selects a given radio group controller. It is called by the Radio when a click was made.
    • setSelectedItem(String text) Selects a Radio whose text matches the given caption.
    • getRadio(int idx) Returns the Radio at the given index.
  • These indices are based in the order that the radios are added to the container.
  • You may use the getRadioGroupController() method to make it easier to parse the events. It returns the RadioGroupController that this radio belongs to, or null if none.
  • The check’s text is right justified in the control’s width by default. You may change this behavior by setting the field leftJustify to true if the width is above the preferred one. This will make the text to be left justified in the control’s width.
  • To change the check’s color, which defaults to the foreground color, use the checkColor field.
  • The clear() method selects the given radio if its clearValueInt is 1.
Sample code:
public void initUI()
{
	RadioGroupController rgFruits = new RadioGroupController();
	add(rOrange = new Radio("Orange", rgFruits), LEFT, TOP + 5);
	add(rApple = new Radio("Apple", rgFruits), LEFT, AFTER);
	add(rGrape = new Radio("Grape", rgFruits), LEFT, AFTER);
	add(rLemon = new Radio("Lemon", rgFruits), LEFT, AFTER);
	rOrange.setChecked(true);
​
	String[] numbers = { "One", "Two", "Three" };
	RadioGroupController rgNumbers = new RadioGroupController();
	for (int i = 0; i < numbers.length; i++)
		add(new Radio(numbers[i], rgNumbers), i == 0 ? CENTER : SAME, i == 0 ? (TOP + 5) : AFTER);
	add(rEna = new Radio("Enable"), RIGHT, BOTTOM);
	add(rDis = new Radio("Disable"), BEFORE - 2, BOTTOM);
	rDis.setEnabled(false);
	rDis.setChecked(true);
}
The radio control posts a ControlEvent.PRESSED event when the user clicks on it. Below is an example of its use:
public void onEvent(Event event)
{
	switch (event.type)
	{
		case ControlEvent.PRESSED:
		{
			if (event.target instanceof Radio)
			{
				Radio r = (Radio)event.target;
				RadioGroup rg = r.getRadioGroup();
				if (rg == rgFruits)
				{
					repaintNow(); // the MessageBox below will popup before the screen is updated to unselect the Radio
					new MessageBox("TotalCross",r.getText()+" juice")).popup();
				}
				else if (rg == rgNumbers)
				{
					repaintNow(); 
					new MessageBox("TotalCross","Number selected: "+r.getText()).popup();
				}
				else
				{
					// no RadioGroup; we have to handle by ourselves
					if (r == rEna)
					{
						rEna.setChecked(true);
						rDis.setChecked(false);
						rDis.setEnabled(true);
					}
					else if (r == rDis)
					{
						rEna.setChecked(false);
						rDis.setChecked(true);
						rDis.setEnabled(false);
					}
				}
			}
		}
		break;
	}
}
For more information, check the totalcross.ui.Radio JavaDoc.

ComboBox and ComboBoxDropDown

The combo box is a very useful control to display a list of items where only one can be selected. It consists of a text with a button and a ComboBoxDropDown control that is popped up when the button is pressed. The popped up list is closed when one of its items is selected or the control loses the focus.
Here are some screenshots from a simple combo box using the Android style from the old UIControls sample (now inside the ui package from the TotalCrossAPI sample) Note that below the combo box component, there is a list box:
figure companion_resources/images/combo_001.png figure companion_resources/images/combo_002.png
figure companion_resources/images/combo_003.png figure companion_resources/images/combo_004.png
Another combo box from the old sample UIGadgets, also using Android style, can be seen here, where the left combo box has a attached MultiListBox, which extends ListBox and lets the user select more than one item:
figure companion_resources/images/combo_005.png figure companion_resources/images/combo_006.png
figure companion_resources/images/combo_007.png
figure companion_resources/images/combo_008.png figure companion_resources/images/combo_009.png
  • The ComboBox class has four constructors:
    • ComboBox() Creates an empty combo box.
    • ComboBox(ListBox userListBox) Creates a combo box with a
      ComboBoxDropDown
      containing the given list box. You can extend ListBox to draw the items by yourself and use this constructor so the pop list will use your class and not the default ListBox one. This constructor forces ListBox.simpleBorder to true. Note: the list box items must be already set.
    • ComboBox(Object[] items) Creates a combo box with the given items.
    • ComboBox(ComboBoxDropDown userPopList) Constructs a combo box with the given ComboBoxDropDown already filled.
  • The combo box drop down control is placed above or below the combo box, without covering it.
  • The ComboBoxDropDown height depends on the control’s position: it will open in the direction that has the most space left (above or below). If you set the public member fullHeight to true, then it will have the same height as the screen, maximizing the number of items displayed. In the same way, the fullWidth will set the width to the screen’s one.
  • The clear() method selects the index using the clearValueInt value, which is 0 by default.
  • The color used in the setBackground() method will be used in the arrow button only. The background color of the combo box text and ComboBoxDropDown will be a lighter version of the given color.
  • You can change the combo box’s scroll bar default width. To do so, you must use the static field Button.commonGap. Increasing its value will also increase the width of the arrow button and the scrollbar. Remember to reset the Button.commonGap value to 0 after the combo box creation, otherwise it will keep affecting all combo box and buttons created after.
  • You may append new items to your combo box using the method add(), which has three useful implementations:
    • add(Object item) Adds an object to the combo box. This method is very slow if used in loop; use the add(Object[] items) to add a bunch of objects instead.
    • add(Object[] items) Adds an array of objects to the combo box.
    • add(Object[] items, int startAt, int size) Adds a subarray of objects to the combo box.
    Avoid making several calls to add(Object item), always prefer add(Object[] items).
  • You may insert new items at a desired position using insert(Object item, int index).
  • The method setItemAt(int i, Object s) replaces the object at the given index starting from zero by the given object.
  • Use size() to retrieve the number of items available on the combo box.
  • The method getItemAt(int i) returns the object at the selected index; the reverse operation can be done with indexOf(Object name), which retrieves the index of a specific object.
  • Use getSelectedIndex() to get the index of the selected item, or getSelectedItem() to retrieve the selected object directly.
  • You may select an item by its index with setSelectedIndex(int i) or by its content with setSelectedItem(Object name). In the first, -1 must be used to blank the combo box view box. In the last one, if the name is not found, the currently selected item is not changed.
  • The selectLast() method selects the last item added to the combo box, doing a scroll if needed. It calls repaintNow().
  • To remove items from a combo box, you may use one of the following three methods:
    • remove(int itemIndex) Removes an object from the combo box by its index.
    • remove(Object item) Removes the given object from the combo box.
    • removeAll() Empties the combo box.
  • The combo box items can be sorted using the methods qsort(). One of them receives a parameter indicating if the sorting is caseless or not in case the elements are strings. In both of them, the current selection is cleared.
  • It is possible to make a list box scroll horizontally (see ListBox↓). To be able to scroll the list box associated with this combo box, just call the method enableHorizontalScroll(). Two buttons will appear below the vertical scrollbar. The add, replace, and remove operations will be a bit slower because the string’s width will have to be computed in order to correctly set the maximum horizontal scroll.
Sample code:
public void initUI()
{ 
	String []items1 = {"","Orange","Apple","Grape","Lemon"};
	String []items2 = {"One","Two","Three", ...};
	String []items3 = {"Disabled","Enabled"};
	
	ComboBox cb1 = new ComboBox(items1);
	add(cb1,LEFT,TOP+5);
​
	ComboBox cb2 = new ComboBox();
	cb2.add(items2);
	cb2.add("Twenty one");
	add(cb2,RIGHT,AFTER+5);
​
	ComboBox cb3 = new ComboBox(items3);
	add(cb3);
	cb3.setRect(LEFT,BOTTOM,PREFERRED+4,PREFERRED);
	cb3.setSelectedIndex(0);
	cb3.setEnabled(false);
}
As the preceding controls, this one also posts a ControlEvent.PRESSED when the user selects an item.
public void onEvent(Event event)
{
	switch (event.type)
	{
		case ControlEvent.PRESSED:
			if (event.target == cb1)
				new MessageBox("TotalCross", "Item selected: " +  cb1.getSelectedItem()).popup();
		break;
	}
}
For more information, check the totalcross.ui.ComboBox JavaDoc.

ListBox

The list box is a control that allows the user to select one item from a list contained within multiple line text box. It looks like an open combo box (in fact, the combo box uses the list box control). Consider using the list box instead of the combo box if the screen has enough space available to show all items.
Here are some screenshots from a simple list box using the Android style from the old UIControls sample (now inside the ui package from the TotalCrossAPI sample). Note that above the list box component, there is a combo box:
figure companion_resources/images/list_001.png figure companion_resources/images/list_002.png
figure companion_resources/images/list_003.png
Here is an example showing how it can be used:
public class MyProgram extends MainWindow 
{
	ListBox lb;
​
	public void initUI()
	{
		lb = new ListBox();
		add(lb);
		lb.add(new String[]{"Daniel","Jenny","Helge","Sandra"});
		lb.add("Marc"); // you may set the rect by using PREFERRED only after the items were added.
		lb.setRect(LEFT, TOP, PREFERRED, PREFERRED); // use control’s preferred width based on the size of the elements
	}
​
	public void onEvent(Event event)
	{
		switch (event.type)
		{
			case ControlEvent.PRESSED:
				if (event.target == lb)
					Object element = lb.getSelectedItem(); // in most cases, this is just a String and may be casted to such
		}
	}
} 
Please notice that TotalCross’ list box does not allow the selection of multiple items like one would expect. If you need this feature, use the MultiListBox control (refer to the advanced controls section).
  • List box has two constructors:
    • ListBox() Creates an empty list box.
    • ListBox(Object[] items) Creates a list box with the given items.
  • You can append new items to your list box using one of the following methods:
    • add(Object item) Adds an object to the list box.
    • add(Object[] moreItems) Adds an array of objects to the list box.
    • add(Object[] moreItems, int startAt, int size) Adds a subarray of objects to the list box.
    Avoid making several calls to add(Object item), always prefer add(Object[] items).
  • You may insert an object at a desired position with insert(Object item, int index). The first item has index 0.
  • The method setItemAt(int i, Object s) replaces the object at the given index by the given object.
  • Use size() to retrieve the number of items available on the list box.
  • The method getItemAt(int i) returns the object at the selected index. It returns an empty string if the index is outside the range. The reverse operation can be done with indexOf(Object name), which retrieves the index of a specific object. It returns -1 if the object is not found.
  • Use getSelectedIndex() to get the index of the selected item, or getSelectedItem() to retrieve the selected object directly. The first returns -1 if the list box has no selected index yet and the last one returns an empty String object if none is selected.
  • You may select an item by its index with setSelectedIndex(int i) or by its content with setSelectedItem(Object name). If the object is not found, the current selected item is not changed. However, if the index is not found, the current selected item is cleared and no item becomes selected.
  • The selectLast() method selects the last item added to the list box, doing a scroll if needed. It calls repaintNow().
  • To remove items from a list box, you may use one of the following three methods:
    • remove(int itemIndex) Removes an object from the list box by its index.
    • remove(Object item) Removes the given object from the list box.
    • removeAll() Empties the list box, setting all elements of the array to null so they can be garbage collected.
      Attention! If you used the same object array to initialize two list boxes (or combo boxes), this method will null both list boxes (because they use the same array reference), and you might get a NullPointerException!
  • The list box items can be sorted using the method qsort(). One of them receives a parameter indicating if the sorting is caseless or not in case the elements are strings. In both of them, the current selection is cleared.
  • To add a horizontal scroll to the list box, use the method enableHorizontalScroll(). Note that enabling this will make the addition of elements slower, because the list box will compute each item’s width to find the maximum offset.
  • To increase the default height of the horizontal scroll buttons, change the
    extraHorizScrollButtonHeight
    static member. Its default value is 2 in 160x160 or a multiple of it in other resolutions.
  • The setSimpleBorder(boolean simpleBorder) method is used to change the border style from 3d to simple if the flag passed is true.
  • If the list box owns the focus and you type a letter, the first item with the typed letter is selected. Typing again selects the next item, and so on.
  • The up/down keys can be used to scroll the list box.
  • The selection color can be changed with setCursorColor(int color). The color used in the setBackColor() method will be used in the scrollbar only. The background color of the control will be a lighter version of the given color.
  • By default, the selection is drawn using the current item’s width. This behavior can be changed by setting the static field useFullWidthOnSelection to true. If so, all list boxes will have the selection bar drawn in the full width.
  • You can extend ListBox to implement a customized type, like a color-chooser list box. If doing so, the following methods must be overridden:
    • protected void drawItem(Graphics g, int index, int dx, int dy)
    • protected int getItemWidth(int index)
    • protected void drawCursor(Graphics g, int sel, boolean on)
  • The clear() method sets the clearValueInt as the selected index.
  • The methods add() and remove() to add and remove a control, respectively, do nothing, because it does not make sense to add controls to or remove controls from a list box.
Sample code:
public void initUI()
{
	String[] items1 = { "", "Orange", "Apple", "Grape", "Lemon" };
	String[] items2 = { "One", "Two", "Three" };
	String[] items3 = { "Disabled", "Enabled" };
​
	ListBox lb1 = new ListBox(items1);
	add(lb1, LEFT, TOP + 5);
​
	ListBox lb2 = new ListBox();
	lb2.add(items2);
	lb2.add("Twenty one");
	add(lb2);
	lb2.setRect(RIGHT, AFTER + 5, PREFERRED, FILL - 5);
​
	ListBox lb3 = new ListBox(items3);
	add(lb3, RIGHT, TOP + 5);
	lb3.setEnabled(false);
}
Be careful when setting the items with an object array. The array is assigned as is, no copy is made. So, if you use the same array in another control and an item inside the array is changed, this change will reflect in both controls. However, if you add a new item, a new array is created to store the added item and the program works correctly. The best practice is to have one array for each list box control. To copy the contents of an object array to a new one, you can use totalcross.sys.Vm.arrayCopy(). If you’re dealing specifically with string arrays, you may instead use totalcross.sys.Convert.cloneStringArray().
The list box also posts the ControlEvent.PRESSED when an item is clicked.
public void onEvent(Event event)
{
	switch (event.type)
	{
		case ControlEvent.PRESSED:
			if (event.target == lb1)
				new MessageBox("TotalCross", "Item selected: " + lb1.getSelectedItem()).popup();
		break;
	}
}
For more information, check the totalcross.ui.ListBox JavaDoc.

MultiEdit

MultiEdit is basically an edit with support for multiple lines, so their usage is pretty similar and, unless noted otherwise, the same methods listed for edit are available for MultiEdit. A static vertical scrollbar is added, but it is disabled/enabled as needed.
Here are some screenshots from a non-editable MultiEdit using the Android style from the old UIGadgets sample being scrolled (below it there is a multi-lined label):
figure companion_resources/images/multi_001.png figure companion_resources/images/multi_002.png
figure companion_resources/images/multi_003.png figure companion_resources/images/multi_004.png
An editable MultiEdit from the old UIControls sample (now inside the ui package from the TotalCrossAPI sample), also using Android style, can be seen below:
figure companion_resources/images/multi_005.png figure companion_resources/images/multi_006.png
figure companion_resources/images/multi_007.png
Here is an example showing a MultiEdit control being used:
public class MyProgram extends MainWindow
{
	MultiEdit mEdit;
​
	public void initUI()
	{
		// the constructor method is called with the mask, the number of lines and the vertical interval in pixel between two lines
		add(mEdit,LEFT,TOP); // add/setRect must precede setText()
		mEdit.setText("What text you want"); // eventually
	}
} 
  • MultiEdit has three constructors:
    • MultiEdit() Creates a MultiEdit with 1 pixel as space between lines and with no lines. You must set the bounds using FILL or FIT.
    • MultiEdit(int rowCount, int spaceBetweenLines) Creates a
      MultiEdit
      with a vertical scrollbar, the given number of rows and the default width FILL. The gap is 1 by default and the control’s bounds must be specified with a setRect(). The space between lines may be 0.
    • MultiEdit(String mask, int rowCount, int spaceBetweenLines)
      Creates a MultiEdit with a vertical scrollbar, the given number of rows and uses the given mask to compute the PREFERRED width. If the mask is “”, the width is set to FILL. The gap is 1 by default and the control’s bounds must be given with a setRect(). The space between lines may be 0. Note that the mask does not masks the input. If the mask is "", the FILL width is choosen.
  • The method getNumberOfTextLines() returns the number of lines in the text.
  • With setScrollbarsAlwaysVisible(boolean asNeeded) you can change the
    MultiEdit
    ’s scrollbar behavior. Set to true to hide the vertical scrollbar when it isn’t needed (instead of disabling it). This must be done right after the constructor.
  • By default, a dotted line is shown under each text row. You can change this behavior with the boolean field drawDots.
  • Use the field gap to change the gap between the rows. The default value is 1.
  • If the MultiEdit object is not editable, the user can scroll the edit a page at a time just by clicking in the middle upper or middle lower.
  • The following methods allow you to manipulate the MultiEdit scrollbar:
    • scrollToBottom() Moves the scroll to the bottom.
    • scrollToLine(int line) Scrolls to the gives text line.
    • scrollToTop() Moves the scroll to the top.
Sample code:
public void initUI()
{
	String s = "James Robert Baker (1946-1997) was an American author of sharply satirical, predominantly gay-themed transgressional fiction. A native Californian, his work is set almost entirely in Southern California. After graduating from UCLA, he began his career as a screenwriter, but became disillusioned and started writing novels instead. Though he garnered fame for his books Fuel-Injected Dreams and Boy Wonder, after the controversy surrounding publication of his novel, Tim And Pete, he faced increasing difficulty having his work published. According to his life partner, this was a contributing factor in his suicide. Baker’s work has achieved cult status in the years since his death, and two additional novels have been posthumously published. First-edition copies of his earlier works have become collector’s items. One of his novels was filmed (though it was not a financial success) and two others have been optioned for the movies, though they have not been produced.";
​
	MultiEdit mEdit;
	mEdit = new MultiEdit("",6,1);
	mEdit.drawDots = (false);
	mEdit.justify = true;
	mEdit.setEditable(false);
	mEdit.hasCursorWhenNotEditable = false;
	add(mEdit,LEFT,AFTER+2);
	mEdit.setText(s); //eventually
	mEdit.requestFocus();
}
For more information, check the totalcross.ui.MultiEdit JavaDoc.

Grid

The grid control is used to display tabulated data, which is represented as a string matrix (each row is a string array). Optionally, you may add an extra column of check boxes on the left side of the grid.
Below are some examples of grid taken from the old GridTest sample (now inside the ui package from the TotalCrossAPI sample) using Android style.
The first two images are from a simple grid being selected:
figure companion_resources/images/grid_001.png figure companion_resources/images/grid_002.png
The next samples show a grid with a check in the first column. Note that it is possible to know which ones are checked:
\strikeout off\uuline off\uwave off
figure companion_resources/images/grid_003.png figure companion_resources/images/grid_004.png
figure companion_resources/images/grid_005.png figure companion_resources/images/grid_006.png
The next sample shows a grid with a combo box in the first column and an edit in the second one:
figure companion_resources/images/grid_007.png figure companion_resources/images/grid_008.png
figure companion_resources/images/grid_009.png
The next case shows the columns being sorted one at a time:
figure companion_resources/images/grid_011.png figure companion_resources/images/grid_012.png
figure companion_resources/images/grid_013.png figure companion_resources/images/grid_014.png
The next sample shows a grid with also a check and a combo box, but this time, it can be seen that the combo box and check can’t be manipulated by the user in all rows:
figure companion_resources/images/grid_015.png figure companion_resources/images/grid_016.png
figure companion_resources/images/grid_017.png
Finally, there is a grid whose first column has an image:
figure companion_resources/images/grid_010.png
Although the TotalCross’ grid control usage is pretty simple and straightforward, it is the most customizable control and it uses its own set of events defined in GridEvents (unlike the almost all the other controls that use only ControlEvents). That means you’ll be able to start using grids within a couple of minutes, but it will take some time to learn to use all its features:
  • Horizontal scrolling in case the columns widths are greater than the screen width.
  • Vertical scrollbar to scroll up and down the information on the grid.
  • An easy to use interface for adding/removing information to the grid.
  • Optional check column which is a column that is clickable, marking an specific row as checked/ unchecked; this is usefull if you want the user to be able to select multiple rows displayed on the grid.
  • Columns can be resized so that the user can see all the information displayed in a given column.
  • Style configuration, which you can set the color of captions boxes all the way through the stripes colors and vertical line types.
  • The GridEvent class contains the events generated by the grid.
  • A column may be marked as editable. In this case, an edit will be placed in the currently focused cell.
  • A column can also have choices (combo box).
  • The columns can be sorted by clicking on the title.
  • If the text is bigger than the size of the column, you can click and hold in the cell to display a tooltip with the full text.
  • You can customize cell back and fore colors, enabled state and choices by using the
    CellController
    class.
  • Clicking on the column’s caption will sort in ascending order; clicking on it again sorts in descending order.
  • It is possible to assign a DataSource to a Grid, making the elements be fetched on demand.
  • A column with size 0 is not displayed, so you can use it to store in the grid important information, for example the rowid of a table.
  • A GridEvent is dispatched in the following situations:
    • When the user selects a row:
      type = SELECTED_EVENT; row and col are set with the cell that was clicked.
    • When the user checks a row:
      type = CHECK_CHANGED_EVENT; row is set with the row that was checked, col is 0, and checked contains the current state.
    • When the user press the check/uncheck all box:
      type = CHECK_CHANGED_EVENT; col is 0 and row is set to Grid.ALL_CHECKED and Grid.ALL_UNCHECKED.
    • When the user changes the text of an editable column or selected a new choice:
      type = TEXT_CHANGED_EVENT; row and col are set with the cell that changed. The new text can be found with getCellText() whereas Grid.oldCellText() contains the old text.
  • Grid has two constructors:
    • Grid(String[] captions, boolean checkEnabled) Creates a grid with the given captions and, optionally, a multi-selection check column so that the user can select multiple rows of the grid. The captions can’t be null. The widths will be computed as the width of the grid captions and the alignment will be all LEFT.
    • Grid(String[] captions, int[] widths, int[] aligns,
      boolean checkEnabled)
      Creates a grid with the given captions, column width, column text alignment and, optionally, a multi-selection check column.
      If the total width is less than the grid’s width, the last column will fill until the grid width. If null, the caption widths will be computed and used as the row width. If a negative value is passed, it will be computed as a percentage against the grid’s width.
      aligns is the alignment of information on the given column. If null, all aligns will be LEFT.
Here´s an example which will create a grid with the given captions, column widths, information alignment, and a check column:
Rect r = getClientRect();
String []gridCaptions = {" WPT "," HDG "," DST "," ETE "," FUEL "};
int gridWidths[] = 
{
	-25, // negative numbers are percentage of width
	fm.stringWidth(" 000 "),
	-25,
	-20,
	-20
};
int gridAligns[] = { LEFT, CENTER, RIGHT, CENTER, RIGHT };
grid = new Grid(gridCaptions, gridWidths, gridAligns, false);
add(grid, LEFT+3,TOP+3,r.width/2,r.height/2);
grid.secondStripeColor = Color.getRGB(235,235,235);
String[][] data = new String[][]
{
	{"0AAAA","000","000.0","00:00","00.0"},
	{"1BBBB","111","111.1","11:11","11.1"},
	{"3DDDD","333","333.3","33:33","33.3"},
	{"4EEEE","444","444.4","44:44","44.4"},
	{"5FFFF","555","555.5","55:55","55.5"}
};
grid.setItems(data);  
Also consider using ListContainer instead of Grid if you’re using devices with big screens.
Another example shows the correct way of increasing the size of a grid in order to support rotation when there is a component below it which must be shown or hidden.
public class TCTestWin extends MainWindow
{
	Grid g;
	Button b1,b2;
​
	public void initUI()
	{
		add(b1 = new Button("Hide"),LEFT,TOP);
		add(b2 = new Button("Place hoder"),RIGHT,BOTTOM);
		add(g = new Grid(new String[]{"Caption"},null,null,false));
		setGridRect();
	}
​
	private void setGridRect()
	{
		g.resetSetPositions(); // let the new coordinates work during rotation
		g.setRect(LEFT,AFTER,FILL,b2.isVisible()? FILL-b2.getHeight() :  FILL,b1);
	}
​
	public void onEvent(Event e)
	{
		if (e.type == ControlEvent.PRESSED && e.target == b1)
		{
			b2.setVisible(!b2.isVisible());
			setGridRect();
		}
	}
} 

Some useful methods

  • add(String[] item) Adds a new item to the grid. It’s up to the user to call Window.
    needsPaint = true
    afterwards. This method does not work if there’s a DataSource assigned.
  • add(String[] item, int row) The same as above but adds a new item to the grid at the given position.
  • add(String[][] items) Appends the given rows at the end of the grid. It is similar to the first add() method except that it appends more than one row.
  • clear() Selects the clearValueInt row, which defaults to -1. That is, by default, this method deselects all rows.
  • getCellText(int row, int col) Returns the text of the given cell. If the grid has a check column, col must start from 1.
  • setCellText(int row, int col, Strint text) Sets the text of the given cell. If the grid has a check column, col must start from 1.
  • getItem(int index) Returns the row from the given index. This method works with or without a DataSource.
  • getItemsVector() Returns a vector containing all information inside the grid. As with getItem(), the returned data does not contain information about the selected item’s. Note that if a DataSource is assigned, this method returns null.
  • getSelectedIndex() Gets the number of the currently selected row, or -1 if none is selected.
  • getSelectedItem() Gets the string array for the currently selected row. Any changes made into the returned array is applied back to the grid automatically (because it holds the grid’s data).
  • horizontalScroll(boolean toLeft) Scrolls the grid to left or right. It returns true if the grid was scrolled; false if it’s impossible to scroll in that direction.
  • isChecked(int lineIndex) Returns if the given row is checked.
  • setChecked(int row, boolean check) Checks or unchecks a given row. This method only works with a grid that has checks and repaint() must be called.
  • replace(String[] item, int row) Replaces the given row by the text. This method does not work if there’s a DataSource assigned.
  • move(int row, boolean up) Moves the items at given indexes one row up or down depending on the given second parameter. If it is true, the items at index row will be shown in row-1 and the items at index row-1 will be shown at index row. If it is false, the items at index row will be shown in row+1 and the items at index row+1 will be shown at index row. It does not make sense to use this method if there’s a DataSource assigned.
  • del(int row) Removes the given row from the grid. This method does not work if there’s a DataSource assigned.
  • setSelectedRow(int line) Selects the given row number. If -1, unselects the grid.
  • setItems(String[][] items) Sets the grid items to be displayed. Notice that it needs to conform to the numbers of columns that the grid currently have. This method removes any assigned DataSource. The strings inside the matrix cannot be null. If items == null, the grid will remain empty.
  • size() Returns the number of rows in this grid.
  • setColumnEditable(int col, boolean editable) Enables/disables the edition for all cells of a grid’s column. Note that setting an editable column as not editable will remove all its formats. Returns the previous edit or the new one, so you can easily change its formats. Important: this must be called AFTER the grid has set its bounds.
  • isColumnEditable(int col) Returns true if the given column is editable.
  • setColumnChoices(int col, String[] choices) Enables a combo box with choices for all cells of a grid’s column. The given choice array is used to create a pop list that is shown when the user clicks on the column. Note that calling this method removes any edit assigned to this column. You can change dynamically the choices by extending the CellController class. Passing a null value removes any combo box assigned to the column.
  • moveFocusToNextControl(Control c, boolean forward) Traverses throught the edits of the grid. It returns the selected control or null if none was found.
  • removeAll() This method does nothing. To remove all elements, removeAllElements() should be used instead.
  • removeAllElements() Remove all elements from the grid, leaving it blank. Removes any assigned DataSource.
  • setDataSource(DataSource ds, int nrItems) Passing the class that implements the Grid.DataSource interface and the total number of items allows the grid to request data on demand. This greatly reduces the memory usage, because only the number of visible items may be loaded at each time, but makes the grid navigation slower. Note that in this mode the grid scroll will not be realtime: the data will be requested only when the user releases the scroll bar. The data source has a single method: String[][] getItems(int startingRow, int count), that must return the given number of items or less.
    Note that when using DataSources, the add(), remove(), insert(), etc methods CANNOT BE USED. A DataSource is mostly used to assign a ResultSet to it, so it’s nonsense any data modification. Note that the scroll is made not live scrolling to speedup data retrieval.
    Here’s a sample of a getItems() implementation:
    public String[][] getItems(int startIndex, int count)
    {
    	if (activeRS != null)
    	{
    		activeRS.absolute(startIndex);
    		activeRS.getStrings(count);
    	}
    	return null;
    }
    
  • getDataSource() Returns the DataSource assigned for this grid, or null if there is none.
  • setCellController(Grid.CellController cc) Passing the class that extends Grid.
    CellController
    will allow you to have a fine control of each cell of the grid. Note that using a cell controller will make the drawings slower. The class has the following methods:
    • getBackColor(int row, int col) Must return the background color for the given cell, or -1 if you want to use the default one. Note that if the grid has a check, the back color of the check cell will be requested passing col value as -1.
    • getForeColor(int row, int col) Must return the foreground color for the given cell, or -1 if you want to use the default one.
    • getFont(int row, int col) Must return the font to be used in this cell, or null to use the default control’s font.
    • isEnabled(int row, int col) Must return if the given cell is enabled, i. e., can have input and can be selected. Regarding the input, it will only work if
      setColumnChoices() or setEditable() was called to the given column. Regarding the selection, it may also disable column’s check state change.
    • String[] getChoices(int row, int col) Returns a customized set of choices for the given cell. Must return null if you want to use the default set. Only works if setColumnChoices() was called to the given column.
Note that the setColumnEditable() and setColumnChoices() methods are mutually exclusive: calling one cancels the effects of the other. The setDataSource() and setItems() methods are also mutually exclusive.

Some useful attributes

  • boldCheck Draws a bold check instead of the tiny one. Useful for high resolution devices.
  • canClickSelectAll Sets to false to disable the click on the check column of the captions to select and unselect all checks.
  • disableSort Disables the sort when clicking in the column’s caption.
  • drawCheckBox Boolean that defines if the box around the check will or not be drawn.
  • enableColumnResize Must be set to false to disable the column resize by dragging the column separator line.
  • extraHorizScrollButtonHeight A value that can increase the horizontal scroll buttons height. Defaults 2 in 160x160 or a multiple of it in other resolutions.
  • oldCellText The text that was in the cell before the user had edited it.
  • checkColor The color used to draw the check. Defaults to black.
  • captionsBackColor The color used to fill the captions area. BRIGHT by default.
  • highlightColor The Color of the selected (highlighted) row. DARK by default.
  • firstStripeColor Color of the first stripe. WHITE by default.
  • secondStripeColor Color of the second stripe. drawStripes must also be set to true. BRIGHT by default.
  • verticalLineStyle Defines the vertical line style of the grid. Possible values are VERT_DOT (default), VERT_LINE, and VERT_NONE.
  • captions These are the column captions. Can be directly assigned, but always make sure it has the same number of columns of the grid, or the widths array, set with the setWidths() method.
Another sample:
public void initUI()
{
	String []gridCaptions = {"Caption 1", "Caption 2", "Caption 3", "Caption 4", "Caption 5", "Caption 6", "Caption 7" };
​
	int ww = fm.getTextWidth("xxxxxxxxx"); 
	int gridWidths[] = {ww, ww, ww, ww, ww, ww, ww};
	int gridAligns[] = { LEFT,LEFT,LEFT,LEFT,LEFT,LEFT,LEFT}; 
	grid = new Grid( gridCaptions, gridWidths, gridAligns, false );
	grid.firstStripeColor = Color.GREEN;
	grid.secondStripeColor = Color.YELLOW;
	grid.verticalLineStyle = Grid.VERT_NONE;
	add(grid);
​
	grid.setRect(LEFT, AFTER+2, FILL, FIT, btnRemove);
​
	String items[][] = new String[4][7]; 
	for(int i = 0; i < 4; i++)
		for(int j = 0; j < 7; j++)
			items[i][j] = "BRAZIL " + j;
	grid.setItems(items); 
}
For more information, check the totalcross.ui.Grid JavaDoc.

ToolTip

It allows the display of a tooltip when user keeps the pen in the control. On Windows and Linux desktop, the tooltip is also shown when the mouse stays over a control. The default popup delay (millisDelay) is 1000 ms and the amount of time it will be displayed (millisDisplay) is 2000 ms (a pen up also hides the control). The tooltip must always be added after the control is added and had its bounds set; otherwise, you must call the added method to initialize the tip’s bounds.
You can set the border color by changing the borderColor field (no border by default) and also the internal gap (the field insideGap). By default, it is -1 and no border is shown.
The static members distX and distY lets you set the default distance from the control for all tooltips. They must be set before the tooltip is created and are 0 by default.
The constructor receives the control which the tooptip refers to and the message that will be displayed. You can use the new line character \n like a label control. Example:
ToolTip t = new ToolTip(control, "Hi!\nIt’s Me"); 
A ControlEvent.PRESSED event will be dispatched to the attached control right before the text is shown. You can then set the tip to a new value using setText(); setting to an empty string will disable the tooltip at that moment. Calling setControlRect() also changes the rectangle around which the tooltip will be displayed. By default, it’s used the absolute rectangle of the control passed in the constructor. The placement of the tooltip will be defined based on it, in a way that the control is not obscured.
ToolTip implements the PenListener interface.
Examples:
ToolTip.distX = 10; // 0 by default
ToolTip.distY = 4; // 0 by default
ToolTip.insideGap = 8; // 4 by default 
Button b; 
add(b = new Button("Hello Tooltip!"),CENTER,BOTTOM); 
​
ToolTip t = new ToolTip(b, "Hi, this is a button");
t.borderColor = 0x00FF00; // -1 (none) by default 
t.millisDelay = 500; // 1000 by default t.millisDisplay = 4000; // 2000 by default
t.setBackColor(Color.getRGB(250,0,0)); // same as control’s container by default 
private void addToolTip(Control c, String text)
{
	ToolTip t = new ToolTip(c,text);
	t.millisDelay = 500;
	t.millisDisplay = 5000;
​
	if (Settings.isColor)
	{
		t.borderColor = Color.BLACK;
		t.setBackColor(Color.getRGB(250,250,0));
	}
}
​
public void initUI()
{
	add(btnChooseColor = new Button("Choose new background color"), LEFT, TOP+2);
	addToolTip(btnChooseColor, "Click this button to open a\nChoiceDialog that contains a\nColorList control from where\nyou can choose a back new color");
} 
For more information, check the totalcross.ui.ToolTip JavaDoc.

20 Some Advanced Controls

In this chapter, some advanced controls will be described in detail. They are organized in alphabetical order.

AlignedLabelsContainer

This class is a Container used to align all controls to the maximum width of a set of labels. You can define the label alignment. The controls that you add to this container are placed at the right of the labels.
Here’s a sample of how to use it:
String[] labels =
{
	"Name",
	"Born date",
	"Telephone",
	"Address",
	"City",
	"Country", 
	""
};
AlignedLabelsContainer c = new AlignedLabelsContainer(labels);
c.setBorderStyle(BORDER_LOWERED);
c.labelAlign = RIGHT;
c.foreColors = new int[]{Color.RED,Color.BLACK,Color.BLACK,Color.BLACK,Color.BLACK,Color.BLACK,Color.BLACK,};
c.setInsets(2,2,2,2);
c.setFont(font.asBold()); // labels are bold
c.childrenFont = font; // but controls are normal
add(c,LEFT+2,TOP+2,FILL-2,PREFERRED+4);
for (int i =0; i < labels.length-2; i++)
	c.add(new Edit(),LEFT+2,AFTER+(i==0?2:0));
c.add(new ComboBox(new String[]{"Brazil","France"}),LEFT+2,AFTER);
c.add(new Button("Insert data"),RIGHT,SAME);
c.add(new Button("Clear data"),RIGHT,AFTER,SAME,PREFERRED); 
The code above generates a screen similar to the shown below taken from the old UIGadgets sample using Android style:
figure companion_resources/images/align_001.png
It is possible to do this alignment by hand. However, it is much easier using this control.
For more detais, take a look at totalcross.ui.AlignedLabelsContainer JavaDoc.

AnimatedButton

An animated button control. It extends Animation, the class described in the next section.
This control displays an animated button which can take S different states and each state is fade in or out in F frames. S and F represent the two first constructor arguments. The frames of this special animation have to be ordered to be supported by this class. The states are numbered from 0 to S-1 and the frames order is the following depending on the layout type value:
  • FADE_OUT_LAYOUT: S0F0,S0F1,S0F2,S1F0,S1F1,S1F2,S2F0,S2F1,S2F2
  • FADE_IN_LAYOUT: S0F2,S0F1,S0F0,S1F2,S1F1,S1F0,S2F2,S2F1,S2F0
  • FADE_OUT_IN_LAYOUT: S0F0,S0F1,S1F1,S1F0,S1F1,S2F1,S2F0,S2F1,S0F1
where S stands for state, F for frame and where S0F0, S1F0, and S2F0 are the full states and the others are transition frames.
Here is an example of how to use this class:
import totalcross.game.AnimatedButton;
import totalcross.io.IOException;
import totalcross.sys.Settings;
import totalcross.ui.*;
import totalcross.ui.image.*;
​
public class Teste extends MainWindow
{
	public Teste()
	{
		super("Teste", HORIZONTAL_GRADIENT);
		setUIStyle(Settings.Android);
	}
​
	AnimatedButton btn;
​
	public void initUI()
	{
		try
		{
			btn = new AnimatedButton(new Image("Ping_onOff.png"), 2,6,AnimatedButton.FADE_OUT_LAYOUT,-1,50);
			add(btn, CENTER, CENTER);
		}
		catch (ImageException e) {}
		catch (IOException e) {}
	}
}
where Ping_onOff.png is the following image:
figure companion_resources/images/Ping_onOff.png
which results in the following screenshots (the button starts an animation when it’s clicked on):
figure companion_resources/images/animatedbutton_001.png figure companion_resources/images/animatedbutton_002.png
figure companion_resources/images/animatedbutton_003.png figure companion_resources/images/animatedbutton_004.png
figure companion_resources/images/animatedbutton_005.png figure companion_resources/images/animatedbutton_006.png
figure companion_resources/images/animatedbutton_007.png figure companion_resources/images/animatedbutton_008.png
figure companion_resources/images/animatedbutton_009.png figure companion_resources/images/animatedbutton_010.png
figure companion_resources/images/animatedbutton_011.png figure companion_resources/images/animatedbutton_012.png
For more detais, take a look at totalcross.game.AnimatedButton JavaDoc.

Animation

This control displays an animation that can be loaded from indexed PNG files (one frame per image), a multi-framed PNG (this kind of PNG file contains a list of images having all the same size and that lay side by side), or a GIF. Some examples taken from the game Scape and GifAnimatedTest are shown below, where the first one uses multi-framed PNGs and the second one uses GIFs (the PNG images are below the sample windows which use them):
figure companion_resources/images/animation_001.png figure companion_resources/images/animation_002.png
figure companion_resources/images/hockey.png figure companion_resources/images/ball.png
figure companion_resources/images/animation_003.png figure companion_resources/images/animation_004.png
For more detais, take a look at totalcross.game.Animation JavaDoc.

ArrowButton

A class used to display a Button with an arrow inside. The following code shows the window below it:
import totalcross.sys.Settings;
import totalcross.ui.*;
import totalcross.ui.gfx.*;
​
public class Teste extends MainWindow
{
	public Teste()
	{
		super("Teste", HORIZONTAL_GRADIENT);
		setUIStyle(Settings.Android);
	}
​
	public void initUI()
	{
		ArrowButton b1 = new ArrowButton(Graphics.ARROW_UP, 10, Color.BLACK);
		add(b1, LEFT, TOP);
		ArrowButton b2 = new ArrowButton(Graphics.ARROW_DOWN, 20, Color.BLUE);
		add(b2, AFTER, SAME);
		ArrowButton b3 = new ArrowButton(Graphics.ARROW_LEFT, 30, Color.CYAN);
		add(b3, LEFT, AFTER);
		ArrowButton b4 = new ArrowButton(Graphics.ARROW_RIGHT, 40, Color.GREEN);
		add(b4, AFTER, SAME);
	}
}
figure companion_resources/images/arrowbutton_001.png
In this class, setArrowSize(int kk) Changes the arrow size of the button. Note that the button size won’t be changed. Some examples using b4 of the above picture:
b4.setArrowSize(20); b4.setArrowSize(80);
figure companion_resources/images/arrowbutton_002.png figure companion_resources/images/arrowbutton_003.png
toString() shows a string representation of the object and its direction. For example, the code below
Vm.debug(b1.toString());
Vm.debug(b2.toString());
Vm.debug(b3.toString());
Vm.debug(b4.toString());
prints the following on the console:
totalcross.ui.ArrowButton@131f71a, dir: 1 
totalcross.ui.ArrowButton@15601ea, dir: 2 
totalcross.ui.ArrowButton@197d257, dir: 3 
totalcross.ui.ArrowButton@7259da, dir: 4
For more detais, take a look at totalcross.game.ArrowButton JavaDoc.

Bar

Bar is a class that provides a title area and a button area (at right). The title and the button are optional, although it doesn’t make sense to have a bar without title and buttons. You can add or remove buttons, and change the title text; the title text can have an icon at left.
When a button is pressed, a ControlEvent.PRESSED is sent to the caller, and the button index can be retrieved using the getSelectedIndex() method. By default, the background is shaded (BACKGROUND_SHADED). You can change it to plain using
backgroundStyle = BACKGROUND_SOLID;
Here’s an example of how to use it:
Font f = Font.getFont(true,Font.NORMAL_SIZE+2);
Bar h1 = new Bar("fakeboot");
h1.canSelectTitle = true;
h1.setFont(f);
h1.setBackForeColors(0x0A246A,Color.WHITE);
h1.addButton(new Image("ic_dialog_alert.png"));
h1.addButton(new Image("ic_dialog_info.png"));
add(h1, LEFT,0,FILL,PREFERRED); // use 0 instead of TOP to overwrite the default menu area 
which results in the following images using Android style, where the bottom images show the title and one of the buttons being pressed:
figure companion_resources/images/bar_001.png
figure companion_resources/images/bar_002.png figure companion_resources/images/bar_003.png
Note that the top and bottom components of the the old UIControls sample (now inside the ui package from the TotalCrossAPI sample) are bars.
You can also add a spinner to the bar and manipulate it using the following methods:
  • createSpinner(int color) Creates a spinner with the given color. The spinner will be placed at the right of the title (only works if there’s a title). It must be created before the bar is added to a container.
  • startSpinner() Shows and starts the spinner (if one has been assigned to the spinner field). If there is no spinner, a NullPointerException will be thrown.
    Changing the code of the example given above after line 7 results in:
    h1.createSpinner(Color.WHITE);
    add(h1, LEFT,0,FILL,PREFERRED); // use 0 instead of TOP to overwrite the default menu area
    h1.startSpinner();
    
figure companion_resources/images/bar_004.png
  • stopSpinner() Stops and hides the spinner (if one has been assigned to the spinner field). If there is no spinner, a NullPointerException will be thrown.
For more detais, take a look at totalcross.ui.Bar JavaDoc.

ButtonMenu

This class adds a multi-button menu that can be scrolled horizontally (single-row) or vertically (multiple-rows), using a scroll bar or flicking. The buttons can have almost all properties present in the Button class, like (the ButtonMenu fields listed below similar to the ones in the class Button are ommited from the fields description at the end of this section):
  • borderType (Button.setBorder())
  • cornerRadius3DG (Button.cornerRadius3DG)
  • borderWidth3DG (Button.borderWidth3DG)
  • borderColor3DG (Button.borderColor3DG)
  • topColor3DG (topColor3DG)
  • bottomColor3DG (bottomColor3DG)
  • pressedColor (Button.setPressedColor())
There are also other properties that can be set, like:
  • textGap
  • buttonVertGap
  • buttonHorizGap
  • imageSize
  • borderGap
The sizes above are not in pixels, but in percentage of the font’s height. So, a value of 25 means 25% of the font’s height, or 1/4; 150 means 150% of the font’s height, or 1.5x; and so on. This enabled the gaps be constant in physical inches no matter the screen DPI or resolution.
The UIContols program has good samples on how to use this class. Some images are shown below:
figure companion_resources/images/buttonmenu_001.png
figure companion_resources/images/buttonmenu_002.png figure companion_resources/images/buttonmenu_003.png
figure companion_resources/images/buttonmenu_004.png figure companion_resources/images/buttonmenu_005.png
For more detais, take a look at totalcross.ui.ButtonMenu JavaDoc.

ComboBoxEditable

This control is a ComboBox and is usually used as an edit that holds old typed values. When the user types a word, it is automatically selected in the combo box. Here’s a sample of how to use it similar to the one used to be found in the old UIGadgets samples:
String[] items = 
{
	"Ana",
	"Barbara",
	"Raul",
	"Marcelo",
	"Eduardo",
	"Denise",
	"Michelle",
	"Guilherme",
	"Vera",
	"Dulce",
	"Leonardo",
	"Andre",
	"Gustavo",
	"Anne",
	"Renato",
	"Zelia",
	"Helio"
};    
ComboBoxEditable cbe = new ComboBoxEditable(items);
cbe.qsort();
add(cbe, LEFT,BOTTOM-100);  
The next images show the result of this sample and some screenshots from the old sample:
figure companion_resources/images/comboeditable_001.png figure companion_resources/images/comboeditable_002.png
figure companion_resources/images/comboeditable_003.png figure companion_resources/images/comboeditable_004.png
figure companion_resources/images/comboeditable_005.png
For more detais, take a look at totalcross.ui.ComboBoxEditable JavaDoc.

ColorList

Implements a ListBox where colors can be choosen from. The only functional methods are setColors(), getSelectedItem() and getSelectedColor(). Next an example of how to use this class as a combo box color chooser:
add(foreCombo = new ComboBox(new ColorList()), CENTER, BOTTOM); 
which results in the following windows:
figure companion_resources/images/colorlist_001.png figure companion_resources/images/colorlist_002.png
figure companion_resources/images/colorlist_003.png figure companion_resources/images/colorlist_004.png
For more detais, take a look at totalcross.ui.ColorList JavaDoc.

Document

Represents an HTML Document. To change the font size, you must change Style.
defaultFontSize
. To change the default colors, change UIColors.htmlXXX fields, and don’t forget to call htmlContainer.setBackForeColors() with these colors.
To see it being used, see the sample HtmlBrowser. Some screen shots of it are in the section HtmlContainer↓.
For more detais, take a look at totalcross.ui.html.Document JavaDoc.

FlowContainer

This class is a container that will place controls one after another and, once the width has been reached, it wraps to the next line. All controls must be added before calling setRect() or add(). When calling setRect() or add() for this control, the height must be PREFERRED (with adjustments, if needed). Also, if initUI() is overriden, be sure to call super.initUI().
Code example:
Settings.uiAdjustmentsBasedOnFontHeight = true;
Label l = new Label("Do you agree that TotalCross is a great development platform?");
l.autoSplit = true;
add(l, LEFT,AFTER,FILL,PREFERRED);
​
FlowContainer fc = new FlowContainer(50,25);
fc.add(new Radio("Probably Yes"));
fc.add(new Radio("Probably No"));
fc.add(new Radio("Maybe"));
fc.add(new Radio("I don’t know"));
add(fc, LEFT,AFTER,FILL,PREFERRED); 
which results in:
figure companion_resources/images/flow_001.png
For more detais, take a look at totalcross.ui.FlowContainer JavaDoc.

HtmlContainer

HtmlContainer renders an HTML Page.
Note that the form controls back and fore colors are defined by UIColors.
htmlContainerControlsFore
and UIColors.htmlContainerControlsBack.
When a link is clicked, a PRESSED event is thrown, with this HtmlContainer as target. The link can then be retrieved with the pressedLink property.
Some images taken from the old sample HtmlBrowser, which uses this control, are shown here, where the application interface style was changed to Android:
figure companion_resources/images/html_001.png figure companion_resources/images/html_002.png
figure companion_resources/images/html_003.png figure companion_resources/images/html_004.png
figure companion_resources/images/html_005.png figure companion_resources/images/html_006.png
figure companion_resources/images/html_007.png figure companion_resources/images/html_008.png
For more detais, take a look at totalcross.ui.html.HtmlContainer JavaDoc.

ImageControl

A control that can show an image bigger than its area and that can be dragged using a pen to show the hidden parts. Note that, by default, events (and dragging) are disabled. You must call setEventsEnabled() to allow dragging.
This is used in the Litebase sample PhotoDB, where it is possible to drag the picture to show other parts of it.
For more detais, take a look at totalcross.ui.ImageControl JavaDoc.

ImageList

Implements a ListBox where the items are images. If you don’t add at least one image before calling add()/setRect(), you must compute the preferred size yourself.
Next an example of how to use this class as a combo box color chooser:
import totalcross.io.IOException;
import totalcross.sys.Settings;
import totalcross.ui.*;
import totalcross.ui.image.*;
​
public class Teste extends MainWindow
{
	public Teste()
	{
		super("Teste", HORIZONTAL_GRADIENT);
		setUIStyle(Settings.Android);
	}
	public void initUI()
	{
		ImageList list = new ImageList();
		try
		{
			list.add(new Image("black.png"));
			list.add(new Image("blue.png"));
			list.add(new Image("green.png"));
			list.add(new Image("red.png"));
			list.add(new Image("white.png"));
		}
		catch (ImageException e) {}
		catch (IOException e) {}
		add(new ComboBox(list), CENTER, BOTTOM);
	}
}
Using 32x32 images, the result of the above code is the following:
figure companion_resources/images/imagelist_002.png figure companion_resources/images/imagelist_001.png
For more detais, take a look at totalcross.ui.ImageList JavaDoc.

ListContainer

ListContainer is a ListBox where each item is a Container.
The correct way to create a ListContainer item is by subclassing a Container and adding the controls in the initUI() method. Adding directly using getContainer().add() will not work.
Below is an example of how to use it, taken from the old UIGadgets sample using the Android user interface style:
class LCItem extends ScrollContainer
{
	Label lDate,lPrice,lDesc;
	Check chPaid;
​
	public LCItem()
	{
		super(false); // VERY IMPORTANT (a RuntimeException will be thrown if this is not used because it can’t have a scroll bar).       
	}
	
	public void initUI()
	{
		add(chPaid = new Check("Paid"),LEFT,TOP);
		add(lDate = new Label("99/99/9999"),RIGHT,TOP);
		add(new Label("US$"),LEFT,AFTER);
		add(lPrice = new Label("999.999.99"),AFTER,SAME);
		add(lDesc = new Label("",RIGHT),AFTER+10,SAME);
		lDesc.setText("description");
	}
}
​
private void testListContainer()
{
	ListContainer lc;
	add(lc = new ListContainer(),LEFT,TOP,FILL,FILL);
	for (int i =0; i < 10; i++)
		lc.addContainer(new LCItem());
} 
The resulting window from it looks like the screenshots below:
figure companion_resources/images/listcontainer_001.png figure companion_resources/images/listcontainer_002.png
From the old UIControls sample (now inside the ui package from the TotalCrossAPI sample), the following screenshots were taken:
figure companion_resources/images/listcontainer_003.png figure companion_resources/images/listcontainer_004.png
For more detais, take a look at totalcross.ui.ListContainer JavaDoc.

MenuBar

Constructs a menu with the given items. A menu can be opened by the user in a couple of ways:
  • By clicking on the menu button on the devices.
  • By clicking on the title of a window.
  • By holding during 1 second the middle button of the 5-way navigation buttons.
The menu supports disabled and checked items. The menu can be closed by a click on a valid item or clicking outside of its bounds. A PRESSED event will be thrown when the menu is closed and a menu item was selected. To discover which item was selected, see the method getSelectedIndex(), which returns -1 if none, or the matrix index otherwise.
Note that the separator dotted line doesn’t generate events and can’t be selected.
After changing the isChecked and isEnabled states, there’s no need to call repaint(), because they will show up only the next time the menu bar opens.
Here is an example of a menu bar taken from the old UIGadgets sample:
MenuItem col0[] =
{
	new MenuItem("File"),
	new MenuItem("Minimize"),
	new MenuItem("Exit"),
	new MenuItem(),
	miShowKeys = new MenuItem("Show key codes", false)
};       
String p = Settings.platform;
col0[1].isEnabled = p.equals(Settings.JAVA) || p.equals(Settings.ANDROID) || Settings.isWindowsDevice() || p.equals(Settings.WIN32);
​
MenuItem col1[] =
{
	new MenuItem("UIStyle"),
	new MenuItem("Flat"),
	new MenuItem("Vista"),
	new MenuItem("Android"),
	new MenuItem(),
	miPenless = new MenuItem("Penless device",false),
	miGeoFocus= new MenuItem("Geographical focus",false),
	new MenuItem(),
	miUnmovableSIP = new MenuItem("Unmovable SIP",false)
};
​
MenuItem col2[] =
{
	new MenuItem("Tests1"),
	new MenuItem("Standard controls"),
	new MenuItem("TabbedContainer with images"),
	new MenuItem("Masked Edit"),
	new MenuItem("Image and text buttons"),
	new MenuItem("Scaled Image button"),
	new MenuItem("Justified MultiEdit and Label")
};
​
MenuItem col3[] =
{
	new MenuItem("Tests2"),
	new MenuItem("Scroll Container"),
	new MenuItem("File Chooser with Tree"),
	new MenuItem("SpinList ToolTip ProgressBar"),
	new MenuItem("Drag scroll"),
	new MenuItem("AlignedLabelsContainer"),
	new MenuItem("ListContainer"),
};
​
setMenuBar(mbar = new MenuBar(new MenuItem[][]{col0,col1,col2,col3}));
The result of this sample is shown in the images below taken from the old UIGadgets sample using Android user interface style:
figure companion_resources/images/menubar_001.png figure companion_resources/images/menubar_002.png
figure companion_resources/images/menubar_003.png figure companion_resources/images/menubar_004.png
For more detais, take a look at totalcross.ui.MenuBar JavaDoc.

MenuBarDropDown

Constructs a MenuBarDropDown with the given items. This class is used in conjunction with the MenuBar. However, you can also use it to create a stand alone "right click" menu. The menu items must fit on the screen. No clipping is applied. Also, the font and colors can be changed if desired.
Here is an example of how to build a stand alone MenuBarDropDown:
import totalcross.sys.Settings;
import totalcross.ui.*;
​
public class Teste extends MainWindow
{
	public Teste()
	{
		super("Teste", HORIZONTAL_GRADIENT);
		setUIStyle(Settings.Android);
	}
	
	public void initUI()
	{
		MenuItem col1[] = // note that the first string is always skipped (it would be the MenuItem title in the MenuBar)
		{
			new MenuItem("Record"),
			new MenuItem("NewEvent"),
			new MenuItem("Delete Note..."),
			new MenuItem("Purge..."),
			new MenuItem(), // create a dotted line
			new MenuItem("Beam Event")
		};
		MenuBarDropDown pop = new MenuBarDropDown(10,10,col1);
		pop.popupNonBlocking();
	}
}
which results in the following window:
figure companion_resources/images/menubardropdown_001.png
For more detais, take a look at totalcross.ui.MenuBarDropDown JavaDoc.

MultiListBox

MultiListBox is a ListBox that allows more than one item to be selected. The maximum number of selections can be defined using setMaxSelections(). Be sure to save a reference to the MultiListBox so you can call the specific methods of this class.
To create a ComboBox with a MultiListBox, use:
MultiListBox mlb;
new ComboBox(mlb = new MultiListBox()) 
getSelectedIndex() returns just the last selected index; to retrieve all indexes, use
getSelectedIndexes().
In penless devices, there will be a cursor which will be used to highlight an item; to select or unselect it, you must press the left key. MultiListBox requires the useFullWidthOnSelection on penless devices.
The following screenshots from the old UIGadgets sample using Android user interface style shows the MultiListBox behavior:
figure companion_resources/images/multilist_001.png figure companion_resources/images/multilist_002.png
figure companion_resources/images/multilist_003.png figure companion_resources/images/multilist_004.png
For more detais, take a look at totalcross.ui.MultiListBox JavaDoc.

PagePosition

PagePosition implements the empty and filled balls that indicates the current page in a set of pages, very common on Android and iOS. PagePosition and Flick use it. It has three properties:
  • visibleCount: the number of visible balls.
  • count: the number of balls that will be displayed. It can be less, equal, or greater than the visible count.
  • position: the current position of the filled ball.
PagePosition is shown in the image below from the old UIControls sample (now inside the ui package from the TotalCrossAPI sample) above the mouse cursor, where it is inside a ButtonMenu:
figure companion_resources/images/pageposition_001.png
For more detais, take a look at totalcross.ui.PagePosition JavaDoc.

Ruler

Ruler is a horizontal or vertical ruler. Here’s an example:
Ruler r = new Ruler();
r.invert = true;
add(r, LEFT,AFTER+2);  
which results in the following using Android style:
figure companion_resources/images/ruler_001.png
The old UIGadgets sample also used a default ruler shown near the mouse arrow:
figure companion_resources/images/ruler_002.png
For more detais, take a look at totalcross.ui.Ruler JavaDoc.

PopupMenu

Creates a popup menu with a single line list and some radio buttons at right, like the Android combo box styles. The PRESSED event is sent when an item is selected. The colors must be set before the control’s bounds are defined using setRect() or add().
This is a sample of how to use it:
String[] items =
{
	"Always",
	"Never",
	"Only in Silent mode",
	"Only when not in Silent mode",
	"None of the answers above"
}; 
PopupMenu pm = new PopupMenu("Vibrate",items);
pm.popup(); 
which results in a window similar to the one below:
figure companion_resources/images/popmenu_001.png figure companion_resources/images/popmenu_002.png
For more detais, take a look at totalcross.ui.PopupMenu JavaDoc.

ProgressBar

A basic progress bar, with the bar and a text. The text is comprised of a prefix and a suffix.
You can create a horizontal endless progress bar, always going from left to right, by setting the given parameters:
  • Call setEndless().
  • max-min: used to compute the width of the bar.
  • prefix and suffix: displayed, but the current value is not displayed.
  • setValue(n): n used to increment the current value, not to set the value to n.
Then set a timer to update the value.
Here is some progress bar samples taken from the old UIGadgets and UIControls samples (now inside the ui package from the TotalCrossAPI sample), using Android user interface style:
figure companion_resources/images/progressbar_006.png figure companion_resources/images/progressbar_002.png
For more detais, take a look at totalcross.ui.ProgressBar JavaDoc.

PushButtonGroup

Group or matrix of push buttons in a single control. It is one of the most versatiles controls of TotalCross. The width of each button is calculated based on its caption size plus insideGap, if you use PREFERRED as the width; otherwise, it uses the size you specified (e. g.: FILL, FIT, etc). The height is calculated based on the font’s size or on the height you specified.
Here is an example of constructor:
new PushButtonGroup(new String[]{"Button1", "Button2", "Button3"},  false, -1, -1, 4, 0, false, PushButtonGroup.NORMAL); 
which results in this window when the control is added to its center using Android user interface style:
figure companion_resources/images/pushbutton_001.png figure companion_resources/images/pushbutton_002.png
figure companion_resources/images/pushbutton_003.png figure companion_resources/images/pushbutton_004.png
Also using Android user interface style, the screenshots below from the old UIGadgets sample also show this control being used:
figure companion_resources/images/pushbutton_005.png figure companion_resources/images/pushbutton_006.png
This class has two very important fields called colspan and rowspan, which spans a cell across multiple columns and rows. These cells that will be overriden must be null and the parameter allSameWidth passed in the constructor must be true. This sample:
String []numerics = {"1", "2", "3", "4", "5", "6", "7", "clear", null, "0", null, null};
PushButtonGroup pbg = new PushButtonGroup(numerics, false, -1, 4, 0, 4, true, PushButtonGroup.BUTTON);
pbg.colspan[7] = 2;
pbg.rowspan[7] = 2;
add(pbg, LEFT+50,AFTER+50,FILL-50,FILL-50); 
will show this:
figure companion_resources/images/pushbutton_007.png
For more detais, take a look at totalcross.ui.PushButtonGroup JavaDoc.

ScrollBar

The scroll bar orientation can be horizontal or vertical. It implements auto scroll when pressing and holding a button or the gap area of the scroll bar. Here is an example of how to use it.
import totalcross.sys.Settings;
import totalcross.ui.*;
import totalcross.ui.event.*;
public class Teste extends MainWindow
{
	ScrollBar sb1, sb2, sb3, sb4;
​
	public Teste()
	{
		super("Teste", HORIZONTAL_GRADIENT);
		setUIStyle(Settings.Android);
	}
​
	public void initUI()
	{
		add(sb1 = new ScrollBar(ScrollBar.VERTICAL), RIGHT, CENTER, PREFERRED, Settings.screenHeight/2);
		add(sb2 = new ScrollBar(ScrollBar.VERTICAL), BEFORE, SAME, PREFERRED, SAME);
		sb2.setLiveScrolling(true);
		add(sb3 = new ScrollBar(ScrollBar.HORIZONTAL), LEFT,CENTER, Settings.screenWidth/2, PREFERRED);
		add(sb4 = new ScrollBar(ScrollBar.HORIZONTAL), SAME, AFTER, SAME, PREFERRED);
	}
​
	public void onEvent(Event event)
	{
		if (event.type == ControlEvent.PRESSED && event.target == sb2)
		{
			int value = sb2.getValue();
			sb1.setValue(value);
			sb3.setValue(value);
			sb4.setValue(value);
		}
	}
}
which results in the following images:
figure companion_resources/images/scrollbar_001.png figure companion_resources/images/scrollbar_002.png
figure companion_resources/images/scrollbar_003.png figure companion_resources/images/scrollbar_004.png
For more detais, take a look at totalcross.ui.ScrollBar JavaDoc.

ScrollContainer

This is a container with a horizontal only, vertical only, both, or no scroll bars, depending on the control positions. The default unit scroll is an edit’s height (for the vertical scrollbar), and the width of an @ (for the horizontal scrollbar).
Caution: you must not use RIGHT, BOTTOM, CENTER, and FILL when setting the control bounds, unless you disable the corresponding scroll bar! The only exception to this is to use FILL on the control’s height, which is allowed.
Here is an example showing how it can be used:
import totalcross.sys.Settings; 
import totalcross.ui.*;
​
public class Teste extends MainWindow 
{    
	public Teste()
	{
		super("Teste", HORIZONTAL_GRADIENT);
		setUIStyle(Settings.Android);
	}
​
	ScrollContainer sc;
	
	public void initUI()
	{
		ScrollContainer sc;
		add(sc = new ScrollContainer());
		sc.setBorderStyle(BORDER_SIMPLE);
		sc.setRect(LEFT+10,TOP+10,FILL-20,FILL-20);
		int xx = new Label("Name99").getPreferredWidth()+2; // edit’s alignment
		for (int i =0; i < 100; i++)
		{
			sc.add(new Label("Name"+i),LEFT,AFTER);
			sc.add(new Edit("@@@@@@@@@@@@@@@@@@@@"),xx,SAME);
			if (i % 3 == 0)
				sc.add(new Button("Go"), AFTER+2,SAME,PREFERRED,SAME);
		}
	}
}
which results in the following screenshots:
figure companion_resources/images/scrollcontainer_001.png figure companion_resources/images/scrollcontainer_002.png
figure companion_resources/images/scrollcontainer_003.png figure companion_resources/images/scrollcontainer_004.png
UIControl uses this control in almost all windows so that its components can be scrolled. The old UIGadgets sample also uses it in one of its tests. Using Android user interface style, the following screenshot can be seen:
figure companion_resources/images/scrollcontainer_005.png
For more detais, take a look at totalcross.ui.ScrollContainer JavaDoc.

ScrollPosition

ScrollPosition implements the auto-hide scroll bar that exists in finger-touched devices. This special scroll bar is just a small position indicator that appears when the area is dragged.
ScrollPosition
does not take an area of the control, since it appears and disappears automatically. All Scrollable controls change their ScrollBar by ScrollPosition when Settings.fingerTouch = true. If the back color and the bar color are the same, the bar is not drawn; this is how the ButtonMenu class hides this control.
Some images of it can be found in the old UIControls sample (now inside the ui package from the TotalCrossAPI sample) when scrolling its main window:
figure companion_resources/images/scrollposition_001.png figure companion_resources/images/scrollposition_002.png
For more detais, take a look at totalcross.ui.ScrollPosition JavaDoc.

Slider

Slider is a simple slider. You can set some properties of the slider, like drawTicks,
invertDirection
and drawFilledArea. You can change the thumb size by setting the minDragBarSize public field and then call setValues(), setMaximum(), or setMinimum() method (the value must always be ODD!). The slider is the component pointed by the mouse arrow in the old UIGadgets sample using the Android style below:
figure companion_resources/images/slider_001.png
For more detais, take a look at totalcross.ui.Slider JavaDoc.

Spacer

Control used to add a space between controls. It shows nothing on screen.
Here’s a sample of how to use it:
Spacer s = new Spacer("  ");
Button btnClear = new Button("Clear");
Button btnOK = new Button("OK");
add(s, CENTER,AFTER+2);
add(btnClear, AFTER,SAME, s);
add(btnOK, BEFORE, SAME, SAME, SAME, s); 
which results in the following using Android style, which places two buttons centered on screen, like this:
figure companion_resources/images/spacer_001.png
The spacer is also used to separate the spinners in the Spinner samples of old UIControls sample (now inside the ui package from the TotalCrossAPI sample) and the back and next buttons from the old Litebase sample PhotoDB using the old Windows CE style (now inside the sample Address Book and using Android style):
figure companion_resources/images/spacer_002.png figure companion_resources/images/spacer_003.png
For more detais, take a look at totalcross.ui.Spacer JavaDoc.

SpinList

Creates a control with two arrows, so that you can scroll values and show the current one. It supports auto-scroll (by clicking and holding) and can also dynamically compute the items based on ranges. The spin list can be horizontal or vertical. You can use something like:
SpinList sl = new SpinList(..., !Settings.fingerTouch); 
This way, in finger-touch devices, it will use the horizontal appearance, which is easier to deal with on such devices.
The screenshots below from the old UIGadgets sample using Android user interface style shows the days being increased and decreased when pressing the increase or decrease arrow, respectively:
figure companion_resources/images/spinlist_001.png figure companion_resources/images/spinlist_002.png
figure companion_resources/images/spinlist_003.png figure companion_resources/images/spinlist_004.png
For more detais, take a look at totalcross.ui.SpinList JavaDoc.

Spinner

Spinner is a control that shows an image indicating that something is running in the background. It has two styles: IPHONE and ANDROID. It’s used in the ProgressBox and can be used in the Bar. To start the spin call the start() method, and to stop it call the stop() method. An image of it has already been shown in section Ruler↑.
For more detais, take a look at totalcross.ui.Spinner JavaDoc.

TabbedContainer

TabbedContainer is a bar of text or image tabs. It is assumed that all images will have the same height, but they may have different widths. A scroll is automatically added when the total width of the titles is bigger than the control’s width. The containers are created automatically and switched when the user presses the corresponding tab.
When the user interface has Android style, the tabs do not look good if the background is the same of the parent’s. In this case, we force the background to be slighly darker. There are a few fields that you can use to change the color, like activeTabBackColor,
useOnTabTheContainerColor
, and pressedColor.
Important: with Settings.fingerTouch = true, you CANNOT call setRect() in your container. Otherwise, the flick and drag will not work and your container will be positioned incorrectly.
Below are some screenshots taken from the old UIGadgets (using Android user interface style) and UIControls (now inside the ui package from the TotalCrossAPI sample) samples:
figure companion_resources/images/tabbedcontainer_001.png figure companion_resources/images/tabbedcontainer_002.png
figure companion_resources/images/tabbedcontainer_003.png
figure companion_resources/images/tabbedcontainer_004.png figure companion_resources/images/tabbedcontainer_005.png
figure companion_resources/images/tabbedcontainer_006.png
For more detais, take a look at totalcross.ui.TabbedContainer JavaDoc.

Tree

This class is a simple implementation of a tree widget. Since it’s natural to render the tree in rows, this class borrows most of the code from ListBox. Features:
  • Similar to Microsoft Windows Explorer tree.
  • Horizontal and vertical scrolling.
  • Allows setting of folder and leaf icons.
  • Expands and collapses of folder.
  • allowsChildren flag to determine if the node is a leaf or a folder.
  • Delete, insert, and modify (user object or identifier) of a node.
  • Clicking on a leaf node will swap to the leaf icon (like hyperlink).
  • Allows the creation of a tree to show or hide the root node.
You should use the TreeModel class to modify the tree after and the class Node to add nodes to the tree. Here’s a sample:
TreeModel tmodel = new TreeModel();
Tree tree = new Tree(tmodel);
add(tree,LEFT,TOP,FILL,FILL);
Node root = new Node("Tree");
tmodel.setRoot(root);
Node n;
root.add(n = new Node("Branch1"));
n.add(new Node("SubBranch1"));
n.add(new Node("SubBranch2")); 
which results in the following window (using Android style):
figure companion_resources/images/tree_001.png figure companion_resources/images/tree_002.png
The old UIGadgets sample also has a sample called FileChooserTest which uses Tree. Using Android style, it looks like this:
figure companion_resources/images/tree_003.png
For more detais, take a look at totalcross.ui.tree classes JavaDocs.

Whiteboard

This is a whiteboard that can be used to draw something. It uses a special event flag in order to improve the accuracy.
The sample Painter uses it. The pictures below show its whiteboard empty and drawn, respectively, where the application interface style was changed to Android:
figure companion_resources/images/white_001.png figure companion_resources/images/white_002.png
For more detais, take a look at totalcross.ui.WhiteBoard JavaDoc.

MultiButton

MultiButton is a control that displays a single line button with a set of titles. It can be used to replace a check (with on/off) or a radio (with their options). Below there is a sample taken from old UIControls sample (now inside the ui package from the TotalCrossAPI sample):
figure companion_resources/images/multi_button_001.png figure companion_resources/images/multi_button_002.png
For more detais, take a look at totalcross.ui.MultiButton JavaDoc.

21 Charts

Here a list of chart classes supported by TotalCross. They are located in the package totalcross.ui. chart. Read their JavaDocs for more information.

Chart

The base class of all Chart classes.

ColumnChart

This is a a vertical column chart. Below are shown some screenshots taken from the old sample CharTest (now inside the ui package from the TotalCrossAPI sample):
figure companion_resources/images/chart_001.png figure companion_resources/images/chart_002.png
figure companion_resources/images/chart_003.png figure companion_resources/images/chart_004.png
figure companion_resources/images/chart_005.png figure companion_resources/images/chart_016.png

PointLineChart

Abstract class used by points and line charts, extended by LineChart and XYChart.

LineChart

This class represents a line chart. Below are shown some screenshots taken from the old CharTest sample (now inside the ui package from the TotalCrossAPI sample):
figure companion_resources/images/chart_006.png figure companion_resources/images/chart_007.png
figure companion_resources/images/chart_008.png figure companion_resources/images/chart_009.png
figure companion_resources/images/chart_010.png

PieChart

A simple pie chart. Below are shown some screenshots taken from the old CharTest sample (now inside the ui package from the TotalCrossAPI sample):
figure companion_resources/images/chart_011.png figure companion_resources/images/chart_012.png
figure companion_resources/images/chart_013.png figure companion_resources/images/chart_014.png
figure companion_resources/images/chart_015.png

XYChart

XYChart is a scatter chart. Here is an example of how to use it, together with a screenshot of the application.
import totalcross.sys.Settings;
import totalcross.ui.*;
import totalcross.ui.chart.*;
import totalcross.ui.event.*;
import totalcross.ui.gfx.Color;
public class ChartTest2 extends MainWindow
{
	static
	{
		Settings.useNewFont = true;
	}
	XYChart chart;
	Check showTitle, showHGrids, showVGrids, showYValues;
	ComboBox legendPosition;
​
	public ChartTest2()
	{
		super("Chart Test 2", TAB_ONLY_BORDER);
	}
​
	public void initUI()
	{
		add(showTitle = new Check("Title"), LEFT, TOP + 2);
		add(legendPosition = new ComboBox(new String[]{"Legend","Right","Left","Top","Bottom"}), AFTER + 2, SAME,PREFERRED,SAME);
		add(showYValues = new Check("YValues"), AFTER+2, SAME);
		add(showHGrids = new Check("HGrids"), LEFT, AFTER + 2);
		add(showVGrids = new Check("VGrids"), AFTER + 2, SAME);
		legendPosition.setSelectedIndex(0);
		add(new Ruler(),LEFT,AFTER+1);
		double[] months = new double[]{100, 200, 300, 400};
		(chart = new XYChart()).series.addElement(new Series("Rice", months, new double[] {100, 102, 104, 106}, Color.YELLOW));
		chart.series.addElement(new Series("Beans", months, new double[] {150, 155, 159, 164}, Color.GREEN));
		chart.series.addElement(new Series("Oil", months, new double[] {130, 137, 143, 150}, Color.RED));
		chart.lineThickness = 2;
		chart.setTitle("Sales Projection");
		chart.setYAxis(0, 200, 10);
		chart.setXAxis(0, 400, 10);
		add(chart, LEFT, AFTER+2, FILL,FILL);
		chart.setBackColor(Color.darker(backColor,16));
		chart.yDecimalPlaces = 0;
		chart.legendPerspective = 6;
	}
	
	public void onEvent(Event e)
	{
		if (e.type == ControlEvent.PRESSED && (e.target instanceof Check || e.target instanceof ComboBox))
		{
			chart.showTitle = showTitle.isChecked();
			chart.showLegend = legendPosition.getSelectedIndex() != 0;
			chart.legendPosition = getLegendPosition();
			chart.showHGrids = showHGrids.isChecked();
			chart.showVGrids = showVGrids.isChecked();
			chart.showYValues = showYValues.isChecked();
			repaint();
		}
	}
​
	private int getLegendPosition()
	{
		switch (legendPosition.getSelectedIndex())
		{
			case 2: return LEFT;
			case 3: return TOP;
			case 4: return BOTTOM;
			default: return RIGHT;
		}
	}
} 
figure companion_resources/images/chart_017.png

ChartData

Represents a table with data that can be displayed together with a chart. Below it is a code sample and a screen with its result. Note that the ChartData grid is integrated with the chart grid.
import totalcross.sys.Settings;
import totalcross.ui.*;
import totalcross.ui.chart.*;
import totalcross.ui.chart.ChartData.ChartDataRow;
import totalcross.ui.gfx.*;
import totalcross.util.Properties;
import totalcross.util.Properties.Str;
​
public class TCTestWin extends MainWindow
{
	public void initUI()
	{
		double[] xAxis = new double[0];
		Series series = new Series("test", xAxis, new double[0], Color.BLUE);
		int cols = 10, rows = 5;
		XYChart chart = new XYChart();
		ChartDataRow[] data1 = new ChartDataRow[rows];
		ChartDataRow[] data2 = new ChartDataRow[rows];
		ChartData cd1 = new ChartData(chart), cd2 = new ChartData(chart);
		Str[] values;
	
		setUIStyle(Settings.Android);
​
		chart.showHGrids = chart.showVGrids = chart.showYValues = chart.snapToTop = chart.snapToBottom = cd2.snapToBottom = cd1.snapToTop = true;
		chart.yDecimalPlaces = 0; // No decimal places.
		chart.setXAxis(0, 100, 10);
		chart.setYAxis(0, 100, 10);
		chart.showLines = false;
		chart.yValuesSize = fm.stringWidth("99999");
		chart.series.addElement(series);
		cd1.lineColor = cd2.lineColor = Color.BLACK;
	
		for (int r = 0; r < rows; r++)
		{
			data1[r] = cd1.new ChartDataRow("row " + (r + 1), values = new Str[cols]);
			data2[r] = cd2.new ChartDataRow("row " + (r + 1), values);
			for (int c = 0; c < cols; c++)
				values[c] = new Properties.Str("" + (r + 1) + c);
			cd1.addLine(rows, data1[r]);
			cd2.addLine(rows, data2[r]);
		}              
​
		add(cd1,LEFT + 5, TOP + 25, FILL, PREFERRED);
		add(cd2,LEFT + 5, BOTTOM - 25, FILL, PREFERRED);
		add(chart,LEFT + 5, AFTER, FILL, FIT, cd1);
		cd2.bringToFront();
		cd1.bringToFront(); 
		series.xValues = new double[]{10, 20, 30, 40, 50};
		series.yValues = new double[]{10, 30, 50, 70, 90};      
	} 
}
figure companion_resources/images/chart_data.png

22 The Window class

As you already know, a TotalCross program with user interface consists of one and only one main window (a class that directly or indirecly extends MainWindow). This main window can pop up a window, and this new window can pop up another one, and so on. Windows in TotalCross are always modal, therefore, only the last popped up window can receive events and you cannot switch from the topmost Window to the previous without closing the topmost one.
Although the Window class extends Control, you can’t add a Window to a Container. Doing this results in a RuntimeException. To show a window, you must use the method popup() or the method popupNonBlocking().
The following example creates a popup window class:
class TestWindow extends Window
{
	Button btnHi;
	public TestWindow()
	{
		super("Test",RECT_BORDER); // with caption and borders
		setRect(CENTER,CENTER,Settings.screenWidth/2,Settings.screenHeight/4);
		add(btnHi=new Button("Hi!"),CENTER,CENTER);
	}
	public void onEvent(Event event)
	{
		if (event.type == ControlEvent.PRESSED && event.target == btnHi)
			unpop(); // a WINDOW_CLOSED event will be posted to this PARENT window.
	}
} 
To use it in the normal way (blocking):
public class Launcher extends MainWindow
{
	Button btn;
	public void onEvent(Event e)
	{
		if (e.target == btn)
		{
			TestWindow tw = new TestWindow();
			tw.popup(); // this line is only executed after the window is closed.
		}
	}
} 
To use it non-blocking (the execution continues right after the popup command, even with the window still open):
public class Launcher extends MainWindow
{
	TestWindow tw;
	public void initUI()
	{
		tw = new TestWindow();
		tw.popupNonBlocking(); // this line is executed immediately
	}
	public void onEvent(Event event)
	{
		if (event.target == tw && event.type == ControlEvent.WINDOW_CLOSED)
		{
			// any stuff
			break;
		}
	}
} 
Blocking popup may be use in InputBox/MessageBox classes, while non-blocking popup is used in MenuBar and other classes. Important note: you can’t use popup() with a delay to unpop it. In this case, the correct would be to use popupNonBlocking():
mb = new MessageBox(...);
mb.popupNonBlocking();
Vm.sleep(5000); // or do something else
mb.unpop(); 
If you use popup() in this specific case, the VM will hang.
Some other features of the Window class:
  • Windows can have a title that can be set by the method setTitle(String title) (which calls repaint()) or passed to the constructor.
  • The window border can be selected from one of the multiple styles shown below, by using the setBorderStyle(byte borderStyle) method or passing the desired style to the Window constructor. The parameter value can be NO_BORDER, RECT_BORDER, ROUND_ BORDER, TAB_BORDER, TAB_ONLY_BORDER, HORIZONTAL_GRADIENT, or VERTICAL_ GRADIENT. To retrive it, use getBorderStyle().
  • There are two constructors: the default one, that creates a window with no title and no border, and one constructor with both title and border parameters.
    • Window()
    • Window(String title, byte borderStyle)
  • Windows can be moved around the screen by dragging the window’s title. If the window has no title, it can’t be moved. You can make a titled window unmovable by calling the makeUnmovable() method.
  • The title font can be changed using the setTitleFont() method. To retrive it, use
    getTitleFont(). By default, the font is the one used by the main window, with bold style.
  • Only one control can hold the focus at a time. To change focus to another control, use the setFocus(Control c) method (this can also be done through the requestFocus() method in the totalcross.ui.Control class). When a user types a key, the control with focus gets the key event. Calling this method will cause a FOCUS_OUT control event to be posted to the window’s current focus control (if one exists) and will cause a FOCUS_IN control event to be posted to the new focus control. The getFocus() method returns the control that currently owns the focus.
  • The rectangle area excluding the border and the title is defined as the client rectangle. You can get it with the getClientRect() method.
  • A window can be popped up by calling the popupNonBlocking() method and can be unpopped by calling the unpop() method. The popup process saves the area behind the window that is being popped up and the unpop process restores that area. The unpop() method posts a ControlEvent.WINDOW_CLOSED event to the caller window. The
    popupNonBlocking()
    method can be called like this.popupNonBlocking(). Calling unpop() when only the MainWindow is active does nothing.
  • A window can also be popped up by calling the popup() method, and be unpopped by the same unpop() method described above. The big difference is that in
    popupNonBlocking()
    , the program execution continues to the next line, while in popup(), the program execution is halted and only continues when the popped up window is dismissed. Menu, MessageBox, ComboBox, and ComboBoxDropDown are popped up using popupNonBlocking(), because execution does not need to be halted. InputDialog, Calendar, and Calculator are usually popped up using popup() because the user may want to get the result of the dialog in an easy way.
    You can’t use popup() to popup alternating windows that call each other recursively. For example, suppose that from win1 you call win2.popup(), then at win2 you call unpop() and then win1.popup(). Then, from win1 you do unpop() again and win2.popup(), and so on. This will lead to an OutOfMemoryError on the device due to a native stack overflow. To fix this, just replace the popup by popupNonBlocking().
  • The topmost window (the one who receive events) can be obtained with the static method getTopMost(). To check if this window is the topmost, use the isTopMost() method.
  • Try setGrabPenEvents() for settting to a control to redirect all pen events directly to it. This method speeds up pen event processing. Used in Whiteboard class.
  • You may check if this window is visible using the isVisible() method. This method is inherited from totalcross.ui.Control, but it simply checks if the current window is the topmost one.
  • Each window can have a menu attached by using the method setMenuBar(). The menuBar can be made visible programatically by calling the popupMenuBar() method.
  • Suppose you wish to allow the user to abort a task being executed by pressing. You can use the method pumpEvents() to process all events in the queue. This method is used to implement a blocking Window. Here is an example:
    while(someCondition) 
    	Event.pumpEvents();
    
  • The methods getPreferredWidth() and getPreferredHeight() have a special meaning for the Window class. They return the minimum width/height needed for the correct display of this window. getPreferredWidth() returns the width of the title (if any) plus the width of the border (if any). getPreferredHeight() returns the height of the title (if any) plus the height of the border (if any).
There are some useful protected methods that may be implemented by controls that extend
totalcross.ui.Window
. Those methods are placeholders and there is no need to call the super method.
  • onClickedOutside(int x, int y) This method is used in popup windows. If the user clicks outside the window’s bounds, this method is called giving the absolute coordinates of the clicked point. There are two options:
    If you had handled the action, return true in this method. Otherwise, false must be returned and if the beepIfOut member is true, a beep is played (in other words, beepIfOut can be set to false to disable this beep).
  • onPopup() Called just after the behind contents are saved and before the popup process begin. When this method is called, the topmost window is still the parent of the window being popped up.
  • postPopup() Called after the popup process ended. When this method is called, the popped up window is fully functional. It is a good place to put a control.requestFocus() to make the window popup with the focus in a default control.
  • onUnpop() Called just before the unpop process begin.
  • postUnpop() Called after the unpop process ended. When this method is called, the unpopped window has gone away and the parent window is currently the topmost.
  • postPressedEvent() Posts a ControlEvent.PRESSED event on the focused control.
A very common mistake is to popup a window without setting its bounds. If no bounds are set, the window will not receive events.
The other members that can be used (all public and some protected) of the Window class are explained here:
  • needsPaint true if there are any controls marked for repaint (some area of the window is invalidated).
  • tempTitle A temporary title that will be displayed when the Window pops up. It will be replaced by the original title when it is closed.
  • topmost Stores the topmost window.
  • firstFocus The control that should get focus when a focus traversal key is pressed and none has focus.
  • canDrag (protected) If true and if this is a popup window, the user is allowed to drag the title and make the window move around.
  • cancelPenUp If true, the next PEN_UP event will be ignored. This is used when a PEN_DOWN cancels a flick, or if a drag-scrollable control needs to cancel the next pen_up during a drag-scrolling interaction.
  • gradientTitleStartColor The starting and ending colors used to fill the gradient title.
  • gradientTitleEndColor The starting and ending colors used to fill the gradient title.
  • titleColor The title color. The title color depends on the border type: it will be the foreground color if NO_BORDER is set; otherwise, it will be the background color.
  • titleGap A vertical gap used to increase the title area. Defaults to fmH/2 on Android and 0 on other user interface styles.
  • titleAlign The title horizontal alignment in the window’s title area. It can be LEFT, CENTER, or RIGHT, and you can use an adjustment on the value (E.G.: LEFT+5).
  • headerColorfooterColor Has the header and the footer colors when on Android style and border type is ROUND_BORDER. Not used on other styles.
  • fadeOtherWindows Set to true to make the other windows be faded when the window appears.
  • fadeValue The value used to fade the other windows. Defaults to 128.
  • robot The UIRobot instance that is being used to record or play events.
Never mess with the public member zStack. It is used to store the windows that are currently popped up. It is made public because the totalcross.Launcher class uses it.
Next is explained how controls inside a window are repainted:
  1. The programmer calls the repaint() method of some controls, or a control is clicked and marks itself for repaint.
  2. The damageRect() method in class window creates a rectangle (stored in the paintX, paintY, paintWidth and paintHeight members) with the union of the bounds of all controls marked for repaint.
  3. The next time a VM event is posted, the _doPaint() method of the topmost window is called. This method paints the window’s title/border (if any) and calls the onPaint() method of all containers and controls that lies inside the rectangle area marked for repaint. This explains why nothing in the window is updated when you receive events directly from a native library (the Scanner class, for example). Because the VM is not receiving the event, it never validates the window. In these cases, you must update the window yourself, calling repaintNow() or the validate methods.
Many classes in the totalcross.ui package extend totalcross.ui.Window. Examples of such classes are CalculatorBox and CalendarBox. Other good examples are
ComboBoxDropDown
and MessageBox.
It’s important to be aware that it is not a good practice to create classes that extend Window if they will occupy the whole screen, because they use a lot of memory to store the underlying area. Opening the menu may lead to time-consuming redraws of all opened windows due to out-of-memory problems. In these cases, it is better to use Containers.

23 UIColors

This class contains the default colors used in most user interface windows, like CalendarBox, KeyboardBox, CalculatorBox, InputBox, MessageBox, and also the default foreground and background colors of all controls created.
There are no methods in this class, only public static members, which can be freely changed to meet your user interface color scheme. To correctly change the colors for your own, you must do it in the constructor of your application. Note that each member defines the default value for all controls of your application.

24 Interface Dialogs

The TotalCross SDK provides some handy interface dialogs, which are windows that handles common user interaction scenarios, like displaying an informative message or requesting a specific user input.
Interface dialogs are pop-up windows that extend the Window class, and just like Window, they are not automatically displayed under creation. To show or hide a dialog you must use Window methods: popup(), popupNonBlocking() and unpop().
For more details about them, check out the package totalcross.ui.dialog JavaDocs.

MessageBox

Simple dialog used to display a text with some user-defined buttons. Useful for displaying informative messages or to make the user take a decision before proceeding (e.g. “Yes/No”, “Ok/Cancel”, “Save/Discard/Cancel”).
Below there is a MessageBox from the old UIGadgets sample using the Android user interface style:
figure companion_resources/images/messagebox_001.png
MessageBox has five constructors:
  • MessageBox(String title, String msg) Creates a MessageBox with the given title and message. The message is displayed using a label, and can be displayed in multiple lines if previously parsed with the \n character. It also displays a single <Ok> button to dismiss the window.
  • MessageBox(String title, String text, String[] buttonCaptions) Same as above, but also receives a string array specifying the button captions. A
    PushButtonGroup
    is used to display the buttons, and the method
    getPressedButtonIndex()
    returns the index of the pressed button (where the first one has index 0).
  • MessageBox(String title,String text,String[] buttonCaptions,
    boolean allSameWidth)
    Same as above, but the last parameter indicates that all the buttons have the same width.
  • MessageBox(String title,String text,String[] buttonCaptions,
    int gap,int insideGap)
    Same as the second one, but also receives the gap (space between buttons) and insideGap (space between the button’s text and it’s bounds) to be used by the PushButtonGroup.
  • MessageBox(String title,String text,String[] buttonCaptions,
    boolean allSameWidth,int gap,int insideGap)
    It is the most complete constructor, with all the possible parameters.
The message box is displayed on the center of the device screen, and its bounds are calculated based on the given title, text and buttons. If the text height is above the screen limits, two arrows are added to the message box to allow the text scrolling.
  • MessageBox has the following public fields (omitting some methods from its superclasses):
    • btns The PushButtonGroup with the box buttons.
    • yPosition Defines the y position on screen where the window opens. It can be changed to TOP or BOTTOM. Defaults to CENTER.
    • buttonKeys If you set the buttonCaptions array in the construction, you can also set this public field to an int array of the keys that maps to each of the buttons. For example, if you set the buttons to {"Ok","Cancel"}, you can map the enter key for the Ok button and the escape key for the Cancel button by assigning:
      buttonKeys = new int[]{SpecialKeys.ENTER,SpecialKeys.ESCAPE}; 
      
      Note that ENTER is also handled as ACTION, since the ENTER key is mapped to ACTION on some platforms.
    • showExceptionTitle Title shown in the showException() dialog for all exceptions shown this way.
  • MessageBox has the following public instance methods (omitting some methods from its superclasses):
    • setText(String text) This method can be used to set the text AFTER the dialog was shown. However, the dialog will not be resized.
    • setIcon(Image icon) Sets an icon to be shown in the MessageBox’s title, at left. It only works if there’s a title. If you really need an empty title, pass as title a string with a couple of spaces, like " ". The icon’s width and height will be set to title’s font ascent.
    • getPressedButtonIndex() Returns the index of the pressed button. This index is zero based (the first element has index 0).
      This method returns -1 if invoked before the window is closed (or if it is closed without any button being pressed). To make sure you’re retrieving the correct value, consider using popup() instead of popupNonBlocking(), or invoke this method only after the window is closed (MessageBox is a Window, so it also posts the event ControlEvent. WINDOW_CLOSED when closed).
    • setTextAlignment(int align) Sets the alignment of the label that displays the text. The argument must be one of the following constants: LEFT, CENTER, or RIGHT. The default alignment is CENTER.
    • setUnpopDelay(int unpopDelay) Automatically unpops the message box after the given time in milliseconds. You must use this method just before displaying the window (either by popup() or popupNonBlocking()). This method actually adds a timer to the message box, invoking the unpop() method when the TimerEvent is triggered. Be sure to learn how the timer works to better understand this method.
    • onEvent(Event e) Handles scroll buttons and normal buttons.
    • setDelayToShowButton(int ms) Calling this method will make the buttons initially hidden and will show them after the specified number of milisseconds. Here’s a sample:
      MessageBox mb = new MessageBox("Novo Tweet!",tweet);
      mb.setTimeToShowButton(7000);
      mb.popup();  
      
    The method setUnpopDelay() does not affect the regular behavior of the message box – the popupNonBlocking() method will not block the program execution and will still return immediately, the popup() method will still block the program execution, and the message box may still be dismissed by the user (e.g. pressing one of its buttons) before the timer finishes.
An interesting feature of the message box is that you may create it with no buttons, just passing the null value for the constructor’s argument buttonCaptions. However, a message box created with no buttons does not provide a default way of being dismissed by the user. You must dismiss it programatically, by invoking unpop(), using setUnpopDelay() or handling events (e.g. you may handle pen events to make it unpop after the user touches the screen).
The MessageBox class has a handy method to debug your application:
  • showException(Throwable t, boolean dumpToConsole) Immediately displays a message box showing the given throwable name, message, and its stack trace. This information is dumped to the debug console if the field dumpToConsole is true.
If you use an IDE with customizable templates for automatic code generation (like Eclipse), try changing the template for try/catch blocks to call MessageBox.showException() instead of Throwable.printStackTrace().

InputBox

Simple dialog used to get a text input from the user. It contains a label to display some text, an edit to receive the user input, and some user-defined buttons. Basically it’s like a message box with an edit.
Below there is a InputBox from the old UIGadgets sample using the Android user interface style:
figure companion_resources/images/inputbox_001.png
It has three constructors:
  • InputBox(String title, String text, String defaultValue) Creates an input box with the given title, text, a default value for the edit, and two buttons: <Ok> and <Cancel>.
  • InputBox(String title,String text,String defaultValue,
    String[] buttonCaptions)
    Same as the above, plus a string array specifying the buttons captions. A PushButtonGroup is used to display the buttons, and the
    getPressedButtonIndex()
    method returns the pressed button index.
  • InputBox(String title,String text,String defaultValue,
    String[] buttonCaptions,boolean allSameWidth,int gap,int insideGap)
    The most complete version, where allSameWidth indicates that all the buttons have the same width, gap is the space between buttons, and insideGap is the space between the button’s text and it’s bounds.
Unlike the message box, the argument buttonCaptions cannot have a null value.
The input box is displayed on the center of the device screen, and its bounds are calculated based on the given title, text, edit, and buttons. If the text height is above the screen limits, two arrows are added to the input box to allow the text scrolling. And its edit receives the focus when the input box is popped up.
The InputBox class provides the following public attributes and methods (omitting some fields from its superclasses):
  • openKeyboardOnPopup Set to true to automatically open the keyboard once the InputBox is open. Since this is a static member, it is valid for all InputBoxes.
  • yPosition Defines the y position on screen where the window opens. Can be changed to TOP or BOTTOM. Defaults to CENTER. The image above shows a InputBox opened at the bottom.
  • buttonKeys If you set the buttonCaptions array in the construction, you can also set this public field to an int array of the keys that maps to each of the buttons.
  • setTextAlignment(int align) Sets the alignment for the text. The parameter must be CENTER (default), LEFT, or RIGHT.
  • getPressedButtonIndex() Returns the index of the pressed button. This index is zero based (the first element has index 0). This method returns -1 if invoked before the window is closed (or if it is closed without any button being pressed). To make sure you’re retrieving the correct value, consider using popup() instead of popupNonBlocking(), or invoke this method only after the window is closed (InputBox is a Window, so it also posts the event ControlEvent.WINDOW_CLOSED when closed).
  • getEdit() Returns a reference to this input box’s edit, so you can change its properties.
  • getValue() Returns a string with the edit’s text.
  • setValue(String value) Sets the edit with the given value.

KeyboardBox

Dialog that displays a virtual keyboard that can be used to handle text input either by the device’s keyboard or by pen events on the virtual keyboard on devices with touchscreen.
Unlike other dialogs, the KeyboardBox constructor does not receive any arguments, and it does not provide any methods for its manipulation. So how does it work?
When the keyboard box is popped up, it gets the control of the topmost window that currently holds the focus. The control is shown at the top of the keyboard box, and any text input received is passed to the control as a key event. If the virtual keyboard is used, the keyboard box handles the pen event, converting it to a key event before passing to the target control.
The edit and multi edit controls may use the keyboard box for text input.
Below there is a KeyboardBox from the old UIGadgets sample using the Android user interface style:
figure companion_resources/images/keyboardbox_001.png

CalculatorBox

A dialog with a simpler calculator. It allows you to enter two numbers, select an operation and calculate the result. You may also paste the result or the first operand.
This class is used by the Edit class when its mode is set to CURRENCY and displays a calculator with six basic operations and a numeric pad. Here is a sample from the old UIControls sample (now inside the ui package from the TotalCrossAPI sample):
figure companion_resources/images/calculator_001.png
This class has the following public attributes, constructors, and methods (excluding some fields from its superclasses):
  • edNumber The edit used to show the number.
  • actions Strings used to display the action messages. You can localize these strings if you wish. Its default value is {"Clear","Ok","Cancel"}.
  • defaultTitle A static field that defines the default title for all calculator boxes, which is Numeric Pad.
  • optionalValue Defines an optional character to be used in the CalculatorBox. Replaces the decimal separator / 00 character.
  • maxLength The maximum length for the edit that will be created.
  • cOrig The control that had focus when the CalculatorBox was popped up.
  • defaultValue The default value of the edit.
  • keepOriginalValue Set to true to don’t replace the original value in the Edit if the user pressed Ok.
  • showNextButtonInsteadOfClear Set to true to replace the Clear button by the Next button. This button is equivalent to the Ok button, but it also changes the focus to the next field. The user can still clean the edit by clicking the backspace << button. The default behaviour calls moveFocusToNextControl(). You can change it by overriding the method gotoNext().
  • CalculatorBox() Constructs a CalculatorBox with the 6 basic operations visible.
  • CalculatorBox(boolean showOperations) Constructs a CalculatorBox with the 6 basic operations hidden if the parameter is false. In this case, it will become a numeric box.
  • clear(boolean requestFocusOnOper1) Clears everything in this calculator.
  • getAnswer() Returns a string with the answer the user selected to be pasted (the result, the first operand, or null if the user canceled).

CalendarBox

Displays a calendar where a date can be chosen. It pops up with the current day as default and the user can scroll through months or years. It uses the Date class for all operations. When a day is selected the calendar is closed and you may retrieve a Date object representing the chosen date.
Instead of creating a new instance (which consumes memory), you may use the Edit’s static field calendar.
If there is something in the edit box which poped up the calendar, the clear button will clear it. Cancel will leave whatever was in there.
The month can be changed via keyboard using the left/right keys, and the year can be changed using up/down keys.
Here is a sample from the old UIControls sample (now inside the ui package from the TotalCrossAPI sample):
figure companion_resources/images/calendar_001.png
This class has the following public attributes, one omitted default constructor, and methods (excluding some fields from its superclasses):
  • canceled true if the user had canceled without selecting.
  • weekNames The 7 week names painted in the control. It defaults to
    {"S","M","T","W","T","F","S"}
    .
  • todayClearCancel The labels for Today, Clear, and Cancel. It defaults to
    {"Today","Clear","Cancel"}
    .
  • yearMonth The labels between the arrows for year and month. It defaults to {"year","month"}.
  • getSelectedDate() Returns a Date object representing the selected date, or null if the calendar is canceled.
  • setSelectedDate(Date d) Changes this calendar box current date by the given one. If the given date is null, the current date is set to today.
To correctly retrieve the selected date, you can handle the ControlEvent.PRESSED event posted by the calendar, and call getSelectedDate().
Sample code:
CalendarBox calendar = new CalendarBox();
​
public void initUI()
{
	calendar.popup();
}
​
public void onEvent(Event event)
{
	if (event.type == ControlEvent.PRESSED && event.target == calendar)
	{
		Date date = calendar.getSelectedDate();
		String text = (date == null)? "No date selected" : date.toString();
		new MessageBox("Selected Date", text).popup();
	}
}

ColorChooserBox

Shows a color dialog that can be used to select a color. There are several ways to choose a color:
  • Using a color matrix.
  • Using a websafe palette.
  • Writting the red, green, and blue components.
  • Writting the color in hexdecimal.
Here’s a sample code:
ColorChooserBox ccb = new ColorChooserBox(getBackColor());
ccb.popup();
if (ccb.choosenColor != -1) // user pressed cancel?
	int color = ccb.choosenColor; // no, color was selected 
The ColorChooserBox looks like the following images taken from the old UIGadgets sample using Android user interface style:
figure companion_resources/images/colorchoser_001.png figure companion_resources/images/colorchoser_002.png

ControlBox

A popup window that displays any control given as parameter to the constructor To add more than one control, use a container. Here is a sample from Litebase AllTests:
figure companion_resources/images/controlbox_001.png

FileChooserBox

A class that shows all folders from a startup one to allow the user select a file or a folder. The file tree is mounted on demand to speed up the process.
Here’s a list of customizations you can do:
  • You can set a path to be selected initially by setting the initialPath property.
  • Set the defaultButton property to allow the selection of an item doing a double-click on it.
Here’s a sample of how to use it:
try
{
	FileChooserBox w = new FileChooserBox("Select the folder",new String[]{" This one "," Cancel "}, 
		new FileChooserBox.Filter()
		{
			public boolean accept(File f) throws IOException
			{
				return f.isDir(); // will only list folders. you may filter by other file types too
			}
		});
	w.mountTree(Settings.appPath,1);
	w.popup();
	return w.getPressedButtonIndex() == 0 ? w.getAnswer() : null;
} 
catch (IOException e)
{
	return null;
} 
The section Tree↑ from chapter “Some Advanced Controls” shows some images of its usage.
This class use a nested interface called FileChooserBox.Filter. This is necessary to indicate that a filter must have the method accept(File f), which must return true if the file is to be added to the tree.

ProgressBox

This class implements a message box that shows a spinner at the left of the text. You can set the spinner color and type before constructing the progress box (usually you do this in your application’s constructor, and not for each progress box created).
Here’s a sample:
ProgressBox pb = new ProgressBox("Message","Loading, please wait...",null);
pb.popupNonBlocking();
... lengthy task
pb.unpop(); 
In the old UIControls sample (now inside the ui package from the TotalCrossAPI sample), there is some examples using ProgressBox. The images order follow the buttons order to show the examples:
figure companion_resources/images/progressbox_001.png
figure companion_resources/images/progressbox_002.png figure companion_resources/images/progressbox_003.png
figure companion_resources/images/progressbox_004.png figure companion_resources/images/progressbox_005.png

TimeBox

Class used to input a time from the user. Correctly handles the AM/PM depending on
Settings.is24Hour
.
When the window closes, a PRESSED event is sent to the caller, and the time can be retrieved using getTime().
The time can be entered also using the arrow keys and by typing the numbers directly.
Here’s a sample:
TimeBox tb;
public void initUI()
{
	try
	{
		(tb=new TimeBox()).popupNonBlocking();
	}
	catch (Exception e)
	{
		e.printStackTrace();
	}
} 
​
public void onEvent(Event e)
{
	if (e.type == ControlEvent.PRESSED && e.target == tb)
		Vm.debug("ret: "+tb.getTime());
} 
It looks like the following image taken from the old UIControls sample (now inside the ui package from the TotalCrossAPI sample):
figure companion_resources/images/timebox_001.png

25 Image

Image is a rectangular image, which you can draw into or copy to a surface (using a Graphics object). They are always 24 bpp, and TotalCross supports only the PNG and JPEG formats when running on the TCVM.
However, you may still use GIF and BMP files on your application, because these formats are also supported when running on Java, and the TotalCross deployer automatically converts those files to 24 bpp PNG files, which are then packaged with your application (along with any other required resources). The deployed PNG may contain transparency information which is correctly handled.
Image objects cannot be directly added to the user interface because they are not controls (i. e. the Image class does not extend the Control class). To display an image on the user interface, you may either use the Graphics object to draw the image on the screen, or use a control that better suits your needs, like ImageControl or Button (both described at the user interface section of this document).
Some transformation methods return a new instance of this image while others apply to the current instance. To preserve an image with a single frame, use getFrameInstance(0).
TotalCross does not support grayscale PNG with alpha-channel. Convert the image to true-color with alpha-channel and it will work fine (the only backdraw is that the new image will be bigger).
Image constructors:
  • Image(int width, int height) Creates an Image object with the given width and height. The new image has the same color depth and color map of the default drawing surface. Here is an example of use:
    Image img = new Image(100,100);
    Graphics g = img.getGraphics();
    g.backColor = Color.WHITE;
    g.fillRect(25,25,50,50);
    ...
    Graphics screenG = getGraphics();
    screenG.drawImage(img,CENTER,CENTER);  
    
  • Image(byte[] fullDescription) Creates an Image object from the given byte array, which must specify the whole image, including its headers. Use only JPEG or PNG images on the devices (GIF and BMP are supported on the desktop only). Here is a code example:
    // create the image and fill it with something
    Image img = new Image(160,160);
    Graphics g = img.getGraphics();
    for (int i =0; i < 16; i++)
    {
    	g.backColor = Color.getRGB(10*i,10*i,10*i);
    	g.fillRect(i*10,0,10,160);
    }  
    // save the bmp in a byte stream
    ByteArrayStream bas = new ByteArrayStream(4096);
    DataStream ds = new DataStream(bas);
    int totalBytesWritten = img.createBmp(ds);
    // parse the saved bmp
    Image im = new Image(bas.getBuffer()); // Caution! the buffer may be greater than totalBytesWritten, but when parsing theres no problem.
    if (im.getWidth() > 0) // successfully parsed?
    {
    	getGraphics().drawImage(im,CENTER,CENTER);
    	Vm.sleep(2000);
    }  
    
Caution: if reading a JPEG file, the original array contents will be changed!
  • Image(String path) Attempts to read the contents of the file specified by the given path, creating an Image object from the bytes read. The path given is the path to the image file. The file must be in 2, 16, 256, 24 bpp color compressed (RLE) or uncompressed BMP bitmap format, a PNG file, a GIF file, or a JPEG file. If the image cannot be loaded, an ImageException will be thrown.
  • Image(Stream s) Attempts to read the contents of the given stream, and create an Image object from the bytes read. Loads a BMP, JPEG, GIF, or PNG image from a stream. Note that GIF and BMP are supported only on the desktop. Note that all the bytes of the given stream will be fetched, even those bytes that may follow the image.
The usage of this last constructor with connection oriented streams (like socket) is not advised.
  • If you want to check if a specific file is supported by the platform at runtime, you may use the static method Image.isSupported(String filename). PNG or JPEG are always supported. GIF and BMP are supported on JavaSE only.
  • To retrieve the image dimensions, you may use the methods getWidth() and
    getHeight()
    . You can check if the image is ok by comparing these values with zero.
  • The method getGraphics() returns the Graphics object used by this image, which provides several methods that may be used for drawing in this image.
  • You may change all pixels of the same color by another color with the method changeColors (int from, int to). The current value of the transparent color is not changed. Using this routine, you can change the colors to any other you want. Note this replaces a single solid color by another solid color. If you want to change a gradient or colorize an image, use the applyColor(int color) method instead, which applies the given color RGB values to all pixels of this image, preserving the transparent color and alpha channel, if set.
The deployer also convert animated GIFs into multi-frame PNGs – which are regular PNG files that contains all frames from the original GIF, along with the number of frames – to be packaged with the application’s tclass file.
In this case, you may use the method getFrameCount() to check if the loaded image contains more than one frame.
However, you may want to load an actual PNG or JPEG file created in multi-frame format to display an animation. In this case, the number of frames contained in the file will be unknown and getFrameCount() will return 1.
To set the actual number of frames of the image, you must use the method setFrameCount (int n), which sets the total number of frames of the image by the given one, but only if the total image width (including all frames) is divisible by the given value.
The image’s frame count cannot be changed if its value is already higher than 1. In this case, the setFrameCount() method simply returns without doing anything.
The following methods should be used only on multi-framed images.
  • getCurrentFrame() Returns the index of the current visible frame.
  • setCurrentFrame(int nr) Sets the given frame index as the current frame, moving its contents to the set of visible pixels. If the given index is negative, the last frame is set as the current frame; if it’s higher than the number of frames, the first one is set instead.
  • nextFrame() Sets the next frame as the current one.
  • prevFrame() Sets the previous frame as the current one.
Both nextFrame() and prevFrame() treat the multi-framed image as a circular list, looping back to the first frame if moving forward from the last frame, or to the last frame if moving backwards from the first frame.
The Image class also provides the methods that creates a new Image object to be changed and returned, instead of changing the original instance:
  • getScaledInstance(int newWidth, int newHeight) Returns a scaled instance of this image. The arguments are the new dimensions for this image in pixels. The algorithm used is the replicate scale: not good quality, but fast.
  • scaledBy(double scaleX, double scaleY) Returns a scaled instance of this image. The new dimensions are calculated based on this image’s dimensions and the given proportions. The algorithm used is the replicate scale: not good quality, but fast. The given values must be > 0.
  • getSmoothScaledInstance(int newWidth, int newHeight, int backColor) Returns a scaled instance of the image using the area averaging algorithm. Transparent pixels are replaced by backColor, which produces a smooth border. Example:
    Image img2 = img.getSmoothScaledInstance(200,200, getBackColor());
    
    On device and JavaSE it uses a Catmull-rom resampling. The reason is that the Catmull-rom consumes more memory and is also slower than the area-average, although the final result is much better.
  • smoothScaledBy(double scaleX, double scaleY, int backColor) Returns a scaled instance of this image. The new dimensions are calculated based on this image’s dimensions and the given proportions. The given values must be > 0. The transparent pixels are replaced by backColor, which produces a smooth border. Example:
    Image img2 = img.smoothScaledBy(0.75,0.75, getBackColor()); 
    
  • getRotatedScaledInstance(int scale, int angle, int fillColor)
    Returns a rotated and/or scaled version of this image. A new Image object is returned which will render the image at the specified scale ratio and rotation angle. After rotation, the empty parts of the rectangular area of the resulting image are filled with the fill color. If color is -1, then the fill color is the transparent color, or white if none. The new image will probably have a different size of the original image. In multi-framed images, each image is rotated/scaled independently.
    scale is a number greater than or equal to 0 stating the percentage of scaling to be performed. 100 is not scaling, 200 doubles the size, and 50 shrinks the image by 2. angle is the rotation angle, expressed in trigonometric degrees and fillColor is the fill color (-1 indicates the transparent color of this image or Color.WHITE if the transparentColor was not set).
    Do not use this method for scaling only, because the scaling methods are faster. If you need a smooth scale and rotate, scale it first with smoothScaledBy() or getSmoothScaledInstance() and rotate it without scaling (or vice-versa).
  • getTouchedUpInstance(byte brightness, byte contrast) Returns a touched-up instance of this image with the specified brightness and contrast.
    brightness
    is a number between -128 and 127 stating the desired level of brightness. 127 is the highest brightness level (white image), while -128 is no brightness (darkest image). contrast is a number between -128 and 127 stating the desired level of contrast. 127 is the highest contrast level, while -128 is no contrast.
    The methods getSmoothScaledInstance() and smoothScaleBy() uses the area averaging algorithm instead of the replication algorithm used by scaleBy() and getScaledInstance(). Although slower, the area averaging algorithm provides better results. You may now create only 320x320 images for your application, and smoothly resizes it to the target resolution, instead of providing one image per resolution.
    Images with anti-aliased borders produce better results because of the extra argument for the background color.
  • smoothScaledFixedAspectRatio(int newSize,boolean isHeight,
    int backColor)
    Returns the scaled instance using fixed aspect ratio for this image, given the scale arguments. The given values must be > 0. This method is useful to resize an image, specifying only one of its sides: the width or the height. The other side is computed to keep the aspect ratio. newSize is the new size (width or height) for the image, if isHeight is true, newSize is considered as the new height of the image; if false, newSize is considered the new width of the image, and backColor is the background color to be used as transparent pixel (for PNG images with alpha-channel, use -1). Example:
    Image img2 = img.smoothScaledFixed(fmH, true, -1); 
    
  • getFadedInstance(int backColor) Creates a faded instance of the image, interpolating all pixels with the given background color. The pixels that match the transparent color will not be changed.
  • getFrameInstance(int frame) In a multi-framed image, returns a copy of the given frame. In a single-framed image, gets a copy of the image.
  • smoothScaledFromResolution(int originalRes, int backColor) Returns a smooth scaled instance of this image with a fixed aspect ratio based on the given resolution (which is the resolution that you used to MAKE the image). The target size is computed as image_size*min(screen_size)/original_resolution. originalRes is the original resolution that the image was developed for (it’s a good idea to create images for 320x320 and then scale them down) and backColor is the background color.
And finally, to save your Image object, you may use one of the methods below:
  • createPng(Stream s) Attempts to write this image as a 24 bpp PNG file on the given stream (if useAlpha is true, it saves as 32 bpp). If you’re sending the PNG through a stream but not saving it to a PDBFile, you can use this method. If you’re going to save it to a PDBFile, then you must use the saveTo() method.
  • saveTo(PDBFile cat, String name) Writes this image as a 24 bpp PNG file on the currently selected record of the given PDBFile, using the given name. The stored image size is limited to near 64 Kb. Note that a stored image size has no relation to its size in pixels. For example, a 1300x1200 completely-white PNG file takes 7 Kb of storage size but 6 MB of RAM when loaded.
    • The PDBFile can save multiple images, but the record must be prefixed with the image’s name and must be sorted.
    • This method finds the exact place where to insert the PNG and puts it there.
    • If you want to create a PNG to be transfered by a stream to serial or socket then you must use the method createPng() instead.
    • If a record with this name already exists, it will be replaced.
    • The name is always converted to lowercase and the method makes sure that PNG is appended to it.
    • To get the list of images in a PDBFile, just do a readString() at the beginning of each record.
    • To retrieve the image, use the loadFrom(PDBFile cat, String name) method.
    Here is a sample code:
    // create the image and paint over it
    Image img = new Image(100,100);
    Graphics g = img.getGraphics();
    g.backColor = Color.getRGB(100,150,200);
    g.fillRect(25,25,50,50);
    g.foreColor = Color.WHITE;
    g.drawCircle(50,50,20);  // create the PDBFile to save the image. You must change CRTR to match your apps creator ID
    String pdbName = "images.CRTR.TYPE";
    PDBFile pdb = new PDBFile(pdbName, PDBFile.CREATE);
    img.saveTo(pdb, "boxcircle.png");
    pdb.close();  // load the previously created image
    PDBFile pdb = new PDBFile(pdbName, PDBFile.READ_WRITE);
    add(new ImageControl(Image.loadFrom(pdb,"boxcircle.png")),CENTER,CENTER);
    pdb.close();  
    
    Here’s a code that lists the images in a PDB (saved using this method).
    public static String[] list(PDBFile cat) throws IOException  
    {
    	DataStream ds = new DataStream(cat);
    	int n = cat.getRecordCount();
    	String[] names = new String[n];
    	for (int i =0; i < n; i++)
    	{
    		cat.setRecordPos(i);
    		names[i] = ds.readString();
    	}
    	return names;
    } 
    
Some other Image public methods (omitting the ones from its superclass):
  • getPixels() Used only on the desktop to get the image’s pixels; NOT AVAILABLE on the device (it will throw a NoSuchMethodError).
  • getX()getY() Returns 0.
  • equals(Object o) Returns true if the given Image object has the same size and RGB pixels of the original one. The alpha-channel is ignored.
  • applyColor2(int color) Applies the given color RGB values to all pixels of this image, preserving the transparent color and alpha channel, if set. This method is used to colorize the Android buttons.
Here’s an example of how to create buttons for several resolutions based on a 320x320 images: (adapted from the old UIGadgets sample)
public void initUI()
{
	setTitle("ImageButton resolution scale");
	String[] imageNames = {"clear.gif", "go.gif"};
	int imgRes = 320;
	int targetRes[] = {160, 176, 240, 320};
	int backColor = getBackColor();
	int coordX = LEFT;
​
	try
	{
		for (int i = imageNames.length - 1; i >= 0; i--)
		{
			Image img = new Image(imageNames[i]);
			int imgWidth = img.getWidth();
			int coordY = TOP;
			for (int j = targetRes.length - 1; j >= 0; j--)
			{
				double factor = (double) targetRes[j] / (double) imgRes;
				Image img2 = img.smoothScaledBy(factor, factor, backColor);
				Button btn = new Button(img2);
				btn.setBorder(Button.BORDER_NONE);
				add(btn, coordX, coordY);
				coordY += img2.getHeight() + 5;
			}
			coordX += imgWidth + 5;
		}
	}
	catch (Exception e)
	{
		MessageBox.showException(e, true);
	}
}
Another example with an animated GIF: (adapted from the old GifAnimatedTest sample now inside the ui package from the TotalCrossAPI sample):
public void initUI()
{
	try
	{
		img = new Image("alligator.gif");
		imgCtrl = new ImageControl(img);
		add(imgCtrl, CENTER, CENTER);
		addTimer(200);
	}
	catch (Exception e)
	{
		MessageBox.showException(e, true);
	}
}
public void onEvent(Event event)
{
	if (event.type == TimerEvent.TRIGGERED)
	{
		img.nextFrame();
		imgCtrl.repaintNow();
	}
}
Note that it will work in the same way on desktop or on device.

26 Camera

This class is used to enable the camera of the underlying device. The following platforms are supported: Android and iOS. It is not possible to use the webcam on PC platforms (JavaSE, Windows XP, Vista, Seven, 8, and Linux).
Note that you can easily rotate the image to put it in portrait mode, using the Image.
getRotatedScaledInstance()
method, after retrieving the image. You may change the following options: initialDir, defaultFileName (must end with .jpg), and resolutionWidth x resolutionHeight (possible values are 320x240, 640x480, 1024x768, 2048x1536; different values defaults to 640x480). All other options are ignored.
On Android you can set the defaultFileName, stillQuality, resolutionWidth and resolutionHeight. All other options are ignored. You can call the
getSupportedResolutions() method to see the resolutions that are available on the device.
On iOS there’s no way to return the supported resolutions; it will take a photo using the default camera’s resolution, and then will resize to the resolution defined in resolutionWidth x resolutionHeight, keeping the camera’s aspect ratio. On iOS you can specify the
defaultFileName
with a path or just the name, or use a system-generated name. On iOS it is not possible to record a movie, only to take pictures.
This class only has de default constructor. The other interesting fields are:
  • title The title to display in the window opened for the camera.
  • stillQuality Defines the quality of the image. It can be equal to
    CAMERACAPTURE_STILLQUALITY_DEFAULT (default quality),
    CAMERACAPTURE_STILLQUALITY_LOW (low quality),
    CAMERACAPTURE_STILLQUALITY_NORMAL (normal quality), or
    CAMERACAPTURE_STILLQUALITY_HIGH (high quality).
  • videoType Can be one of
    CAMERACAPTURE_VIDEOTYPE_ALL (produces video clips that match video profiles, using just the video resolution for the match criteria, the default value)
    CAMERACAPTURE_VIDEOTYPE_STANDARD
    (produces high-quality video clips used for home movies and e-mail video messaging, using a video encoder such as the Windows Media encoder), or
    CAMERACAPTURE_VIDEOTYPE_MESSAGING (Produces video clips used for Multimedia Messaging Service (MMS) video messaging, which require a video encoder that conforms to the 3rd Generation Partnership Project (3GPP) specification on
    http://go.microsoft.com/fwlink/?LinkId=32710).
  • videoTimeLimit Maximum time limit for recording a video.
  • captureMode Can be one of
    CAMERACAPTURE_MODE_STILL (only picture, the default value),
    CAMERACAPTURE_MODE_VIDEOONLY (no sound), or
    CAMERACAPTURE_MODE_VIDEOWITHAUDIO (video and sound).
  • allowRotation Use this on Android only. If false, the camera buttons will be on landscape. If true, the camera buttons will follow the device current rotation when the camera is opened.
The class Camera only has one method:
  • click() Takes a photo or records a video based on the members set. It returns a string with the file name where the image or video is located, or null if the user canceled.
  • getSupportedResolutions() Gets the supported resolutions on the current device.

Part III. UTILITY CLASSES

Overview

This section covers utility classes to deal with date and time, random number generation, data structures, logging and conversions.

27 totalcross.util

For more details about each class, please read their JavaDocs.

Date

The Date class is a general date data type implementation of the Gregorian Calendar, and supports dates from January 1st, 1000 to December 31th, 2999. It checks to make sure that the dates that are instanciated or changed exist and if they don’t an exception (InvalidDateException) is thrown. It provides methods to advance the date backwards and forwards by increments of day, week, and month. It provides comparisons =,>,<. It also provides constants and methods for date manipulation and comparison.
Months
Date.JANUARY
Date.FEBRUARY
Date.MARCH
Date.APRIL
Date.MAY
Date.JUNE
Date.JULY
Date.AUGUST
Date.SEPTEMBER
Date.OCTOBER
Date.NOVEMBER
Date.DECEMBER
  • Date has six constructors:
    • Date() Creates a Date object set with today’s date.
    • Date(int sentDate) Creates a Date object using the given value, which must be in the format YYYYMMDD.
    • Date(int sentDay, int sentMonth, int sentYear) Creates a Date object using the given values, where sentDay must be an integer between 1 and the last day in the month, sentMonth must be an integer between 1 and 12, and sentYear must be an integer between 1000 and 2999.
    • Date(String strDate) Creates a Date object with the given strDate, which must be a valid date in the current device date format (See Settings.dateFormat). The date separator can be any non-number character; the constructor auto-detects what character is being used as separator.
    • Date(String strDate, byte dateFormat) Creates a Date object with the given strDate, which must be a valid date in the given dateFormat. The argument dateFormat must be one of the Settings.DATE_XXX constants, where DATE_MDY stands for month, day, and year format, DATE_DMY stands for day, year, and month format, and DATE_YMD stands for year, month, and day format.
    • Date(Time t) Creates a Date object using the given Time object (See section Time↓).
    Except for the default constructor, all Date constructors may throw the checked exception InvalidDateException.
  • Date objects can be manipulated in a simple way with the following methods:
    • advance(int numberDays) Advances (or regresses if numberDays is negative) the date by the given value.
    • advanceMonth() Advances the date to the beginning of the next month.
    • advanceMonth(boolean direction) Changes the date to the beginning of the next or previous month. The static variables Date.FORWARD or Date.BACKWARD instructs the method to either move to the next or previous month.
    • advanceWeek() Advances the date to the beginning of the next week.
    • advanceWeek(boolean direction) Changes the date to the beginning of the next or previous week. The static variables Date.FORWARD or Date.BACKWARD instructs the method to either move to the next or previous week.
    • setToday() Sets this Date object to the current day.
    • set(int day, int month, int year) Sets the date fields to the given ones and returns an integer in the format YYYYMMDD.
    • set(String strDate, byte dateFormat) Sets the date fields by parsing the given string, and using the given dateFormat. If you want to use the default date format, use Settings.dateFormat. Trailing spaces are skipped. It returns an integer in the format YYYYMMDD.
  • Date objects can be easily compared using the following methods:
    • isAfter(Date sentDate) Returns true if sentDate is after this Date object.
    • isBefore(Date sentDate) Returns true if sentDate is before this Date object.
    • equals(Object sentDate) Returns true if sentDate represents the same Date as this Date object.
    • compareTo(Object other) Implementation of the Comparable interface. Return is > 0 if this object is greater than the other one, < 0 if its smaller, and 0 if they are equal.
    You can get the difference in days between two dates using the method subtract(Date other) (other – this).
    Never use subtract() to compare dates. Using subtract() to compare dates can be 15 times slower than using one of the above methods, like compareTo(). Use subtract() only when you need to know the exact difference between two dates.
  • The following methods can be used to retrieve information about the set date:
    • getDay() Returns the day.
    • getMonth() Returns the month.
    • getYear() Returns the year.
    • getDayOfWeek() Returns the day of week, where 0 is Sunday and 6 is Saturday.
    • getWeek() Calculates and returns the ordinal value of the week (1-52).
    • getDateInt() Returns an integer representing the date in the format YYYYMMDD.
    • getDaysInMonth() Returns the number of days the set month has.
    • getDaysInMonth(int month) Returns the number of days in the given month of the set year.
    • getDaysInMonth(int month, int year) Static method which returns the number of days in the passed month and year.
    • getMonthName(int m) Static method that returns the string representation of the month passed, that is monthNames[m] if Date.JANUARY <= m and m <= Date.
      DECEMBER
      . It returns an empty string if m is out of the valid bounds.
    • getGregorianDay() Returns the number of days since the January 1st of the epoch year 1000.
    • isLeapYear(int year) Static method that checks if the year is a leap year.
  • You can also retrieve the date as a string:
    • formatDayMonth() Returns a zero padded string representation of the day and month, using the current device settings (see Settings.dateFormat).
    • formatDate(int day, int month, int year) Formats the date specified with Settings.dateFormat, zero padded. The date separator used is Settings.
      dateSeparator
      .
    • formatDate(int day, int month, int year, String separator)
      Formats the date specified with Settings.dateFormat, zero padded, and using the given separator.
    • formatDate(int day, int month, int year, byte dateFormat)
      Formats the date specified with the dateFormat parameter, zero padded. The date separator used is Settings.dateSeparator.
    • formatDate(int day,int month,int year,byte dateFormat,String
      dateSeparator)
      Formats the date specified with the dateFormat parameter, zero padded, and using the given separator.
    • toString() Returns a string representation of the date using the current device settings (see Settings.dateFormat). The date separator used is Settings.
      dateSeparator
      .
    • toString(byte format) Returns a string representation of the date using the given format, which must be one of the Settings.DATE_XXX constants. The date separator used is Settings.dateSeparator.
    • toString(byte dateFormat, String separator) Returns the date in a string format and using the given format and separator.
  • The Date class has the field monthNames, which is the month names used in some routines. You can localize it if you wish. Its default value is: {"", "January", "February", "March", "April", "May", "June", "July", "August", "September",
    "October", "November", "December"}
    .
Sample code:
Date PaulBorn = null;
Date DavidBorn = null;
​
try
{
	PaulBorn = new Date(23, 12, 1960);
	DavidBorn = new Date(20, 7, 1978);
}
catch (InvalidDateException e)
{
	MessageBox.showException(e, true);
}
new MessageBox("Paul was born in " + Date.getMonthName(PaulBorn.getMonth())).popup();
if (PaulBorn.isAfter(DavidBorn))
	new MessageBox("David is older than Paul").popup();
else
	new MessageBox("Paul is older than David").popup();
PaulBorn.setToday(); // Now Paul has just been born.

Vector

The Vector class implements a growable array of objects. Like an array, it contains components that can be accessed using an integer index. However, the size of a Vector can dynamically grow as needed, but it never shrinks. If the array never needs to be increased, an array should be used.
In Java you cannot directly access the Vector’s internal array, you must use its methods to insert(), remove(), retrieve(), or replace() elements in a Vector. This way the programmer cannot accidentally (or intentionally) corrupt the object’s internal state.
Unfortunately this makes Vector iteration a very costly operation, because each iteration requires at least one method call.
To avoid this performance issue, TotalCross’ Vector implementation allows public access to its internal array (items). However, to guarantee the Vector’s integrity you must only access it to retrieve (get) or replace (set) elements, and never to insert, remove, or resize the buffer.
You may also use a Vector as a stack, whose feature is better explained below.
Here is an example showing a vector being used:
...
Vector vec = new Vector();
vec.addElement(obj1);
vec.addElement(obj2);
...
vec.insertElementAt(obj3, 3);
vec.removeElementAt(2);
if (vec.size() > 5)
   ... 
  • Vector has three constructors:
    • Vector() Default constructor, which creates a Vector with the default size 8. This constructor should be avoided.
    • Vector(int size) Creates a Vector with the given size, which is the initial size of the vector’s internal object array and cannot be negative (the minimum size is 0). The vector will grow as needed when objects are added.
    • Vector(Object[] startingWith) Creates a Vector, assigning the given array as the internal buffer and its size as the Vector size. Note that the given array must not have null elements.
    Please notice this constructor uses the given array as internal buffer, not a copy. So you should not use this array after initializing the Vector with it. If you want to initialize the Vector with a copy of the array, use Vm.arrayCopy(), or for string arrays, Convert.cloneStringArray().
  • To add an element to a vector, you may use:
    • addElement(Object obj) Adds a reference to the given object to the end of the Vector.
    • addElements(Object[] objects) Adds an array of objects at the end of the vector.
    • addElementsNotNull(Object[] objects) Adds an array of objects at the end of the vector (null objects are skipped).
    • insertElementAt(Object obj, int index) Inserts a reference to the given object at the specified index of this Vector, shifting elements with index greater or equal to the specified index to have an index one greater than it had previously. If index is less than 0 or above the number of elements, the object is inserted at the end.
  • And to remove:
    • removeElement(Object obj) Removes the reference of the given object from this Vector, shifting elements with index greater than the specified index to have an index one smaller than it had previously.
    • removeElementAt(int index) Removes the reference to the object at the given index of this Vector, shifting elements with index greater than the specified index to have an index one smaller than it had previously.
    • removeAllElements() Removes all elements from this Vector, reseting its length to 0. Note that this method sets all items in this vector to null, so, if you had directly assigned an array to this vector, all items inside it will be nulled.
  • To find the index of a Vector’s element:
    • indexOf(Object obj, int startIndex) Returns the index of the given object, or -1 if the object is not found. The array is searched using an O(n) linear search, starting at the given start index, until the object is found or the end of the array is reached.
    • indexOf(Object obj) Like the above, but always start the search at the first position (index 0). In fact its implementation consists of a single method call: indexOf(obj, 0).
  • Vector also provides the following handy methods:
    • isEmpty() Returns true if the Vector is empty.
    • size() Returns the number of elements in the Vector.
    • setSize(int newSize) Sets the size of the Vector. If the new size is greater than the current size, new null items are added to the end of the Vector. If the new size is less than the current size, all components at index newSize and greater are discarded (set to null). The new size can’t be negative.
    • qsort() Sorts the elements of the Vector.
      If the elements are strings, the sort will be faster because a cast to String will be done. Otherwise, it will use the method toString() to get a String representation of the object to use on the sort.
    • toObjectArray() Returns an array of objects containing all elements of the Vector, or null if the Vector is empty.
      If the first element of the Vector is a string, it will assume all elements are of the same type and create a String array instead of an Object array. In this case, you may safely cast the return from Object[] to String[]. This is only valid for strings.
    • dump() Dumps the contents of the Vector, returning a string representation of it. If the number of elements is big, it can take a lot of memory!
    • toString() Returns the items of the vector separated by comma in a string.
    • toString(String separator) Returns the items of the vector separated by the given string separator in a string.
    • copyInto(Object[] out) Copies the items of the vector into the given array, which must have at least the current size of the vector. If the output vector is greater than the current size, the remaining positions will remain unchanged.
    • contains(Object o) Returns true if the vector contains the specified element.
    • reverse() Reverses the order of the elements in the vector. In a vector with n elements, the element of index 0 is moved to the index n-1, the element of index 1 is moved to the index n-2, and so on.
  • To use a Vector as a stack, you may use the following methods:
    • push(Object obj) Pushes an item onto the top of the stack. It actually just calls the method addElement() passing the given object.
    • pop() Removes the object at the top of the stack, returning that object.
    • pop(int n) Pops n last elements from the stack.
    • peek() Returns the object at the top of the stack without removing it.
    • peek(int n) Returns the n-last object, without removing it. Note that peek(0) is the same of peek(). n is how many elements to get from the top and must be a positive number.
Although Vector can grow to accommodate more elements, this is a costly operation that should be avoided. Keep in mind the following when using Vectors:
  • If you can estimate the number of elements, you can create the Vector object with enough room for them:
    // we expect at least 1000 elements
    Vector v = new Vector(1100) // a little extra space just in case
    Foo f;
    while ((f = getNextFoo()) != null)
    	v.addElement(f);
    
  • If you know the exact number of elements, you can create an array and initialize the Vector object with this array:
    // we can get the exact number of elements we’ll receive
    int fooCount = getFooCount();
    Foo[] f = new Foo[fooCount];
    for (int i = 0 ; i < fooCount ; i++)
    	f[i] = getNextFoo();
    Vector v = new Vector(f);
    
    Use this approach only when you know the initial number of elements is not too low and you do not expect to make several insertions later. Otherwise the Vector may need to grow several times, decreasing the performance.
  • The Vector growth rate is 20% + 1 on device (100% + 1 on Java), so you should avoid using the default constructor (the default size is 8) or initializing the Vector with less than 20 positions.
  • Vector does not shrinks automatically. If you make several inserts followed by even more removals, you may end up with a huge Vector consuming more memory than it actually needs (i. e. after the inserts the Vector ends up with 1200 positions but after the removals only 500 positions are actually being used).

IntVector

The IntVector is a growable array of integers. It’s a Vector implementation specific for integers, which provides better performance and takes less memory than using Vector to hold only integers.
For efficiency, IntVector was implemented exactly like Vector: it also allows public access to its internal array (items), but you must only access it to retrieve (get) or replace (set) elements, and never to insert, remove, or resize the buffer.
Like Vector, IntVector may also be used as a stack (an integer stack in this case). It can also be used as a bit array. These features are better described below.
  • IntVector has three constructors:
    • IntVector() Default constructor, creates an IntVector with the default size 20.
    • IntVector(int size) Creates an IntVector with the given size, which MUST BE greater than 0.
    • IntVector(int[] items) Creates an IntVector, assigning the given array as the internal buffer and its size as the an IntVector size.
    Please notice that this constructor uses the given array as internal buffer, not a copy. So you should not use this array after initializing the IntVector with it. If you want to initialize the IntVector with a copy of the array, use Vm.arrayCopy().
  • To add an element to an IntVector, you may use:
    • addElement(int item) Adds the given integer to the end of the intVector.
    • addElements(int[] elements) Appends an array of integers at the end of the intVector.
    • insertElementAt(int item, int index) Inserts the given integer at the specified index of this IntVector, shifting elements with index greater or equal to the specified index to have an index one greater than it had previously. If index is less than 0 or above the number of elements, the new item is inserted at the end.
  • And to remove:
    • removeElement(int item) Removes the given integer from this IntVector, shifting elements with index greater than the specified index to have an index one smaller than it had previously.
    • removeElementAt(int index) Removes the integer at the given index of this
      IntVector
      , shifting elements with index greater than the specified index to have an index one smaller than it had previously.
    • removeAllElements() Removes all elements from this IntVector, reseting its length to 0 and setting all elements to 0.
  • To find the index of an element of this IntVector:
    • indexOf(int item, int startIndex) Returns the index of the given integer, or -1 if the integer is not found. The array is searched using an O(n) linear search, starting at the given start index, until the integer is found or the end of the array is reached.
    • indexOf(int item) Like the above, but always start the search at the first position (index 0). In fact its implementation consists of a single method call: indexOf(item, 0).
  • IntVector also provides the following handy methods:
    • isEmpty() Returns true if the IntVector is empty.
    • size() Returns the number of elements in the IntVector.
    • qsort() Sorts the elements of the IntVector.
    • toIntArray() Returns an array of integers containing all elements of the IntVector.
      Unlike Vector.toObjectArray(), the method toIntArray() does not return null if the IntVector is empty. It always returns an integer array, which may contain elements or not.
    • setSize(int newSize) Sets the size of the IntVector. If the new size is greater than the current size, new items equal to zero are added to the end of the IntVector. If the new size is less than the current size, all components at index newSize and greater are discarded (set to 0). The new size can’t be negative.
    • copyInto(int[] out) Copies the items of the vector into the given array, which MUST have at least the current size of this vector. If the out vector is greater than the current size, the remaining positions will remain unchanged.
    • contains(int v) Returns true if the vector contains the specified element.
    • reverse() Reverses the order of the elements in the vector. In a vector with n elements, the element of index 0 is moved to the index n-1, the element of index 1 is moved to the index n-2, and so on.
  • To use an IntVector as a stack, you may use the following methods:
    • push(int item) Pushes an item onto the top of the stack. It actually just calls the method addElement() passing the given integer.
    • pop() Removes the item at the top of the stack, returning that integer.
    • pop(int howMany) Removes the desired number of items from the top of the stack. Unlike the previous method, nothing is returned.
    • peek() Returns the item at the top of the stack without removing it.
When we want to deal with bit values, we usually use a boolean array. However, each position of a boolean array takes 1 byte of memory instead of 1 bit. This can be a problem when you need to deal with large arrays of bits.
A single boolean variable is stored in memory as an integer, which costs 4 bytes. But in a boolean array, each position takes 1 byte instead of 4 bytes.
As an alternative, you may use the IntVector as a real bit array using the provided methods and save a lot of memory. But there is a drawback: the internal array does not grow by itself when you try to access an index bigger than the array’s size. So you must make sure the IntVector is large enough to hold the number of bits you want to manipulate, otherwise you may get an ArrayIndexOutOfBoundsException.
  • The IntVector provides the following methods to be used as a bit array:
    • ensureBit(int sizeInBits) Makes sure the IntVector is large enough to hold the given number of bits. You should always call this method before using it as a bit array. After that, you can safely use the two methods below. This must be done because those methods do not check the bounds of the array.
    • setBit(int index, boolean on) Sets the bit at the given index if on is true; unsets it, otherwise.
    • isBitSet(int index) Returns true if the bit at the given index is set; false, otherwise.
Sample code of the IntVector as a bit array:
  • We need to hold 40000 bits, let’s do it with a boolean array:
boolean[] bitArray = new boolean[40000]; // That’s 40000 bytes
bitArray[5] = true; // easy to set a bit, just use the index.
boolean aux = bitArray[5]; // easy to get a bit.
  • Now using an IntVector:
IntVector bitVector = new IntVector();
bitVector.ensureBits(40000); // First we make sure the array is large enough. 
​
// This time our array uses only 1251 bytes.
bitVector.setBit(5, true); // just as easy to set
boolean aux = bitVector.isBitSet(5); // just as easy to get.

Hashtable

This class implements a hash table, which is a data structure that associates keys with values. It’s primary operation is a lookup: given a key, finds the corresponding value. It works by transforming the key into a hash using the method hashCode() (inherited from Object), which is then used as an index for the hash table.
Hash tables should be used when the number of search operations greatly supersedes the number of insertions.
Any non-null object can be used as a key or as a value, optionally, you can provide your own hash code instead of providing a key from which the hash code will be calculated.
An instance of Hashtable has two parameters that affect its efficiency: its capacity and its load factor. The load factor should be between 0.0 and 1.0. When the number of entries in the hash table exceeds the product of the load factor and the current capacity, the capacity is increased by calling the rehash() method. Larger load factors use memory more efficiently, at the expense of larger expected time per lookup.
If many entries are to be made into a hash table, creating it with a sufficiently large capacity may allow the entries to be inserted more efficiently than letting it perform automatic rehashing as needed to grow the table.
  • Hashtable has two public fields:
    • collisions Computes the number of collisions for a set of inserts. You must zero this each time you want to compute it. Keep in mind that the lower collisions is better, but don’t waste too much memory if it’s too high.
    • allowDuplicateKeys Setting this to true will allow the hashtable to have more than one key with the same value. In this case, the methods will always return the first matching key.
  • Hashtable has four constructors:
    • Hashtable(int initialCapacity) Creates an empty hash table with the specified initial capacity and default load factor of 0.75f. If initialCapacity is zero, it is changed to 5.
      The argument initialCapacity’s value should be the final number of entries in the hash table to avoid rehashing. The hash table will grow if necessary, but using a number near or above the final size can improve performance.
    • Hashtable(int initialCapacity, double loadFactor) Creates a new,
      empty hashtable with the specified initial capacity and the specified load factor, which must be between 0.0 and 1.0. If initialCapacity is zero, it is changed to 5.
    • Hashtable(String res) Creates a new hash table parsing the elements from the given string. The string must be in the form: key = value, split in lines (\n). This aids the task of creating resource bundles to add localization to your application. For example:
      // save these two lines in a file named EN.txt:  
      Message = Message  
      TestMsg = This is a test  
      Exit = Exit  
      // save these other two in a file named PT.txt:  
      Message = Mensagem 
      TestMsg = Isso e um teste 
      Exit = Sair 
      
      The TotalCross deployer will include the two files referenced below in the .tcz file. Now, when your program starts, you can do:
      String txt = idiom == EN? "EN.txt" : "PT.txt";
      byte[] b = Vm.getFile(txt);
      Hashtable res = new Hashtable(new String(b,0,b.length));
      new MessageBox(res.get("Message"), res.get("TestMsg"), new String[]{res.get("Exit")}).popupNonBlocking();
      
      Note that the keys are case sensitive, and that all strings are trimmed.
    • Hashtable(Object[] keys, Object values) Creates a Hashtable with the given keys and values. The values can be two things:
    1. An Object array (Object[]). In this case, the number of keys and values must match.
    2. A single Object. This object is set as value to all keys.
      The values parameter cannot be null.
  • To insert a new entry on the hash table:
    • put(Object key, Object value) Maps the given value to the hash code of the given key. Neither the key nor the value can be null. The value can be retrieved by calling the get() method with a key that is equal to the original key. It returns the previous value of the specified key in the hash table, or null if it did not have one.
    • put(int hash, Object value) Maps the given value to the given hash. The value can’t be null and can be retrieved by calling the get() method with a key that is equal to the original key. It returns the previous value of the specified key in the hash table, or null if it did not have one.
      This method receives a hash code instead of the object. You MUST use the get(int) method to retrieve the value, otherwise you will get a NullPointerException, because no key is stored using this method.
      If the given hash (or key’s hash code) already exists in the hash table, the old value will be replaced by the given value (unless allowDuplicateKeys is true) and the put() method will return the old value. Otherwise, a null value will be returned.
  • To test if a key is already mapped in the hash table, use the method exists(Object key).
  • To retrieve a value from a hash table:
    • get(Object key) Returns the value to which the specified key is mapped in the hash table. It returns null if the key is not mapped to any value in the hash table.
    • get(Object key, Object defaultValue) Returns the value to which the specified key is mapped in the hash table. It returns defaultValue if the key is not mapped to any value in the hash table.
    • get(int hash) Returns the value to which the specified hash is mapped in the hash table. Caution: since you’re passing an integer instead of an object, if there are two objects that map to the same key, this method will always return the first one only. It returns null if the key is not mapped to any value in the hash table.
    • get(int hash, Object defaultValue) Returns the value to which the specified hash is mapped in the hash table. Caution: since you’re passing an integer instead of an object, if there are two objects that map to the same key, this method will always return the first one only. It returns defaultValue if the key is not mapped to any value in the hash table.
    • getString(Object key) Returns the value to which the specified key is mapped in this hash table as a string. If the item is a string, a cast is made, otherwise, the toString() method is called. It returns null if the key is not mapped to any value in the hash table.
    • getString(Object key, String defaultValue) Returns the value to which the specified key is mapped in the hash table as a string. If the item is a atring, a cast is made, otherwise, the toString() method is called. It returns defaultValue if the key is not mapped to any value in the hash table.
  • To retrieve all values and/or keys from a hash table:
    • getKeys() Returns a Vector with all the keys in the hash table. The order is the same of the getValues() method.
    • getValues() Returns a Vector with all the values in the hash table. The order is the same of the getKeys() method.
    • getKeyValuePairs(String separator) Returns a Vector with all the key/value pairs in the form key + separator + value. Each vector’s element can safely be casted to string and separator sould be :, =, etc.
    • copyInto(Hashtable target) Copies the keys and values of the hash table into the given target hash table. Note that the target hash table is not cleared; you should do that by yourself.
    • dumpKeysValues(StringBuffer sb,vString keyvalueSeparator,
      String lineSeparator)
      Dumps the keys and values into the given StringBuffer, where keyvalueSeparator is the separator between the key and the value (e. g. :) and lineSeparator is the separator placed after each key+value pair (e. g. \r\n). The last separator is cut from the StringBuffer.
  • The method size() returns the number of keys in the hash table.
  • To remove a key and its corresponding value from the hash table, use:
    • remove(Object key) Removes the key (and its corresponding value) from the hash table. This method does nothing if the key is not in the hash table. It returns the value to which the key had been mapped in the hash table, or null if the key did not have a mapping.
    • remove(int hash) Removes the key (and its corresponding value) from the hash table. This method does nothing if the key is not in the hash table. It returns the value to which the key had been mapped in the hash table, or null if the key did not have a mapping.
  • To clear the hash table, removing all its entries, use clear().
  • The Hashtable class has a nested class called Hashtable.Entry. It has the following public fields:
    • hash The hash code of a key.
    • key A key in the hash table.
    • value The value mapped to this key.
    • next Another entry in the hash table.
This example creates a hash table of numbers. It uses the name of the numbers as keys:
Hashtable numbers = new Hashtable(13);
numbers.put("one", Convert.toString(1));
numbers.put("two", Convert.toString(2));
numbers.put("three",Convert.toString(3));
To retrieve a number, use the following code:
String n = (String)numbers.get("two");
if (n != null)
	new MessageBox("two = " + Convert.toInt(n)).popup(); 

IntHashtable

This class implements a hash table where both the keys and values inserted must be integers.
  • Its public fields are similar to the ones from Hashtable. In allowDuplicateKeys, set it to false to throw a IntHashtable.DuplicatedKeyException if you add a key that already exists. It’s very unusually to have two objects with same key, but it can occur. This is good to improve the program’s correctness.
  • It also has an Entry nested class. However, diferently from Hashtable, it does not have the field hash and key is an integer instead of an object.
  • IntHashtable has two constructor:
    • IntHashtable(int initialCapacity) Creates an empty hash table with the specified initial capacity and default load factor of 0.75f. The initial capacity must be the number of elements you think the hash table will end with. The hash table will grow if necessary, but using a number near or above the final size can improve performance. If initialCapacity is zero, it is changed to 5.
    • IntHashtable(int initialCapacity, double loadFactor) Same as above but receiving a load factor which must be between 0.0 and 1.0.
  • To insert a new entry on the hash table:
    • put(int key, int value) Maps the given value to the given key.
    • put(Object key, int value) Takes out the hash code from the given key object (object.hashCode()) and calls the previous method. To increase safeness, set allowDuplicateKeys to false.
    If the given hash (or key’s hash code) already exists in the hash table, the old value will be replaced by the given value (unless allowDuplicateKeys is true) and the put() method will return the old value (or the object hash code when inserting an object). Otherwise, the given value will be returned. If allowDuplicateKeys is false and a duplicated key is inserted, IntHashtable.DuplicatedKeyException is thrown.
    The value can be retrieved by calling the get() method with a key that is equal to the original key.
  • To test if a key is already mapped in the hash table, use the method exists(int key).
  • To retrieve a value from a hash table:
    • get(int key) Returns the value to which the specified key is mapped in this hash table. It throws an ElementNotFoundException when the key was not found.
    • get(Object key) Returns the value to which the specified key hash code (key.
      hashCode()
      ) is mapped in this hash table. It throws an ElementNotFoundException when the key was not found.
    • get(int key, int def) Returns the value to which the specified key is mapped in this hash table, or the given default value (def) if the key is not found.
      Make sure you choose a value for def that is never inserted as a value in the hash table, otherwise the return value of this method may be treated as a false negative (i. e. the key maps to a value equals to the given def, thus, if you test if the return is not equals to def to check if the key was found, the result will be a false negative).
  • To retrieve all keys and values from a hash table:
    • getKeys() Returns an IntVector with all the keys in the hash table. The order is the same of the getValues() method.
    • getValues() Returns an IntVector with all the values in the hash table. The order is the same of the getKeys() method.
  • You may use the method getKey(int pos) to retrieve the key at the given position (pos). An ArrayIndexOutOfBoundsException is thrown if the given position is out of range. Note that the first key has no relation with the smallest key.
  • The method size() returns the number of keys in the hash table.
  • To remove a key and its corresponding value from the hash table, use remove(int key). This method throws an ElementNotFoundException when the key was not found.
  • To clear the hash table, removing all its entries, use clear().
  • The method incrementValue(int key, int amount) increments the value of a key by the given amount. If the key doesn’t exist, a new one is created with the amount. Otherwise, its value is changed by the amount. This method is useful to use an IntHashtable as a multi counter.
This example creates a small hash table of numbers:
IntHashtable numbers = new IntHashtable(10);
numbers.put(1, 1000);
numbers.put(2, 2000);
numbers.put(3, 3000);
To retrieve a number, use the following code:
try
{ 
	int i = numbers.get(2);
	new MessageBox("two = " + i).popup();
}
catch(ElementNotFoundException e)
{
	MessageBox.showException(e, true);
}
Another example is to count the number of collisions so that it is possible to chose the best IntHashtable size (a similar code can be used for Hashtable):
int max = 0xFFFFFFF;
for (int h = 5; ; h++)  
{     
	IntHashtable ht = new IntHashtable(h);
	ht.put("nbsp".hashCode(),’ ’);
	ht.put("shy".hashCode(),’-’);
	ht.put("quot".hashCode(),’"’);
	...     
	if (ht.collisions < max)
	{
		Vm.debug("h: "+h+" colli: "+ht.collisions);
		max = ht.collisions;
		if (max == 0)
			break;
	}
}  

Properties

Properties can be used to store properties pairs (key, value) using a hash table. Currently, the key must be a string and the value must be an instance of Properties.Value.
The properties can be saved and loaded to/from a DataStream.
Here’s a sample:
Properties props = new Properties();
File file;
if (existe_arquivo(arquivo))
{
	file = new File(arquivo, File.READ_WRITE);
	props.load(new DataStream(file));
	file.close();
}         
props.put(propriedade,new Properties.Str(valor));
file = new File(arquivo, File.CREATE);
props.save(new DataStream(file));
file.close();      
  • To avoid that the load() method gets into an infinite loop if the file is empty or corrupted, the constant MAX_PROPS limits the number of properties to 1000. If you are saving more than 1000 properties, just change this maximum value.
  • Properties has the following constructors:
    • Properties() Default constructor, creates an empty Properties object.
    • Properties(String[] keys, Properties.Value[] values) Creates a
      Properties
      object, storing the given keys/values pairs.
  • The following methods are provided to handle a Properties objects and its entries:
    • put(String key, Properties.Value v) Maps the given value to the given key in the Properties.
    • get(String key) Returns the value whose key is mapped to in the Properties.
    • remove(String key) Remove the value mapped to the given key from the Properties.
    • size() Returns the number of Properties stored.
    • getKeys() Returns a Vector with the current keys.
    • clear() Clears the Properties.
    • dumpKeysValues(StringBuffer sb,String keyvalueSeparator,
      String lineSeparator)
      Similar to Hashtable.dumpKeysValues().
  • To load or save data directly from/to a DataStream:
    • load(DataStream ds) Load all Properties from the given DataStream. Before calling this method, be sure that there’s something to be read (i. e., that the file is not empty), to improve efficiency.
    • load(DataStream ds, boolean cleanBeforeLoad) Load all Properties from the given DataStream. If cleanBeforeLoad is true, the contents of the object will be cleared before reading from the stream.
    • save(DataStream ds) Save all Properties in the given DataStream.
To store a value in Properties, you must extend the abstract class Properties.Value and implement the method toString().
  • The following classes (and their field definition) are already implemented in the SDK:
    1. Properties.Boolean Implements a value of type boolean.
      • type Equals to TYPE (B).
      • typeStr Equals to boolean.
      • value The boolean value.
      • toString() Returns 1 if value is true; 0, otherwise.
      • hashCode() Returns the integer 1231 if the object represents true and returns the integer 1237 if the object represents false.
      • equals(Object obj) Returns true if and only if the argument is not null and is a Boolean object that represents the same boolean value as the current object.
    2. Properties.Double Implements a value of type double.
      • type Equals to TYPE (D).
      • typeStr Equals to double.
      • value The double value.
      • toString() Returns a string representation of the value number (Convert.
        toString(value)
        ).
      • hashCode() Returns a hash code for the Properties.Double object. The result is the exclusive OR of the two halves of the long integer bit representation, exactly as produced by the method doubleToLongBits(double), of the primitive double value represented by the object. That is, the hash code is the value of the expression: (int) (v ˆ (v >>> 32)) where v is defined by:
        long v = Convert.doubleToLongBits(this.value); 
        
      • equals(Object obj) Compares one object against another specified object. The result is true if and only if the argument is not null and is a Properties.
        Double
        object that represents a double that has the same value as the double represented by the original object. For this purpose, two double values are considered to be the same if and only if the method doubleToLongBits(double) returns the identical long value when applied to each.
        Note that, in most cases, for two instances of class Properties.Double, d1 and d2, the value of d1.equals(d2) is true if and only if
        Convert.doubleToLongBits(d1.value) ==
        Convert.doubleToLongBits(d2.value)
        also has the value true. However, there are two exceptions:
        If d1 and d2 both represent Convert.DOUBLE_NAN_BITS, then the equals() method returns true, even though Convert.doubleToLongBits(Convert.
        DOUBLE_NAN_BITS) == Convert.doubleToLongBits(Convert.
        DOUBLE_NAN_BITS)
        has the value false.
        If d1 represents +0.0 while d2 represents -0.0, or vice versa, the equal test has the value false, even though +0.0==-0.0 has the value true. This definition allows hash tables to operate properly.
    3. Properties.Int Implements a value of type int.
      • type Equals to TYPE (I).
      • typeStr Equals to int.
      • value The int value.
      • toString() Returns a string representation of the value number (Convert.
        toString(value)
        ).
      • hashCode() Returns a hash code value for the object, equal to the primitive int value represented by the Properties.Int object.
      • equals(Object obj) Compares one object against another specified object. The result is true if and only if the argument is not null and is a Properties.Int object that contains the same int value as the original object.
    4. Properties.Long Implements a value of type long.
      • type Equals to TYPE (L).
      • typeStr Equals to long.
      • value The long value.
      • toString() Returns a string representation of the value number (Convert.
        toString(value)
        ).
      • hashCode() Returns a hash code for the Properties.Long object. The result is the exclusive OR of the two halves of the primitive long value held by the object. That is, the hash code is the value of the expression: (int) (this.value ˆ (this.value >>> 32)).
      • equals(Object obj) Compares one object against another specified object. The result is true if and only if the argument is not null and is a Properties.
        Long
        object that contains the same long value as the original object.
    5. Properties.Str Implements a value of type String.
      • type Equals to TYPE (S).
      • typeStr Equals to String.
      • toString() Returns value.
      • hashCode() Returns the hash code of the enclosed string value (value.
        hashCode()
        ).
      • equals(Object obj) Compares one object against another specified object. The result is true if and only if the argument is not null and is a String object, or another Properties.Str object, that represents the same sequence of characters as the original object.

Random

This is a simple Linear Congruential Generator which produces random numbers in the range [0, 231), derived from ran0() in Numerical Recipes. Note that ran0() isn’t that great – there are much better generators out there — but it is fast and has low memory consumption, and it will do the job.
Here’s a sample of how to use the Random class:
Random r = new Random(0x1234); // the use of the same key is sometimes desirable 
for (...) 
{
	int nextRandomInt = r.between(0,10);
	char nextRandomChar = r.between(’a’,’z’);
	...
} 
  • Random has the following constructors:
    • Random() Default constructor, creates a Random instance using a seed based on the timestamp.
    • Random(int seed) Creates a Random object using the given seed. The only reasonable seeds are between 0 and 231 inclusive. If a negative value is received, its absolute value will be used.
  • The class has only 4 simple methods:
    • nextInt(int n) Returns a random integer in the range [0, n). n must be > 0; otherwise, -1 is returned.
    • nextDouble() Returns a random double in the half-open range [0, 1).
    • between(int s, int e) Returns a random integer in the given region. E. g. : rand(1, 10) returns a random integer in the range of 1 to 10, inclusive. Note that if s == e, it will return in the range s and e+1.
    • between(char s, char e) Returns a random character in the given region. E. g.: between(’a’, ’e’) returns a random character between a and e, inclusive.
You should only use one instance of Random, otherwise you might not get good values. Creating a new instance using always the same seed will always result on the same sequence of numbers.

Logger

A Logger object is used to log messages for a specific system or application component. Loggers are usually named using a hierarchical dot-separated namespace. Logger names can be arbitrary strings, but they should usually be based on the package name or class name of the logged component, such as totalcross.net or totalcross.io. In addition it is possible to retrieve one global "anonymous" logger that can be used in the whole system. A logger can be created or retrieved (if it already exists) by getLogger(). To dispose a logger after using it, just call dispose() and it will be permanently discarded (it will be closed and it won’t be possible to use it anymore). To log a message, you may call the log() method or any of the other convenience methods (info(), severe(), entering(), etc).
  • First you must be familiar with the different message levels:
    CONFIG Message level for static configuration messages.
    INFO Message level for informational messages.
    FINE Message level providing tracing information.
    FINER Indicates a fairly detailed tracing message.
    FINEST Indicates a highly detailed tracing message.
    WARNING Message level indicating a potential problem.
    SEVERE Message level indicating a serious failure.
  • You may get or set the logger default settings with these static methods:
    • getDefaultSeparator() Returns the string used as initial message separator, which is used by new loggers. A separator is a string that separates two log messages.
    • setDefaultSeparator(String separator) Sets the initial message separator to be used by new loggers.
    • getDefaultLevel() Returns the initial logger level, which is used by new loggers.
    • setDefaultLevel(int level) Sets the initial logger level to be used by new loggers.
  • The logger level may be one of the above message levels, or any composition of them OR’ed together. You may also use one of the following constants:
    ALL Indicates that all messages should be logged.
    OFF Special level that can be used to turn off logging.
  • To get a logger, you may use one of the following static methods:
    • getLogger(String name) Returns the logger with the given name, keeping its level and output handlers unchanged. If the logger does not exist, it will be created and stored for future use.
    • getLogger(String name, int level) Returns the logger with the given name, setting the specified level and keeping the output handlers unchanged. If the logger does not exist, it will be created and stored for future use. If level == -1, it won’t be changed.
    • getLogger(String name, int level, Stream outputStream) Returns
      the logger with the given name, setting the level specified and optionally adding the given stream to the list of output handlers. If the logger does not exist, it will be created and stored for future use. If level == -1 or outputStream == null, it won’t be changed.
    • getGlobalLogger() Returns the global anonymous logger (name == null). Notice that the name can’t be null in the above methods.
  • After getting a Logger instance, you may get or set its settings:
    • getName() Returns the name of the logger or null, if the logger is the global anonymous logger.
    • getSeparator() Returns the string used by the logger to separate log messages.
    • setSeparator(String separator) Sets the separator to be used by the logger.
    • getLevel() Returns the current level of the logger, which is an integer number representing the current logger level. To check if the logger is set to log a specific type of message (SEVERE, WARNING, etc), just check if the level OR’ed with the message level is different from zero.
    • setLevel(int level) Sets the current level of the logger. level should be a composition of one or more of the message types constants (FINEST, FINER, FINE, CONFIG, INFO, WARNING, or SEVERE), OFF, if you want to disable all logging, or ALL, if you want to enable all logging.
    • getSeparator() Gets the string used to separate two log messages, that is, the string written after each log message. A separator can be \n (newline), white spaces, etc.
    • setSeparator(String separator) Sets the string used to separate two log messages.
  • You must add an output handler to a logger, which is a stream object that will receive the log messages. A logger may have multiple output handlers.
    • addOutputHandler(Stream output) Adds an output stream to the logger’s output handler set. It does nothing if the given stream was previously added to this logger. This means that every logged message (depending on the current logger level) will be written to this new stream.
    • getOutputHandlers() Gets the output handlers associated with the logger, returning an array of all registered output handlers.
    • removeOutputHandler(Stream output) Removes an output handler from the logger’s output handler set. It return true if, and only if, the stream was successfully removed.
    You may use the static field DEBUG_CONSOLE, which is a stream to the TotalCross’ debug file.
  • The following methods can be used to help you trace the execution flow of your code and log exceptions:
    • entering(String sourceClass, String sourceMethod,
      String params)
      Logs a FINER message reporting a method entry. A record with message ENTRY, log level FINER, and the given parameters are logged.
    • exiting(String sourceClass, String sourceMethod) Logs a FINER message reporting a method return. A record with message RETURN, log level FINER, and the given parameters are logged.
    • throwing(String sourceClass, String sourceMethod,
      Throwable thrown)
      Logs a SEVERE message reporting an exception being thrown. A record with message THROW, log level SEVERE, and the given parameters are logged.
  • To log your own message, you may use:
    • log(int level, String message, boolean prependInfo) Logs the given message, using the given level. The last argument is a flag indicating whether this log message must be prepended with the current date and time, level string, logger name, etc. It will throw a NullPointerException if one or more registered output handler streams are null or the logger has been disposed.
  • Or one of the convenience methods, which are actually just calls to the method log() using the given message, the appropriate level, and the prependInfo flag always on:
    • config(String message) Logs a CONFIG message.
    • info(String message) Logs an INFO message.
    • fine(String message) Logs a FINE message.
    • finer(String message) Logs a FINER message.
    • finest(String message) Logs a FINEST message.
    • warning(String message) Logs a WARNING message.
    • severe(String message) Logs a SEVERE message.
  • Finally, you should dispose the logger and close all streams associated to the logger, unless its streams are still in use by the program.
    • dispose(boolean closeOutputHandlers) Permanently discards this logger, removing it from the loggers registry. If closeOutputHandlers is true, all streams associated to this logger are closed.
You shouldn’t (and in fact you can’t) dispose the global logger. Calling dispose() for the global logger does not discard it, only adds an useless method call to your code.

BigDecimal

Arbitrary-precision signed decimal numbers. A BigDecimal consists of an arbitrary precision integer unscaled value and a 32-bit integer scale. If zero or positive, the scale is the number of digits to the right of the decimal point. If negative, the unscaled value of the number is multiplied by ten to the power of the negation of the scale. The value of the number represented by the BigDecimal is therefore unscaledValue × 10-scale.
The BigDecimal class provides operations for arithmetic, scale manipulation, rounding, comparison, hashing, and format conversion. The toString() method provides a canonical representation of a BigDecimal.
The BigDecimal class gives its user complete control over rounding behavior. If no rounding mode is specified and the exact result cannot be represented, an exception is thrown; otherwise, calculations can be carried out to a chosen precision and rounding mode.
Since the same numerical value can have different representations (with different scales), the rules of arithmetic and rounding must specify both the numerical result and the scale used in the result’s representation. In general the rounding modes and precision setting determine how operations return results with a limited number of digits when the exact result has more digits (perhaps infinitely many in the case of division) than the number of digits returned.
For all arithmetic operators, the operation is carried out as though an exact intermediate result were first calculated and then rounded to the number of digits specified by the precision setting (if necessary), using the selected rounding mode. If the exact result is not returned, some digit positions of the exact result are discarded. When rounding increases the magnitude of the returned result, it is possible for a new digit position to be created by a carry propagating to a leading "9" digit. For example, rounding the value 999.9 to three digits rounding up would be numerically equal to one thousand, represented as 100×101. In such cases, the new "1" is the leading digit position of the returned result.
Besides a logical exact result, each arithmetic operation has a preferred scale for representing a result. The preferred scale for each operation is listed in the table below.
Operation Preferred Scale of Result
Add max(addend.scale(), augend.scale())
Subtract max(minuend.scale(), subtrahend.scale())
Multiply multiplier.scale() + multiplicand.scale()
Divide dividend.scale() - divisor.scale()
These scales are the ones used by the methods which return exact arithmetic results; except that an exact divide may have to use a larger scale since the exact result may have more digits. For example, 1/32 is 0.03125.
Before rounding, the scale of the logical exact intermediate result is the preferred scale for that operation. If the exact numerical result cannot be represented in precision digits, rounding selects the set of digits to return and the scale of the result is reduced from the scale of the intermediate result to the least scale which can represent the precision digits actually returned. If the exact result can be represented with at most precision digits, the representation of the result with the scale closest to the preferred scale is returned. In particular, an exactly representable quotient may be represented in fewer than precision digits by removing trailing zeros and decreasing the scale. For example, rounding to three digits using the floor rounding mode,
19/100 = 0.19 // integer=19, scale=2

but
21/110 = 0.190 // integer=190, scale=3
Note that for add, subtract, and multiply, the reduction in scale will equal the number of digit positions of the exact result which are discarded. If the rounding causes a carry propagation to create a new high-order digit position, an additional digit of the result is discarded than when no new digit position is created.
Other methods may have slightly different rounding semantics. For example, the result of the pow() method using the specified algorithm can occasionally differ from the rounded mathematical result by more than one unit in the last place, one ulp.
Two types of operations are provided for manipulating the scale of a BigDecimal: scaling/rounding operations and decimal point motion operations. Scaling/rounding operations (
setScale()
) return a BigDecimal whose value is approximately (or exactly) equal to that of the operand, but whose scale or precision is the specified value; that is, they increase or decrease the precision of the stored number with minimal effect on its value. Decimal point motion operations (movePointLeft() and movePointRight()) return a BigDecimal created from the operand by moving the decimal point a specified distance in the specified direction.
All methods and constructors for this class throw NullPointerException when passed a null object reference for any input parameter.
If you need numerical precision, you need to use this class when dealing with floating point numbers.
Computers don’t deal properly with decimals. As you already know, data is represented internaly using the binary format.
The number 4, for instance, is represented by 100 (1x22 + 0x21 + 0x20 = 4).
How to represent a number smaller than 1? Each decimal digit after the dot represents the values 1/2 (2-1), 1/4 (2-2), 1/8 (2-3), and so on.
Therefore, the numbers 4.25, 4.5, and 4.75 are represented in binary like this, respectively: 100,01, 100,10, and 100,11.
This is very poor to represent decimals. Trying to represent 0.3 in binary results in an infinite binary number, because 0.3 = 0.25 + 0.03125 + 0.009765635...
To bypass the problem, the calculations are done with many decimal digits and then the result is rounded. This explains why double and float calculations are not precise. Some processors use 50 bits, which is good enough for many applications. However, it is not an infinite number of bits, which is needed to represent some numbers, such as 0.3.
A correct example to use this class is the following:
public static BigDecimal newBigDecimal(String value) throws InvalidNumberException
{
	return new BigDecimal(value.replace(Settings.decimalSeparator, ’.’));
}
​
public static String toPlainString(BigDecimal value)
{
	return value.toPlainString().replace(’.’, Settings.decimalSeparator);
}
BigDecimal num1 = newBigDecimal("5.912");
BigDecimal num2 = newBigDecimal("0.01");
BigDecimal num3 = num1.multiply(num2).setScale(4, BigDecimal.ROUND_HALF_EVEN);
Vm.debug("Resultado: "+toPlainString(num3)); 
If you are getting a value typed by the user in a text box, DON’T CONVERT IT TO DOUBLE! You must create a BigDecimal directly.
That is, the following code is WRONG:
double d = Convert.toDouble(edValor.getText());
BigDecimal b = new BigDecimal(d);
When you convert the number to double, you LOSE PRECISION. The correct code is:
BigDecimal b = new BigDecimal(edValor.getText()); 
Read its JavaDoc (and also java.math.BigDecimal JavaDoc for the parts used in TotalCross) to learn how to use each method, constructor, and constant!

BigInteger

This class represents arbitrary-precision integers. All operations behave as if BigIntegers were represented in two’s-complement notation (like Java’s primitive integer types). BigInteger provides analogues to all of Java’s primitive integer operators. Additionally, BigInteger provides operations for modular arithmetic, GCD calculation, primality testing, prime generation, bit manipulation, and a few other miscellaneous operations.
Semantics of arithmetic operations exactly mimic those of Java’s integer arithmetic operators, as defined in The Java Language Specification. For example, division by zero throws an
ArithmeticException
, and division of a negative by a positive yields a negative (or zero) remainder. All of the details in the specification concerning overflow are ignored, as BigIntegers are made as large as necessary to accommodate the results of an operation.
Semantics of shift operations extend those of Java’s shift operators to allow for negative shift distances. A right-shift with a negative shift distance results in a left shift, and vice-versa. The unsigned right shift operator (>>>) is omitted, as this operation makes little sense in combination with the "infinite word size" abstraction provided by this class.
Semantics of bitwise logical operations exactly mimic those of Java’s bitwise integer operators. The binary operators (and, or, xor) implicitly perform sign extension on the shorter of the two operands prior to performing the operation.
Comparison operations perform signed integer comparisons, analogous to those performed by Java’s relational and equality operators.
Modular arithmetic operations are provided to compute residues, perform exponentiation, and compute multiplicative inverses. These methods always return a non-negative result, between 0 and (modulus - 1), inclusive.
Bit operations operate on a single bit of the two’s-complement representation of their operand. If necessary, the operand is sign-extended so that it contains the designated bit. None of the single-bit operations can produce a BigInteger with a different sign from the BigInteger being operated on, as they affect only a single bit, and the "infinite word size" abstraction provided by this class ensures that there are infinitely many "virtual sign bits" preceding each BigInteger.
Most methods and constructors in this class throw NullPointerException when passed a null object reference for any input parameter.
Read its JavaDoc (and also java.math.BigInteger JavaDoc for the parts used in TotalCross) to learn how to use each method, constructor, and constant!

28 totalcross.sys

For more details about each class, please read their JavaDocs.

Time

The Time class stores a specific a date and time. The year must have 4 digits and the hour is numbered in 24-hour notation, which is the international standard notation of time, and may also be referred as military time or astronomical time.
For performance reasons, the Time fields have public access. So you can directly access the field day to get or set its value, instead of calling a method. However, that makes the Time objects unsafe because the fields’ values are not checked when they are set, and may not be within the field valid range.
Since the fields can be set without any kind of validation, it would be pointless to add validation to the other methods, therefore, the Time fields’ values are never validated by any method or constructor. So you must know and always respect the fields’ range, and never set a field with a variable without first checking if the value is withing range (for instance, let the user type the hour in an edit and simply convert it to int and set the hour field, without checking if its value is between 0 and 23).
  • The Time fields with their respective range:
    year The year in 4 digits.
    month The month in the range of 1 to 12.
    day The day in the range of 1 to the last day of the specified month.
    hour The hour in the range of 0 to 23.
    minute The minute in the range of 0 to 59.
    second The second in the range of 0 to 59.
    millis Milliseconds in the range of 0 to 999.
  • Time has a constant called SECONDS_PER_DAY, which obviously represents the number of seconds in a day, being equal to 24 * 3600.
  • Time has six constructors:
    • Time() Default constructor, creates a Time object set with the device’s current date and time.
      Most devices do not keep track of the milliseconds, therefore, the field millis of the new object will always have the default value 0 on them.
    • Time(int year, int month, int day, int hour, int minute,
       int second, int millis)
      Creates a Time object with the given values.
    • Time(long t) Creates a Time object from the given value, which must be in the format YYYYMMDDHHMMSS.
    • Time(int yyyymmdd, int hhmmssmmm) Constructs a Time object from the given date and time values.
    • Time(String iso8601) Creates a Time object using the given string, which must be in the ISO8601 format: YYYYMMDDTHH:MM:SS.
    Please notice the last three constructors do not include the milliseconds, so the field millis will keep its default value 0.
    • Time(String time, boolean hasYear, boolean hasMonth,
      boolean hasDay, boolean hasHour, boolean hasMinute,
      boolean hasSeconds)
      Constructs a Time object, parsing the string and placing the fields depending on the flags that were set, using Settings.timeSeparator as spliter. The number of parts must match the number of true flags, or an
      ArrayIndexOutOfBoundsException
      will be thrown. AM/PM is supported.
    Remember: no kind of validation is done on the Time fields values, not even on the constructors. However, the default constructor will never initialize an object with invalid values, and the last two constructors may throw an InvalidNumberException if it fails to parse the given string.
  • Finally, Time has the following methods:
    • update() Updates the internal fields with the current timestamp.
    • equals(Object o) Compares two Time objects for equality. The result is true if and only if the argument is not null and it’s a Time object that represents the same point in time, from year to millisecond, as this object.
    • getTimeLong() Converts this Time object to a long value in the format
      YYYYMMDDHHMMSS.
      Milliseconds is not included.
    • toIso8601() Converts this Time object to a string in the ISO8601 format:
      YYYYMMDDTHH:MM:SS
      . Milliseconds is not included.
    • toString() Returns the time in format specified in totalcross.sys.Settings (does NOT include the date neither the milliseconds). To return the date, use the class totalcross.util.Date. So, to get a string with the date and time, use:
      Time t = new Time(); 
      String dateAndTime = new Date(t) + " " + t;
      
    • toString(String timeSeparator) Similar to the above method except that it uses the specified separator.
    • dump(StringBuffer sb, String timeSeparator,
      boolean includeMillis)
      Dumps the time into the given StringBuffer, using the given separator and including the millileconds if asked by the user.
    • isValid() Returns true if the time is valid. Note that the date part is NOT checked; only hour, minute, second, and millis are checked against valid ranges.
    • inc(int hours, int minutes, int seconds) Increments or decrements the fields below. Note that this method does NOT update the day/month/year fields. The parameters can be positive (to increment), zero (to keep it), or negative (to decrement).

CharacterConvert

This class is used to correctly handle international character conversions. The default character scheme converter is the 8859-1 (ISO Latin 1). If you want to use a different one, you must extend this class, implementing the bytes2chars() and chars2bytes() methods, and then assign the public member of Convert.charConverter to use your class instead of this default one. You can also use the method Convert.setDefaultConverter() to change it, passing, as parameter, the prefix of your CharacterConverter class (better look at the implementation to know what to pass on). For example, if you created a class named Iso88592CharacterConverter, call
Convert.setDefaultConverter("Iso88592"); 
To find out which sun.io.CharacterEncoder you’re using on JDK to implement an equivalent version for TotalCross, use:
System.out.println("" + sun.io.ByteToCharConverter.getDefault());

UTF8CharacterConvert

This class extends the CharacterConvert class, and implements the UTF8 byte to UCS-2 character conversion. To use this class, you can call:
Convert.setDefaultConverter("UTF8");

Convert

Convert basically provides methods that allows object and basic type conversion. Furthermore, it also provides handy methods for common operations that should be used for a better performance.
This class is final and cannot be instantiated – its methods and fields are static.
To give you a better view of this class, its documentation was split into sub-sections:

Changing the default character converter

The field charConverter keeps a reference to a character converter that will be used by default. You may change it by setting another character converter of your choice.
You may also use the method setDefaultConverter(String name), which searches for a character converter by its name, and makes it the default by changing the charConverter field. Use like
Convert.setDefaultConverter("UTF8");
to change all bytes_to_char and char_to_bytes operations to use UTF8 instead. Issuing
Convert.setDefaultConverter("");
sets back the default encoder. The method returns true if the given encoder was found; false, otherwise. If not found, the encoder is reseted to the default one (ISO 8859-1).

Conversion between String and basic types

  • toDouble(String s) Converts the given string to double.
  • toInt(String s) Converts the given string to int. The number may be prefixed with 0’s.
  • toShort(String s) Convert a string to the short type. Note that this method is slower than (short)Convert.toInt(). However, it will throw an InvalidNumberException if the number is out of the short range.
  • toLong(String s) Converts the given string to long.
  • toLong(String s, int radix) Converts the given string to long in the given radix, which must be between 2 and 16.
  • toString(boolean b) Converts the given boolean to a string.
  • toString(char c) Converts the given char to a string.
  • toString(double d) Converts the given double to a string, formatted in scientific notation.
  • toString(double val, int decimalCount) Converts the given double to a string, formatted with the given number of decimal places.
  • toString(int i) Converts the given int to a string.
  • toString(long l) Converts the given long to a string using base 10.
  • toString(long i, int radix) Converts the given long to a string in the given radix, which must be between 2 and 16.
  • toString(String doubleValue, int n) Formats the given string as a double, rounding with n decimal places.
  • unsigned2hex(int b, int places) Converts the given unsigned integer to hexadecimal using the given number of places (up to 8).

Character, String and StringBuffer utilities

  • appendPath(String path1, String path2) Concatenates two strings, ensuring
    there’s a single slash between them. Removes extra slashes or backslashes if necessary.
  • digitOf(char ch, int radix) Returns the value of the digit stored as char in the specified radix, which must be between 2 and 16. This method only handles the standard ASCII table.
  • dup(char c, int count) Returns a string filled with the given char and size equals to count.
  • forDigit(int digit, int radix) Returns the given digit in the specified radix, which must be between 2 and 16.
  • getBreakPos(FontMetrics fm,StringBuffer sb,int start,
    int width,boolean doWordWrap)
    Finds the best position to break the line with the given width, respecting word-wrap option and line endings (\n).
  • hashCode(StringBuffer sb) Returns the hash code of the string stored by this
    StringBuffer
    .
    The class StringBuffer does not have a method that returns its hash code, so you would have to first create a String from the StringBuffer to get its hash code, like this:
    int hashCode = sb.toString.hashCode();
    Convert.hashCode() calculates the StringBuffer’s hash code directly, without using an intermediary String object, resulting in better performance and memory usage.
  • insertAt(StringBuffer sb, int pos, char c) Inserts the given char at the specified position in the StringBuffer.
  • insertLineBreak(int maxWidth, FontMetrics fm, String text) Returns a new string which is a copy of the given text with line breaks, placed based on the maxWidth and fm arguments.
    Very useful method to help you keep your application’s interface cross-platform. It can be used to insert line breaks on strings passed to message boxes or list boxes.
  • numberOf(String s, char c) Returns the number of occurrences of the specified char in the given string.
  • replace(String source, String from, String to) Searches the string source for occurrences of the string from, replacing them by the string to.
  • tokenizeString(String input, char delim) Tokenizes the given string using the given char as separator. The return is a string array with size equal to the number of tokens.
  • tokenizeString(String input, String delim) Same as the above, but uses a String instead of a char as separator.
    Never use this method with 1 character length strings, like:
    String[] tokens = Convert.tokenizeString(input, “#”);
    Use the previous method instead for better performance:
    String[] tokens = Convert.tokenizeString(input, ’#’);
  • toLowerCase(char c) Converts the given char to lower case.
  • toUpperCase(char c) Converts the given char to upper case.
  • zeroPad(String s, int size) Pads the string, adding zeros at left.
  • zeroUnpad(String s) Removes left zeros of the string.

Arrays

  • cloneStringArray(String[] strs) Returns a copy of the given string array.
  • toStringArray(Object[] objs) Converts the given object array into a string array, by calling toString() for each object.
  • detectSortType(Object item) Returns the sort type for the given item sample (which is usually the first item of an array).
Convert provides the quick sort algorithm for array sorting.

Constants

SORT_AUTODETECT Chooses between one of the sort types below based on the first element of the array.
SORT_OBJECT The objects are compared by their string representation.
SORT_STRING The array contains String objects, and the sort is case sensitive.
SORT_INT The array contains String objects that represents integer values.
SORT_DOUBLE The array contains String objects that represents double values.
SORT_DATE The array contains String objects that represents a Date object with day, month, and year.
SORT_COMPARABLE The array contains comparable objects (objects that implements the
Comparable
interface).
SORT_STRING_NOCASE The array contains String objects, and the sort is case insensitive, which is slower than case sensitive sorting.

Methods

  • qsort(Object[] items, int first, int last) Applies the quick sort algorithm to the elements of the given array, sorting in ascending order and sort type equals to
    SORT_AUTODETECT
    .
  • qsort(Object[] items, int first, int last, int sortType) Same as the above method, but you can specify the sort type.
  • qsort(Object[] items, int first, int last, int sortType,
    boolean ascending)
    Same as the above, but you can also choose between sorting in ascending or descending order.

Other Conversions and Methods

  • chars2int(String fourChars) Converts a creator id or type to int.
  • int2chars(int i) Converts an int to a creator id or type.
  • doubleToIntBits(double f) Converts the given double to its bit representation in IEEE 754 format, using 4 bytes instead of 8 (a conversion to float is applied).
  • intBitsToDouble(int i) Converts the given IEEE 754 bit representation of a float to a double.
  • doubleToLongBits(double value) Returns a representation of the specified floating-point value according to the IEEE 754 floating-point "double format" bit layout.
  • longBitsToDouble(long bits) Converts the given bit representation to a double.
  • rol(long i, int n, int bits) Does a rol of n bits in the given long. n must be < bits. Unlike the shift left operator (<<), bits that would have been lost are reinserted in order at the right.
  • ror(long i, int n, int bits) Does a ror of n bits in the given long. n must be < bits. Unlike the shift right operator (>>), bits that would have been lost are reinserted in order at the left.

Another Useful Constants

  • CRLF Equals to \r\n.
  • CRLF_BYTES Equals to {’\r’,’\n’}.
  • MAX_SHORT_VALUE The maximum short value: 32767.
  • MIN_SHORT_VALUE The minimum short value: -32768.
  • MIN_INT_VALUE The minimum int value: -2147483648.
  • MAX_INT_VALUE The maximum int value: 2147483647.
  • MIN_LONG_VALUE The minimum long value: -9223372036854775808.
  • MAX_LONG_VALUE The maximum long value: 9223372036854775807.
  • MAX_DOUBLE_VALUE The maximum double value: 9.007199254740992E15.
  • MIN_DOUBLE_VALUE The minimum double value: 1.1102230246251565E-16.
  • MAX_DOUBLE_DIGITS The maximum number of digits in a double value, used when formatting to string.
  • DOUBLE_POSITIVE_INFINITY_VALUE The double that represents a positive infinity.
  • DOUBLE_NEGATIVE_INFINITY_VALUE The double that represents a negative infinity.
  • DOUBLE_POSITIVE_INFINITY_BITS The long whose bits represent a positive infinity.
  • DOUBLE_NEGATIVE_INFINITY_BITS The long whose bits represent a negative infinity.
  • DOUBLE_NAN_BITS The long whose bits represent a Not a Number (NaN).

Settings

This class provides some preferences from the device configuration and other VM settings. All settings are read-only, unless otherwise specified. Changing their values may cause the VM to crash. Look at its JavaDoc for more details.

Vm

Vm contains various system-level methods. This class contains methods to copy arrays, obtain a timestamp, sleep, and get platform and version information, among many other things. Look at its JavaDoc for more details.

Part IV. INPUT & OUTPUT

Overview

The purpose of this part is to teach you how to use the classes in the totalcross.io package. The classes that will be covered in this tutorial are outlined below:
  1. totalcross.io.Stream: is the main class of all classes inside the totalcross.io package. It can be viewed as a generic stream where data can flow in and out of the device. All classes inside package totalcross.io extend this class, and most classes receive a totalcross.io.Stream in their constructor parameter. This allows you to combine different streams to meet your needs.
  2. totalcross.io.DataStream (and DataStreamLE): used to read/write primitive types and strings in Big Endian (default) or Little Endian format.
  3. totalcross.io.BufferedStream: offers a faster way to read and write data from streams in a buffered manner.
  4. totalcross.io.ByteArrayStream: a resizable buffer that can be used to feed data to a stream or to store data coming from a stream. It can be used to emulate a virtual stream.
  5. totalcross.io.CompressedByteArrayStream: creates a compressed byte array stream, saving memory when reading and writting huge amount of data.
  6. totalcross.io.RandomAccessStream: represents a stream that behaves like a large array of bytes and may be randomly accessed.
  7. totalcross.io.CRC32Stream: computes CRC32 data checksum of a stream.
  8. totalcross.io.Connector: used to open socket connections over CRADLE, WIFI, MDS, GPRS.
  9. totalcross.io.StreamConnectionNotifier: base class for connection notifiers.
  10. totalcross.io.device.PortConnector: used to send and receive bytes via Serial Cradle, USB, IrComm, and Bluetooth.
  11. totalcross.io.File: used to create files and manage directories.
  12. totalcross.io.LineReader: used to read lines ending with \r\n (enter/linefeed) or \n (linefeed) from a stream.
  13. totalcross.io.TokenReader: Used to read an array of tokens in a line ending with \r\n (enter/linefeed) from a stream.
  14. totalcross.io.device.gps.GPS retrieves GPS coordinates.
  15. totalcross.io.device.RadioDevice provides access to the device’s radios and information about their status.
The classes related to PDB files (PDBFile, ObjectPDBFile, PDBStream, ResizeRecord, RemotePDBFile, and RemotePDBRecord) are ommited because they are inefficient on all platforms. Unless you use a legacy TotalCross program, don’t use PDB files.

29 Stream

Stream is an abstract class that serves as the base class for all classes that handles I/O operations. It defines the interface and the basic behavior that all its subclasses must implement.
  • close() Closes this stream, releasing any resources associated to it. Once closed a connection is no longer valid.
  • readBytes(byte[] buf, int start, int count) Tries to read count bytes from this stream to the given byte array buf, beginning from the position start. Returns the number of bytes actually read.
  • writeBytes(byte[] buf, int start, int count) Tries to write count bytes to this stream from the given byte array buf, beginning from the position start. Returns the number of bytes actually written.
These three methods may throw an IOException, which usually means that something out of our control went wrong, preventing the method to be executed.
Stream subclasses usually throw more exceptions that extends IOException to denote more specific problems and allow you to properly handle the situation, like FileNotFoundException.
Streams subclasses can be divided into two main groups:
  • Classes that actually perform I/O operations, like File or Socket.
  • Classes that wrap a stream, providing features or operations that can be applied to any other stream, like DataStream or BufferedStream.
We’ll first look into this last group, which provides easier and more efficient ways of reading and writing data.
You should always chech the return of the methods to see if the written or read number of bytes are the expected one.
For more details, check out totalcross.io.Stream JavaDoc.

30 DataStream/DataStreamLE

DataStream is a wrapper that you can place around any stream such as a File, Socket, or ByteArrayStream, which allows you to read and write TotalCross’ primitive data types and objects, like ints, longs, doubles, and Strings, in a simple and standard way.
Although TotalCross internally uses the little endian format, the DataStream class uses the big endian format - the standard Java format. To read and write using the little endian format, you must use the DataStreamLE class instead.
Here is a sample:
PortConnector port = new PortConnector(9600, 0);
DataStream ds = new DataStream(port);
ds.writeString("Hello");
int status = ds.readUnsignedByte();
if (status == 1)
{
	ds.writeString("Pi");
	ds.writeDouble(3.14);
}
port.close();   
  • DataStream has two constructors:
    • DataStream(Stream stream) Creates a data stream which wraps the given stream.
    • DataStream(Stream stream, boolean ensureWrite) The same as above but if the second parameter is true, write operations are blocked until all the requested data is written to the underlying stream.
Handling primitive data types:
  • byte
    • readByte() Reads a byte. The returned value will range from -128 to 127.
    • writeByte(byte by) Writes a byte.
    • readUnsignedByte() Reads an unsigned byte, returning its value as an int. The returned value will range from 0 to 255. Use writeByte() to write the unsigned byte.
    • writeByte(int by) Writes an unsigned byte, which is the first byte from the given int, whose value must be between 0 and 255.
  • boolean
    • readBoolean() Reads a byte as a boolean. true is returned if the byte is not zero; false, otherwise.
    • writeBoolean(boolean bool) Writes a boolean as a byte. true values are written as 1 and false values as 0.
  • char
    • readChar() Reads two bytes as a char.
    • writeChar(char c) Writes a char as two bytes.
  • short
    • readShort() Reads two bytes as a short. The returned value will range from -32768 to 32767.
    • readUnsignedShort() Reads two bytes as an unsigned short, returning its value as an int. The returned value will range from 0 to 65535. Use writeShort() to write the unsigned short.
    • writeShort(int i) Writes a short as two bytes.
    There’s only one method to write a short, which receives an integer. It may be used to write signed and unsigned shorts.
  • int
    • readInt() Reads four bytes as an int. The returned value will range from -2147483648 to 2147483647.
    • readUnsignedInt() Reads four bytes as an unsigned int, returning its value as a long. The returned value will range from 0 to 4294967295. Use writeLong() to write the unsigned int.
    • writeInt(int i) Writes an int as four bytes.
  • long
    • readLong() Reads eight bytes as a long.
    • writeLong(long l) Writes a long as eight bytes.
  • float
    • readFloat() Reads four bytes as a float in IEEE 754 format, returning its value as a double.
    • writeFloat(double f) Writes a float as four bytes in IEEE 754 format.
  • double
    • readDouble() Reads eight bytes as a double.
    • writeDouble(double d) Writes a double as eight bytes.
Handling char arrays:
  • readChars() Reads an array of chars, where its length is stored in the first two bytes as an unsigned short (limited to 65,535 characters).
  • readChars(int len) Reads an array of chars with the given length.
  • readChars(char[] chars, int len) Reads an array of chars with the given length to the given array of chars.
  • readChars(char[] chars, int start, int count) Reads count chars from the stream to the given array of chars, starting from index start.
  • writeChars(char[] chars, int start, int len) Writes an array of chars, placing its length in the first two bytes as an unsigned short (limited to 65535 characters), where start is the start index of the array and len is the length to be written or -1 if it is to write the whole char array.
  • writeChars(char[] chars, int start, int len, int lenSize) Writes the given char array, writting the length as an int, as a short, as a byte, or don’t writting the length, depending on the number of bytes given (4,2,1,0). If the size to be written is greater than a non-zero lenSize, an IOException will be thrown.
  • writeChars(String s, int len) Writes the string as a char array. The characters are read using the charAt() method from the String class. This method is slower on all devices. The char array size is limited to 65535 characters.
  • readBigChars() Reads an array of chars, where its length is stored in the first four bytes as an int (limited to +2147483647 characters).
  • writeBigChars(char[] chars, int start, int len) Writes an array of chars, placing its length in the first four bytes as an int (limited to +2147483647 characters), where start is the start index of the array and len is the length to be written or -1 if it is to write the whole char array.
Handling Strings:
  • readString() Reads a String, where its length is stored in the first two bytes as an unsigned short (limited to 65535 characters). If the length is zero, an empty string is returned. null is never returned.
  • writeString(String s) Writes a String, placing its length in the first two bytes as an unsigned short (limited to 65535 characters).
  • readBigString() Reads a String, where its length is stored in the first four bytes as an int (limited to +2147483647 characters). If the length is zero, an empty string is returned. null is never returned.
  • writeBigString(String s) Writes a String, placing its length in the first four bytes as an int (limited to +2147483647 characters).
  • readCString() Reads a C-style (null terminated) string (no implicit limit). This format is commonly used by other applications. Note that if you’re creating your own stream, choose readString() instead of readCString(), because readCString() is MUCH slower. Also, this method does not handle correctly unicode characters.
  • writeCString(String s) Writes a C-style (null terminated) string. (no implicit limit). This means that all the characters of the string are written out, followed by a NULL (0) character. This format is commonly used by other applications.
Handling ASCII strings:
These methods are used to read and write strings comprised of only ASCII chars – each character is cast from/to byte, and requires 1 byte instead of 2 bytes. Make sure your strings have only characters between the ASCII range (0-255), because characters outside this range will not be properly handled.
  • readSmallString() Reads a small string, where its length is stored in the first byte as an unsigned byte (limited to 255 characters). If the length is zero, an empty string is returned. null is never returned.
  • writeSmallString(String s) Writes a small string, placing its length in the first byte as an unsigned byte (limited to 255 characters).
  • writeSmallString8(String s) Writes a small string taking each character as a byte. To read it, use readString(). The maximum allowed length is 65536.
  • readFixedString(int length) Reads a fixed length string from the stream. The given number of characters are read and converted to a string.
  • writeFixedString(String s, int length) Writes a fixed length string to the
    stream. If the given string is longer than the given length, it will be truncated and if it is shorter, it will be padded with spaces.
  • writeFixedString(String s, int length, char pad) Writes a fixed length
    string to the stream. If the given string is longer than the given length, it will be truncated and if it is shorter, it will be padded the given pad character.
Handling String arrays:
The methods below may be used to handle String arrays, and both should be used together. This is because the size of the array is stored in the first two bytes as an unsigned short, before the very first String (e.g. if you write several strings with writeString(), you cannot use readStringArray() to read them, and vice-versa).
It’s important to notice these methods handle Strings like the readString() and
writeString()
methods, so the current character encoding will be used and their length stored as an unsigned short, therefore, the String array cannot contain a String with more than 65,535 characters.
  • readStringArray() Reads an array of Strings, where its size is stored in the first two bytes as an unsigned short. If the length is zero, an empty array is returned. null is never returned.
  • readStringArray(int size) Reads an array of Strings, where its size is given as a parameter. If the length is zero, an empty array is returned. null is never returned.
  • writeStringArray(String[] v) Writes an array of Strings, placing its size in the first two bytes as an unsigned short.
Handling Objects (Storable):
Objects that implements the interface Storable may have its internal state persisted or retrieved directly using the following methods:
  • readObject() Reads an Object which implements the Storable interface and was written using writeObject().
  • writeObject(Storable s) Writes a Storable Object.
Utility methods:
  • getStream() Returns the stream attached to this DataStream.
  • pad(int n) Pads the stream writing n bytes with value 0.
  • skip(int n) Skips reading the next n bytes in the stream (only forward!).
  • readBytes(byte[] buf) Same as readBytes(buf, 0, buf.length).
  • writeBytes(byte[] buf) Same as writeBytes(buf, 0, buf.length).
  • close() Closes the stream. This just calls the close() method of the attached stream, thus closing it. Usually, this method may never be called. Remember that closing a stream twice may throw an IOException, so if you call this close method, don’t call the base Stream’s.
As a general rule, strings and arrays with null value are persisted as zero sized strings and arrays (e.g. a string persisted with writeString(null) is read as “” - a zero sized string, which is not a null value).
For more details, check out totalcross.io.DataStream and totalcross.io.
DataStreamLE
JavaDocs.

31 BufferedStream

BufferedStream offers a faster way to read and write data from streams in a buffered manner. This is especially useful when reading or writing large amounts of data. It works like the CompressedByteArrayStream, however it does not compresses the data like that one. Here’s a sample code:
public void writeLargeFile(String path, byte[] largeData) throws IOException
{
	File f = new File(path, File.CREATE_EMPTY);
	BufferedStream bs = new BufferedStream(f, BufferedStream.WRITE, 4096);
	bs.writeBytes(largeData, 0, largeData.length);
	bs.close(); // important!
	f.close();
} 
For more details, check out totalcross.io.BufferedStream JavaDoc.

32 ByteArrayStream

ByteArrayStream is a Stream that has an internal growable byte array, and it is commonly used as a buffer.
  • In I/O operations, usually it’s faster and/or less resource consuming to send larger chunks of data at once, instead of sending several small chunks of data. You may use a ByteArrayStream as a buffer and wait until it reaches a minimum size performing any actual I/O operation.
Sample code:
Socket s = new Socket(host, 8080); // connect to our host
PortConnector pc = new PortConnector(0, 9600); // open a serial connection
ByteArrayStream bas = new ByteArrayStream(512); // length 512
bas.readFully(s, 10, 0); /* reads from the given stream (our socket) until the maximum number of retries is reached, in this case, 10 times. */
int available = bas.available();
if (available > 0) // did we receive anything?
	pc.writeBytes(bas.getBuffer(), 0, available); /* yes! Let’s send the data over the serial connection. */
This difference is easily noticeable when dealing with external connections, like socket or port connector, but don’t forget their operations usually have a time limit (timeout), that must be adjusted according to your connection speed and the amount of data you want to be able to read or write in a single I/O operation. Using a low timeout to send or receive large amounts of data may cause problems.
You may also use a ByteArrayStream with DataStream to ensure you received enough data before handling it.
Sample code:
Socket s = new Socket(host, 8080); // connect to our host
ByteArrayStream bas = new ByteArrayStream(256); // length 256
bas.readFully(s, 5, 0); /* reads from the given stream (our socket) until the maximum number of retries is reached, in this case, 5 times. */
DataStream ds = new DataStream(bas); /* the DataStream wraps the ByteArrayStream instead of the Socket. */
int value;
if (bas.available() >= 4) // do we have at least 4 bytes available?
	value = ds.readInt(); // yes! Then we can read an int from the buffer.
The ByteArrayStream is usually used as a read-only or write-only buffer, because its internal cursor is advanced every time a read or write operation is performed. However, you may use methods like setPos(), reset(), mark(), and skipBytes() to navigate through the buffer and be able to perform read and write operations on the same buffer.
Like a Vector, ByteArrayStream expands its internal buffer dynamically as needed (at a 20% rate).
ByteArrayStream has three constructors:
  • ByteArrayStream(byte[] buffer) Creates a ByteArrayStream, using the given byte array, which may not be empty, as the internal buffer. More often used to create read-only buffers.
  • ByteArrayStream(int size) Creates a ByteArrayStream, with a newly created (empty) internal byte array of the given size. More often used to create write-only buffers.
  • ByteArrayStream(byte[] buffer, int len) Creates a ByteArrayStream where len is the length to be read from the buffer.
Although the internal buffer is a private field, you may create a ByteArrayStream with your own byte array or use methods like getBuffer() and setBuffer() to have a reference for this field. But you can’t set the internal buffer to null or change its size without also updating its length and reseting its cursor position.
  • getBuffer() Returns a reference to the internal buffer. The actual length of the buffer may differ from the current buffer size. Use the getPos() method to get the correct value.
  • setBuffer(byte[] buffer) Assigns the given byte array as the internal buffer, reseting its current position and updating its size.
  • toByteArray() Returns a byte array, which is a copy of the contents of the internal buffer.
Methods inherited from Stream:
  • readBytes(byte[] buf, int start, int count) Copies count bytes from this byte array stream’s buffer to the given byte array, starting at position start. The contents of the buffer are not changed, but its cursor is moved count positions. It returns the number of bytes actually read or -1 if the end of the base stream was reached (if applicable to the base stream).
  • writeBytes(byte[] buf, int start, int count) Copies count bytes from the given byte array, starting at position start, to this byte array stream’s buffer, expanding it if necessary. The internal cursor is moved count positions.
  • close() Does nothing. The ByteArrayStream does not encapsulate an actual handler for I/O operations, so there’s nothing to be done.
Manipulating the internal array:
  • getPos() Returns the current cursor position in the buffer – the number of bytes read in a read-only, or the number of bytes written in a write-only way.
  • available() Returns the number of bytes available for reading or writing from the current cursor position (marked position of the buffer – current cursor position).
  • mark() Marks the current position as the buffer size and the cursor position to 0, so you may read from the buffer without going past the number of bytes actually available for reading.
  • reset() Resets the cursor position to 0 and sets the mark with the actual array size.
  • reuse() Shifts the bytes starting at the current position to the first position of the buffer – this way you may reuse the buffer without exceeding the buffer limits, which would make the buffer grow and consume more memory. This method also returns the number of bytes shifted, which may be used to move the cursor to the end of it and reuse the buffer without overwriting the shifted bytes.
  • skipBytes(int count) Moves the cursor count bytes from the current position. A positive value moves the cursor forward, while a negative value moves the cursor backwards. The cursor cannot be placed outside the stream limits, stopping at position 0 when moving backwards, or at the last position of the stream, when moving forward.
    This method does not throw an exception if the given argument exceeds the buffer bounds; instead, it returns -1.
  • setPos(int newPos) Sets cursor position to the given value (which should be between 0 and the position marked as the end of the buffer).
    This method does check if the given value is valid and throws an IOException if not.
  • setSize(int newSize, boolean copyOldData) Resizes the internal buffer if the given size is greater than the position marked as the end of the buffer. If copyOldData is true, it also copies the contents of the current buffer to the new buffer, starting from 0 to the current cursor position. If the given size is equal or smaller than the marked position of the buffer, nothing is changed.
  • readFully(Stream inputStream, int retryCount, int bufSize) Resets
    this byte array stream and reads the maximum amount of data available from the given stream, marking the last position used as the end of the buffer. The argument retryCount, if greater than 0, specifies the number of times the method should try to read data from the stream before returning. It will always try to read data at least once, even if retryCount is not a positive value. This is specially useful for streams that handle external connections, like Socket or PortConnector; you may specify 1 or any lower value for other streams, like File or PDBFile.
  • Be careful when using this method with streams like file. It may read the whole file at once, resulting in a great loss of performance and resources (the buffer may be resized several times). In the worst scenario, the device will run out of memory and the application will be halted. In this case, you should check the size of the stream before using readFully().
  • The argument bufSize specifies a minimum size for the internal buffer – therefore, if its value is equal or lower than the size of the buffer, it is not resized (i.e. you cannot use it to shrink the buffer). Otherwise, the buffer will be resized to match the given value.
    Please notice, the buffer will still grow if necessary, regardless of the bufSize value. So you cannot use it to limit the amount of data read, and you may specify a value 0 or lower if you do not wish to define a minimum size – this will not prevent its automatic resize.
  • The byte array stream is reseted at the beginning of readFully(), so you can’t use it in a loop as a buffer for a stream, you must use the argument retryCount instead.
  • Calling the method reset before readFully() is pointless, since this is already done by readFully().
  • After readFully() finishes reading from the internal buffer, it marks the current cursor position and sets the cursor to the first position of the array, so you may read from the buffer without going past the number of bytes actually available for reading.
For more details, check out totalcross.io.ByteArrayStream JavaDoc.

33 CompressedByteArrayStream

Creates a CompressedByteArrayStream, saving memory when reading and writting huge amount of data. Arrays of 16000 bytes will be created and each byte array will be compressed once filled and will be automatically decompressed on read. This saves space but adds a slowdown to the process. It is useful when transferring FTP files to/from the server.
This class cannot be used for output AND input, but only for output OR input, in an absolutely sequential mode (the skipBytes() method is NOT implemented): you must write everything, then read everything. To change the mode, use the setMode(READ_MODE or WRITE_MODE) method. No check is made to see if you’re in the right mode, but your program will probably crash if you do it in the wrong one.
Sample that transfers bytes to the server:
CompressedByteArrayStream cbas = new CompressedByteArrayStream(9); // default mode is WRITE_MODE
for (int i = 0; i < 50000; i++)
	cbas.writeLine("1234567890"); // already appends \r\n
cbas.flush();
cbas.setMode(CompressedByteArrayStream.READ_MODE); // prepare for read
ftp.sendFile(cbas, "bigfile.txt", true); 
// if you want to send another one, just call
cbas.setMode(CompressedByteArrayStream.WRITE_MODE); 
Sample that transfers bytes from the server:
CompressedByteArrayStream cbas = new CompressedByteArrayStream(9);
ftp.receiveFile("bigfile.txt", cbas);
cbas.flush();
String line;
while ((line = cbas.readLine()) != null)
	// do something with the line! 
Here is another fully functional sample:
int i;
String g = "1234567890";
CompressedByteArrayStream cbas = new CompressedByteArrayStream(9); // default mode is WRITE_MODE
for (i = 0; i < 50000; i++)
	cbas.writeLine(g); // already appends \r\n
cbas.flush();
Vm.debug("size: " + cbas.getCompressedSize() + " -> " + cbas.getSize());
String s;
for (i = 0; (s = cbas.readLine()) != null; i++)
	if (!g.equals(s))
		Vm.debug("error in " + i);
if (i != 50000)
	Vm.debug("i differs!");
cbas.close(); 
Note that, although the samples above use writeLine() and readLine(), you can store any kind of data. By attaching a DataStream it’s possible to read any data type from the stream.
CompressedByteArrayStream cbas = new CompressedByteArrayStream(5);
DataStream ds = new DataStream(cbas);
byte[] big = new byte[200000];
​
// fill big with something
ds.writeBytes(big);
for (int i = 0; i < 100000; i++)
{
	ds.writeInt(0x123456);
	ds.writeString("Michelle");
	ds.writeDouble(123.456d);
}  
​
// well, now we do something with these!
int realSize = cbas.getSize(); // just for fun
int compressed = cbas.getCompressedSize(); // just for fun
ds.readBytes(big);
for (int i = 0; i < 100000; i++)
{
	int i = ds.readInt();
	String love = ds.readString(); // Michelle
	double d = ds.readDouble();
} 
Call the close() method only when you’re completely done in using it: all the internal buffers will be released, and reading from it will crash your program. Note that the readLine() method will not work if there are any character with accentuation.
CompressedByteArrayStream has an enclosed class called DirectCharConverter, which implements a CharacterConverter that converts from char[] to byte[] which just casts the char to byte; thus, ignoring any non-ASCII character.
For more details, check out totalcross.io.CompressedByteArrayStream and
CompressedByteArrayStream.DirectCharConverter
JavaDocs.

34 RandomAccessStream

This class represents a stream that behaves like a large array of bytes and may be randomly accessed. For more details, check out totalcross.io.RandomAccessStream JavaDoc.

35 CRC32Stream

Computes CRC32 data checksum of a stream. The actual CRC32 algorithm is described in RFC 1952 (GZIP file format specification version 4.3). It can be used to get the CRC32 over a stream if used with input/output streams. For more details, check out totalcross.io.CRC32Stream JavaDoc.

36 Connector

Class used to open socket connections over WIFI. The other kind of connections are not used for the currently supported devices anymore. For more details, check out totalcross.io.Connector JavaDoc.

37 StreamConnectionNotifier

This is the base (abstract) class for connection notifiers. For more details, check out totalcross.io.StreamConnectionNotifier JavaDoc.

38 PortConnector

This class is used to accesses the device ports. This works only on Windows 32 and on JDK (in this case, it uses javax.comm, which MUST be installed separately). Use this to access the computer serial ports (COM 1 to COM 4), bluetooth, and USB ports. For more details, check out totalcross.io.device.PortConnector JavaDoc.

39 File

Being able to persist and retrieve data is very important for any application – and also a difficult task when you’re dealing with different platforms and OS versions, each one with your own features and flaws.
The File class was designed to allow you to browse and manipulate the file system – with its directories and archives – of any supported device in a transparent and reliable manner.
A File object represents an archive or a directory of the device’s file system, stored either on its non-volatile memory or in a memory card inserted on the device (currently supported only on Android).
When creating a new File object, you may specify the open mode using one of the following constants:
  • DONT_OPEN The path argument denotes either a directory or an archive that may not exist. The given path is stored but the file isn’t actually accessed (i.e. no resource is locked to manipulate the file).
    This mode may be used to perform operations that do not require the allocation of any resources, or to manipulate directories. In fact, it is the only mode that allows directory manipulation.
    The DONT_OPEN mode allows the exists(), rename(), delete(), listFiles(),
    createDir()
    , and isDir() methods to be called without requiring the file to be open for reading or writing.
Files created with mode DONT_OPEN don’t need to be closed since there are no resources to be released, but you can call the close() method anyway. However, calling the close() method twice for the same File object (i.e. trying to close a file that was already closed) throws an IOException regardless of the file open mode used.
  • CREATE The given path denotes an archive that may not exist. If the archive does not exist, it is created and opened with read and write access, otherwise, it justs opens the existing file with read and write access.
  • READ_WRITE The given path denotes an archive to be opened with read and write access. The archive must exist, otherwise a FileNotFoundException is thrown.
    Use this mode when you want to manipulate an archive only if it already exists - don’t forget to catch and handle the FileNotFoundException. This is more efficient than creating a file object in DONT_OPEN mode to check if the file exists and is not a directory.
  • CREATE_EMPTY The given path denotes an archive that may not exist. If the archive exists, it is replaced by an empty one, otherwise, a new one is created. Either way, the archive is opened with read and write access.
  • READ_ONLY Works only for files, must not be used for folders. With this mode it is possible to access an already opened file in any mode on all platforms. All the other modes are exclusive with each other except on Android, iOS, and Linux. Obvioulsy it is not possible to write in the file using this mode.
There is a constant called INVALID, whose value is 0, which means an invalid file open mode. This should not be used to open a file. This is used by TotalCross to indicate that a file is invalid after closing it.
File constructors:
  • File(String path, int mode, int slot) Creates a File object with the given path, using the given mode (which should be one of the above constants) and the specified slot which was used only on Palm OS, not supported anymore.
  • File(String path, int mode) Creates a File object with the given path, using the given mode (which should be one of the above constants) and the last available slot, which was used only on Palm OS, not supported anymore.
  • File(String path) Creates a File object with the given path, using the DONT_OPEN mode and the last available slot, which was used only on Palm OS, not supported anymore.
When creating a new file, you may start the path using the alias device/, which evaluates to the platform’s base user directory:
  • Java - / (current directory)
  • Windows 32 - / (root of the current drive)
  • iOS - in the documents folder of the current application
  • Android - /data/data/totalcross.app.<main class name> or /data/data/
    totalcross.app<application id>
    if using single package
  • WP8 -
On iOS, Android, and WP8, if you don’t specify a path, the file will be open in device/.
The method isCardInserted(int slot)works on Android and WP8, but its parameter is ignored.
To access the card on Android devices, prefix the path with /sdcard. Be sure that the card is NOT MOUNTED (being used by the desktop), otherwise your application will not have access to it. Some android devices have more than one sdcard, an internal and an external ones. On such devices, /sdcard is the internal one; to find the external path, you must get into the device because there’s no API to get it. For example, on Galaxy devices, it is /mnt/extSdCard.
The card on WP8 isn’t supported yet. Anyway, when it becomes possible, it will be only possible to read from it, since it’s read-only.
Always use / (slash) as the file separator, and never the \ (backslash), regardless of the target platform. Although the File constructor should be able to handle both slashes and backslashes right now, the backslash support may be removed in the future to increase performance. Besides, the slash is TotalCross’ official file separator, so you’d better get used to it.
Also, avoid using accentuated characters in the file name because it may not work on some devices.
Depending on the target file type (directory or archive) and the used open mode, some methods may not be used, or have a different behavior. Using a method under the wrong circumstances may result in an IOException.
To clarify, we’ll split the File methods into the four categories below (some methods from its superclass are omitted):
  • Archive and directory methods – May be used with any file, regardless of the type and the open mode.
  • Directory only methods – Can only be used with directories.
  • Archive only methods – Can only be used with open archives (cannot be used with File objects created with the DONT_OPEN mode).
  • Behavior depends on the file type – May be used with any file, but the method implementation and result depends on the file type.
Archive and directory methods:
  • exists() Returns true if the file exists and false, otherwise. Example:
    if (new File("dummy.txt").exists())
       ...
    
  • isDir() Returns true if and only if the file exists and is a directory, returning false, otherwise. To check if a file is an archive, you must test if the file is not a directory and if the file exists. Obviously this method should only be used in mode DONT_OPEN, since it will always return false in any other mode.
  • getPath() Returns the file’s path passed in the constructor.
  • getParent() Returns a new File object that references this file’s parent directory, or null if this file does not have a parent directory (it’s the root directory). The new file object is created in DONT_OPEN mode.
  • rename(String path) Renames the file with the given path.
    This operation closes the File object, so you should not perform any operations after a rename. You must give the full directory specification for the file to keep compatibility between all platforms. The file is automatically closed prior to renaming. After this operation, the File object is invalid. It cannot be used in READ_ONLY mode.
  • delete() Removes the file from the file system. If the file is a directory, it must be empty, otherwise an IOException will be thrown. If the file does not exist, a
    FileNotFoundException is thrown.
    This operation closes the file object, so you should not perform any operations after a delete. The file could have been opened in any of the available modes, except for READ_ONLY. Example:
    new File("/my/file.c").delete(); 
    
  • close() Closes the File object, releasing any resources held by this object. Trying to close a File object twice (e.g. calling close() twice, or rename() and close()) results in an IOException.
    This operation closes the File object, so you should not perform any operations after a close.
  • isEmpty() If the object is a file, returns true if the file has 0 bytes. If it is a folder, returns true if there are no files nor folders inside of it.
Directory only methods:
  • createDir() Creates the directory named by this path, including any necessary but nonexistent parent directories. It throws an IOException if the file was closed, if it was open in anything else than DONT_OPEN, if the directory already exists, or if the directories could not be created.
  • deleteDir() Deletes a directory and all its subdirectories and files. If you have problems trying to recreate the directory, be sure to call Vm.gc() after calling this method.
  • listFiles() Returns an array of strings denoting the files in this directory. The strings returned are the names of the files and directories contained within this directory. Paths are suffixed by a slash. If the folder is empty, the method returns an empty array whereas if the File object is not a directory, null is returned.
  • listFiles(String dir) Static method that returns a recursive list of all files inside the given directory (including it). The array is sorted upon return. If the folder is empty, the method returns an empty array whereas if the File object is not a directory, null is returned.
  • listFiles(String dir, boolean recursive) Same as above but also lists the files in the subdirectories if recursive is true.
  • listRoots() Lists the root drives. If there are no roots, returns null. Works on Windows 32 and Java platforms.
Archive only methods:
  • readBytes(byte[] b, int off, int len) Attempts to read len bytes from the file into the given byte array, starting at the array’s position off. Returns the number of bytes actually read.
  • writeBytes(byte[] b, int off, int len) Attempts to write len bytes to the file from the given byte array, starting at the array’s position off. Returns the number of bytes actually written.
  • flush() Flushes the stream, forcing any buffered bytes to be written on the file. This causes any pending data to be written to disk. Calling this method too much may decrease the performance. It has no effect on Java.
  • setPos(int pos) Moves the file pointer for read and write operations to the given position. The position passed is an absolute position, in bytes, from the beginning of the file. To set the position to just after the end of the file, you can call:
    file.setPos(file.getSize()); 
    
    Note: if you plan to change the file size using setPos(), you must write something on the new size to effectively change the size. For example, on some devices if you call setPos() and then read (assuming that the new position is past the end of the file, the read method will fail. Here’s a code that will change the size for sure:
    private static byte[] zeros = new byte[4096];
    public void setSize(int newSize)
    {
    	int size = f.getSize();
    	f.setPos(newSize - 1); // note: setPos(1) makes the file 2 bytes long (0, 1)
    	f.setPos(size);
    	for (int dif = newSize - size, n = 0; dif > 0; dif -= n)
    		n = f.writeBytes(zeros, 0, dif > zeros.length ? zeros.length : dif);
    } 
    
  • getAttributes() Gets the file attributes, returning the attributes constants ORed together (more information below). The file must be opened in a mode different from DONT_OPEN.
  • setAttributes(int attr) Sets the opened file attributes with the given attributes constants ORed together (more information below). The file must be opened in a mode different from DONT_OPEN.
  • getTime(byte whichTime) Gets the time for the given time type constant (more information below). The file must be opened in a mode different from DONT_OPEN.
  • setTime(byte whichTime, Time time) Sets the time for the given time type constant (more information below). The file must be opened in a mode different from DONT_OPEN and READ_ONLY.
  • setSize(int newSize) Sets the size of this file. If newSize is greater than the current size, the file is resized to the given size, otherwise the file is truncated, discarding all bytes from the position newSize to the current end of file.
  • getSize() If the file is a directory, returns the amount of free space available on this file system. If the file is an open archive, returns its size. If the file is neither a directory nor an open archive, an IOException is thrown. If the total amount is greater than 2 GB, 2 GB is returned. Examples:
    int freeSpace; 
    if (Settings.platform.equals("Win32") || Settings.platform.equals("Java"))
       freeSpace = new File("c:\\").getSize();
    else
       freeSpace = new File("\\").getSize(); // Posix 
    
  • copyTo(File dest) Copies the current file to the given one. You must close both files after calling this method. Here’s a sample of how to copy a file:
    File src = new File(srcFileName,File.READ_WRITE);
    File dest = new File(destFileName,File.CREATE_EMPTY);
    src.copyTo(dest);
    src.close();
    dest.close(); 
    
    This method is thread-safe.
  • moveTo(File dest) Moves the current file to the given one (the original file is deleted). You must explicitly close the destination file after this operation is done. Here’s a sample of how to move a file:
    File src = new File(srcFileName,File.READ_WRITE);
    File dest = new File(destFileName,File.CREATE_EMPTY);
    src.moveTo(dest); 
    // src.close(); - not needed! src was deleted
    dest.close();
    
    This method is thread-safe.
  • copy(String src, String dst) A handy method to call copyTo() creating two File instances and closing them. This method is thread-safe. If you want to have more control, use the copyTo() method.
  • move(String src, String dst) A handy method to call moveTo() creating two File instances and closing the destination file. The source file is erased if it exists. This method is thread-safe. If you want to have more control, use the other moveTo() method.
  • chmod(int mod) Applies the given permissions to this file. Works only on Unix-based operating systems: Linux, Android, and iOS. On JDK 1.6, the first number (user) is applied to all groups. Below you see a table with some chmod values (r = read, w = write, x = execute).
    Number Permission
    000 ---------
    400 r--------
    444 r--r--r--
    600 rw-------
    620 rw--w----
    640 rw-r-----
    644 rw-r--r--
    645 rw-r--r-x
    646 rw-r--rw-
    650 rw-r-x---
    660 rw-rw----
    661 rw-rw---x
    662 rw-rw--w-
    663 rw-rw--wx
    664 rw-rw-r--
    666 rw-rw-r--
    700 rwx------
    750 rwxr-x---
    755 rwxr-xr-x
    777 rwxrwxrwx
    The numbers represents a group of 3. The first number is the permission for user, the second number for group, and the third number for others. These are the possible permission values for each number:
    Permission Binary Decimal
    --- 000 0
    --x 001 1
    -w- 010 2
    -wx 011 3
    r-- 100 4
    r-x 101 5
    rw- 110 6
    rwx 111 7
    Failing to change the permission returns -1. Here’s a sample:
    try
    {
    	// testing in a folder
    	File f = new File(Settings.appPath);
    	add(new Label("mods of appPath = "+f.chmod(-1)),CENTER,CENTER);
    ​
    	// testing in a file
    	String name = "test";
    	f = new File(Settings.appPath+’/’+name,File.CREATE_EMPTY);
    	int m0 = f.chmod(777); // change it
    	int m1 = f.chmod(-1); // retrieve the changed value
    	add(new Label("mods of "+name+" = "+m0+" -> "+m1+" (777)"),CENTER,AFTER+5);
    }
    catch (Exception ee)
    {
    	MessageBox.showException(ee,true);
    }
    
    The modifiers you want to set (mod) must be given in DECIMAL, or -1 to just return the current ones. It returns the modifiers that were set before you called this method (or the current modifiers, if -1 is being passed). Some platforms may return more than 3 digits, indicating extra attributes (for example, if it’s a file or a directory).
  • readAndClose() Reads the entire file into a byte array and closes itself. A handy method that can be used like this:
    byte[] bytes = new File(...,File.READ_ONLY).readAndClose();
    
    The only drawback is that this method consumes lots of memory if the file is big; use it carefully.

File attributes

As shown above, you may use the methods getAttributes() and setAttributes() to retrieve or change the attributes of an archive (these operations are not supported for directories) with the following constants:
  • ATTR_ARCHIVE Often used to mark an archive for backup or removal.
  • ATTR_HIDDEN Hidden files are usually not listed on the file system.
  • ATTR_READ_ONLY Archives marked as read only cannot be opened with write access.
  • ATTR_SYSTEM Archives marked as system are normally used only by the OS, and should not be changed by the user.
These values are platform independent, and can be ORed together. However, these attributes are not supported by all platforms, and although you are not required to write platform specific code, you must be aware that you should not rely on certain attributes when writing cross-platform applications.
Below you can see the archive attributes support for each platform:
Java The methods have no effect on any file when running on JDK, it only checks if the object state and the received argument are valid.
Win32 Full support.
WP8 Full support.
iOS Supports only ATTR_READ_ONLY.
Android Supports only ATTR_READ_ONLY.
Linux Supports only ATTR_READ_ONLY.
The attributes ATTR_HIDDEN and ATTR_ARCHIVE are not supported by Unix based systems. Using them will not throw an exception, but it will have no effect on the file.

File times

Usually a file has three attributes which represents its creation time, the last time it was accessed and the last time it was modified. TotalCross provides the following constants to be used with the methods setTime() and getTime() in the whichTime parameter to retrieve and change these attributes:
  • TIME_CREATED Used to get or set the file’s creation time.
  • TIME_ACCESSED Used to get or set the time the file was last accessed.
  • TIME_MODIFIED Used to get or set the time the file was last modified.
  • TIME_ALL Used to set all of the above times with the same value.
Unfortunately these operations are not fully supported by all platforms, so you must be aware that you should not rely on these values when writing cross-platform applications.
Below you can see the times attributes support for each platform:
Java setTime() has no effect on any file when running on JDK, it only checks if the object state and the received argument are valid. getTime() will always return the time of the last modification if the object state and the received argument are valid.
Win32 Full support.
WP8 Full support.
iOS Full support with getTime(), but you cannot set the attribute TIME_CREATED with setTime().
Android Full support with getTime(), but you cannot set the attribute TIME_CREATED with setTime().
Linux Full support with getTime(), but you cannot set the attribute TIME_CREATED with setTime().
Unix based systems do not keep record of the file’s creation time. Attempting to change it will not thrown an exception, but it will have no effect on the file. Using the constant TIME_CREATED will return the last time the file was changed, which is updated when changes are made to the file’s inode (owner, permissions, etc.), and also when the contents of the file are modified. The constant TIME_MODIFIED returns the last time the contents of the file were modified.
Finally, here is an example showing data being read from a file:
File file = new File("/temp/tempfile", File.READ_WRITE);
byte b[] = new byte[10];
file.readBytes(b, 0, 10);
file.close();
file = new File("/temp/tempfile"); // opens in DONT_OPEN mode
file.delete(); 
For more details, check out totalcross.io.File JavaDoc.

40 LineReader

Class used to read lines ending with \r\n (enter/linefeed) or \n (linefeed) from a stream. Consecutive newlines are skipped. This class does not work well with multi-byte characters when the second byte contains the delimiter or enter/linefeed.
Here’s a sample:
LineReader reader = new LineReader(new File("text.txt",File.READ_WRITE));
String line; 
while ((line = reader.readLine()) != null) 
{ 
	// ... do whatever you want with the line. 
}
Note that this class already uses a buffer for faster detection of the newline. Don’t use LineReader with a BufferedStream, it’s nonsense and it will throw a warning on the desktop.
For more details, check out totalcross.io.LineReader JavaDoc.

41 TokenReader

This is a LineReader subclass used to read an array of tokens in a line ending with \r\n (enter/linefeed) from a stream. Consecutive newlines are skipped. This class does not work well with multi-byte characters when the second byte contains the delimiter or enter/linefeed.
The usual way to parse a CSV file is:
LineReader reader = new LineReader(new File("text.txt",File.READ_WRITE)); 
String line;
String[] tokens;
while ((line = reader.readLine()) != null)
	tokens = Convert.tokenizeString(line, "\n,");   
Using this class takes less memory, because the line is read in tokens. For example, suppose a line contains 200 chars, and splitting them contains 10 tokens of 20 chars each. Using the first approach (readline()/tokenizeString()), the readline() will create a string with 200 chars, then that string will be tokenized into 10 smaller strings of 20 chars each.
Using this class, it will read the 10 tokens of 20 chars each directly, no longer having to create the temporary string of 200 chars.
The delimiter can be any character except for \r and \n. Note that two consecutive delimiters are considered a single token. So ;a;; is returned as {"","a","",""}.
Here’s a sample that parses the input from a file:
TokenReader reader = new TokenReader(new File("text.csv",File.READ_WRITE), ’,’);
String[] tokens;
while ((tokens = reader.readTokens()) != null)
{
   // ... do whatever you want with the tokens.
}
And here’s another sample that parses from a String:
String lines = "a;;;a;\na;;;a; \nb;;b;b;b \nb;;b;b;b;\nb\nb;\n;b\n;\n b ;\n b \n b \n b \nb \n b";
String ll[] = Convert.tokenizeString(lines,’\n’);
TokenReader tk = new TokenReader(new ByteArrayStream(lines.getBytes()),’;’);
tk.doTrim = true;
String[] line;
for (int j =0; ((line = tk.readTokens()) != null); j++)
{
	Vm.debug(’"’+ll[j]+’"’);
	for (int i =0; i < line.length; i++)
	Vm.debug(i+": ’"+line[i]+"’");
	Vm.debug("");
} 
The output is:
"a;;;a;"
0: ’a’
1: ’’
2: ’’
3: ’a’
4: ’’
​
"a;;;a; "
0: ’a’
1: ’’
2: ’’
3: ’a’
4: ’’
​
"b;;b;b;b "
0: ’b’
1: ’’
2: ’b’
3: ’b’
4: ’b’
​
"b;;b;b;b;"
0: ’b’
1: ’’
2: ’b’
3: ’b’
4: ’b’
5: ’’
​
"b"
0: ’b’
​
"b;"
0: ’b’
1: ’’
​
";b"
0: ’’
1: ’b’
​
";"
0: ’’
1: ’’
​
" b ;"
0: ’b’
1: ’’
​
" b "
0: ’b’
​
"  b "
0: ’b’
​
" b  "
0: ’b’
​
"b  "
0: ’b’
​
"  b"
0: ’b’
Note that this class already uses a buffer for faster detection of the newline and delimiters. Don’t use TokenReader with a BufferedStream, it’s nonsense and it will throw a warning on the desktop, such as with its superclass LineReader.
For more details, check out totalcross.io.TokenReader JavaDoc.

42 GPS

GPS is a class that retrieves GPS coordinates read from Android, WP8, and iOS native API. This class only retrieves data updating the internal fields. If you want to display that data, you may use the GPSView class.
If the GPS fails connecting to the satellites, and the phone has signal, you can use the cell tower location as a rough location. The precision varies between 50m to 3km, depending on the phone location. In this case, you can get the latitude and longitude using CellInfo.toCoordinates() on Android and Windows 32. This won’t work on other platforms. Don’t forget to turn on the GPS, going to somewhere similar to Settings / Security & Location / Enable GPS satellites. You won’t be able to use if if it’s off in the settings.

GPS class

Here is an example of GPS usage:
new Thread()   
{
	public void run()
	{
		gps = new GPS();
		for (int i = 0; i < 60*2 && gps.location[0] == 0; i++) // wait 60s
		{
			Vm.safeSleep(500);
			try
			{
				gps.retrieveGPSData();
			}
			catch (Exception eee)
			{
				eee.printStackTrace();
				break;
			}
		}
	}
}.start(); 
For more details, check out totalcross.io.device.gps.GPS JavaDoc.

GPSView class

This control displays the GPS coordinates read, using the GPS class to show the values on labels. You can use the GPS class standalone if you wish. The field gps is the class used to retrieve the GPS coordinates. One way to use this class is:
add(gps = new GPSView(1000),LEFT,TOP); 
For more details, check out totalcross.io.device.gps.GPSView JavaDoc.

GoogleMaps class

Shows a Google Maps viewer on a separate window. Internet connection is required. Currently works on Android and iOS only. Pressing back returns to the application on Android. On iOS you must do a double-click on the home button to open the task manager to return to your application.
For more details, check out totalcross.map.GoogleMaps JavaDoc.

43 RadioDevice

This class provides access to the device’s radios and information about their status. All methods are static. For more details, check out totalcross.io.device.RadioDevice JavaDoc.

Part V. COMPRESSION

Overview

This section covers the usage of classes that handle data compression, inflating compressed data or deflating uncompressed data using streams.

44 CompressedStream

Abstract class used as the basis for compression filters, such as ZLibStream and GZipStream. It is the base class for stream filters that perform data compression and decompression.
Instances of subclasses of CompressedStream cannot be used for both read and write operations. At the object creation, you MUST choose if the new object is going to be used either for reading or writing compressed data, by passing one of the following constants to the class constructor:
  • DEFLATE Used to create a stream for data compression. In this mode, you may ONLY use write operations, that is, you may NOT use the method readBytes().
  • INFLATE Used to create a stream for data decompression. In this mode, you may ONLY use read operations, that is, you may NOT use the method writeBytes().
Subclasses of CompressedStream that implements algorithms with different levels of compression, should always use a constant default value for data compression.
Also notice that closing a compressed stream (using its close() method) DOES NOT affect the underlying stream (the stream passed to the constructor). To also close the underlying stream, you must call its own close() method after closing the compressed stream.
For more details, check out totalcross.util.zip.CompressedStream JavaDoc.

45 ZLibStream

Subclass of CompressedStream that implements data compression using the zlib algorithm.
For more details, check out totalcross.util.zip.ZLibStream JavaDoc.
“zlib is designed to be a free, general-purpose, legally unencumbered -- that is, not covered by any patents - lossless data-compression library for use on virtually any computer hardware and operating system. The zlib data format is itself portable across platforms.” - http://www.zlib.net/

46 GZipStream

Subclass of CompressedStream that implements data compression using the gzip algorithm.
For more details, check out totalcross.util.zip.GZipStream JavaDoc.
“gzip (GNU zip) is a compression utility designed to be a replacement for compress. Its main advantages over compress are much better compression and freedom from patented algorithms. It has been adopted by the GNU project and is now relatively popular on the Internet.” - http://www.gzip.org/

47 ZipStream

This class implements a stream filter for reading and writing files in the ZIP file format. Currently supports only compressed entries. For more details, check out totalcross.util.zip.ZipStream JavaDoc.

48 ZipEntry

The ZipEntry class is used to represent a ZIP file entry. For more details, check out totalcross.util.
zip.ZipEntry
JavaDoc.

49 ZipFile

This class represents a ZIP file. For more details, check out totalcross.util.zip.ZipFile JavaDoc.

50 ZLib

Utility class that provides static methods to perform full stream compression and decompression using the zlib algorithm.
It has the following methods:
  • deflate(Stream in, Stream out) Deflates the given stream in with the specified compression level, writing the result to the given stream out. Compressed data will be generated in zlib format using the default strategy and the default compression level.
  • deflate(Stream in, Stream out, int compressionLevel) Deflates the given stream in with the specified compression level, writing the result to the given stream out. Compressed data will be generated in zlib format using the default strategy.
  • deflate(Stream in, Stream out, int compressionLevel, int strategy,
    boolean noWrap)
    Deflates the given stream in using the specified strategy and compression level, writing the result to the given stream out. If nowrap == true then the zlib header and checksum fields will not be used in order to support the compression format used in both gzip and pkzip.
    The compression level value must be between -1 and 9, where:
    • -1: Default compression level.
    • 0: Storage only, no compression.
    • 1 – 9: Defines nine levels of compression, being 1 the less efficient (also the fastest) and 9 the most efficient (also the slowest).
    You may also use one of the constants below:
    • NO_COMPRESSION Constant for compression level 0.
    • BEST_SPEED Constant for compression level 1.
    • BEST_COMPRESSION Constant for compression level 9.
    • DEFAULT_COMPRESSION Constant for the platform’s default compression level (usually 5 or 6), same as -1.
    The compression strategy value must be one of the following:
    • DEFAULT_STRATEGY Default compression strategy.
    • FILTERED Compression strategy best used for data consisting mostly of small values with a somewhat random distribution.
    • HUFFMAN_ONLY Compression strategy for Huffman coding only.
  • inflate(Stream in, Stream out, int sizeIn) Attempts to read the number of bytes specified by sizeIn from the the given stream in, inflating and writing to the given stream out. If sizeIn is -1, it will attempt to fully read the stream.
  • inflate(Stream in, Stream out) Attempts to fully read the given stream in, inflating and writing to the given stream out. It’s equivalent to the calling of the above method passing the value -1 for sizeIn.
  • inflate(Stream in, Stream out, int sizeIn, boolean noWrap) Attempts
    to read the number of bytes specified by sizeIn from the the given stream in, inflating and writing to the given stream out. If sizeIn is -1, it will attempt to fully read the stream. If the parameter noWrap == true then the zlib header and checksum fields will not be used. This provides compatibility with the compression format used by both gzip and pkzip.
    Note: When using the noWrap option it is also necessary to provide an extra "dummy" byte as input. This is required by the zlib native library in order to support certain optimizations.
For more details, check out totalcross.util.zip.ZLib JavaDoc.

51 GZip

Utility class that provides static methods to perform full stream compression and decompression using the gzip algorithm. The GZip algorithm is provided with the ZLib library.
It has the following methods:
  • deflate(Stream in, Stream out) Deflates the given stream in writing the result to the given stream out.
  • inflate(Stream in, Stream out, int sizeIn) Attempts to read the number of bytes specified by sizeIn from the the given stream in, inflating and writing to the given stream out. If sizeIn is -1, it will attempt to fully read the stream.
  • inflate(Stream in, Stream out) Attempts to fully read the given stream in, inflating and writing to the given stream out. It’s equivalent to the calling the above method passing the value -1 for sizeIn.
The classes ZLib and GZip interfaces are almost identical, the only difference is that you may specify the desired compression level when deflating with ZLib.
For more details, check out totalcross.util.zip.GZip JavaDoc.

52 Checksum

Abstract class to compute a data checksum used by checked input/output streams. A data checksum can be updated by one byte or with a byte array. After each update the value of the current checksum can be returned by calling getValue(). The complete checksum object can also be reset so it can be used again with new data.
For more details, check out totalcross.util.zip.Checksum JavaDoc.

53 Adler32

Computes an Adler-32 checksum for a stream of data. An Adler-32 checksum is not as reliable as a CRC32 checksum, but a lot faster to compute.
The specification for Adler-32 may be found in RFC 1950 (ZLIB Compressed Data Format Specification version 3.3).
From that document:
"ADLER32 (Adler-32 checksum) This contains a checksum value of the uncompressed data (excluding any dictionary data) computed according to Adler-32 algorithm. This algorithm is a 32-bit extension and improvement of the Fletcher algorithm, used in the ITU-T X.224 / ISO 8073 standard.
Adler-32 is composed of two sums accumulated per byte: s1 is the sum of all bytes, s2 is the sum of all s1 values. Both sums are done modulo 65521. s1 is initialized to 1, s2 to zero. The Adler-32 checksum is stored as s2*65536 + s1 in most-significant-byte first (network) order."
"8.2. The Adler-32 algorithm
The Adler-32 algorithm is much faster than the CRC32 algorithm yet still provides an extremely low probability of undetected errors.
The modulo on unsigned long accumulators can be delayed for 5552 bytes, so the modulo operation time is negligible. If the bytes are a, b, c, the second sum is 3a + 2b + c + 3, and so is position and order sensitive, unlike the first sum, which is just a checksum. That 65521 is prime is important to avoid a possible large class of two-byte errors that leave the check unchanged (the Fletcher checksum uses 255, which is not prime and which also makes the Fletcher check insensitive to single byte changes 0 <-> 255) .
The sum s1 is initialized to 1 instead of zero to make the length of the sequence part of s2, so that the length does not have to be checked separately (any sequence of zeroes has a Fletcher checksum of zero)."
This class extends the class Checksum. For more details, check out totalcross.util.zip.
Adler32
JavaDoc.

54 CRC32

Computes CRC32 data checksum of a data stream. The actual CRC32 algorithm is described in RFC 1952 (GZIP file format specification version 4.3). It can be used to get the CRC32 over a stream if used with checked input/output streams. It extends the abstract class Checksum. All the methods of Adler32 are avaiable for this class, except that they are used for a CRC32. For more details, check out totalcross.util.zip.CRC32 JavaDoc.

Part VI. NET

55 Socket

The Socket class allows you to open TCP/IP connections from your device. To be able to establish a connection with a particular server, both the server and the device must be connected to a common network (e.g. a local network that connects your computers and devices, or the Internet).
A Socket object denotes a client-server connection over a network using the TCP/IP protocol, where the device acts as a client, which connects to a specific server port.
The Socket class does not provide many methods besides the ones inherited from Stream. After creating a socket, you’ll usually just perform read and write operations, closing the socket after you’re done.
However, streams that handle remote connections are more likely to fail due to hardware and communication problems, so we shouldn’t handle socket operations the same way we do with file operations.
In TotalCross, socket’s read and write operations are blocking with a timeout – that means that socket methods will block the thread until the operation is completed, or the timeout for the operation is reached. If the operation is completed, the method returns immediately, regardless of the amount of time left. Otherwise, the method will just return the amount of data processed.
It’s important to notice that no exception is thrown if the method times out. This is just a way to prevent your thread from being blocked indefinitely because of communication problems. You may just keep executing the same method until it finishes processing all the data.
Socket constructors:
  • Socket(String host, int port, int timeout, boolean noLinger)
    Creates a new socket that attempts to establish a connection by looking up the given host and performing the 3 way TCP/IP handshake. The argument port specifies the server port to connect to, while timeout specifies the timeout for this operation in milliseconds. The argument noLinger indicates if a socket upon close should shutdown the connection immediately or check for any server response before closing.
  • Socket(String host, int port, int timeout) Same as the above, but uses the default value false for the argument noLinger.
  • Socket(String host, int port) Same as the above, but uses the default value 1500 (milliseconds) for the argument timeout.
  • Socket(String host, int port, int timeout, String params) Opens a
    socket with additional parameters. Each parameter is specified in a key=value form and separated by a ; from the next parameter. For example: p1=v1;p2=v2. If true, the socket is closed immediately, and no ack is waited from the server. Note that this must be done for the very first socket creation per application.
You cannot open a socket before the main event loop. In other words, you cannot open a socket in the app’s constructor, but you can in the initUI() method.
The socket general behavior, including the timeout for read and write operations, are stored in the following public fields:
  • readTimeout The timeout value for read operations. Its default value is Socket.
    DEFAULT_READ_TIMEOUT
    .
  • writeTimeout The timeout value for write operations. Its default value is Socket.
    DEFAULT_WRITE_TIMEOUT
    .
Usually you should not worry about the read and write timeouts. The default values will be fine in most cases. However, you may increase the timeout value if you experience problems with slow connections.
Reducing the timeout value is usually a bad idea. If your device has a high speed connection, the read and write methods should also be fast and return before the timeout is reached. However, reducing the timeout value won’t give you any benefit, and may even decrease your program performance.
Methods for read and write operations:
  • readBytes(byte[] buf) Attempts to read enough bytes from this socket to fill the given buffer.
  • readLine() Attempts to read a line of text from this socket. A line is a sequence of characters delimited by any character lower than blank. This method correctly handles newlines with \n or \r\n.
  • writeBytes(byte[] buf) Attempts to write the contents of the given buffer to this socket.
  • writeBytes(String s) Attempts to write the given string to this socket.
Except for the method readLine(), the methods above are just available for convenience and may be replaced by read/write calls inherited from Stream. This has a cost however - using these methods increases the number of method calls for each read/write operation by one (they actually just call the Stream methods). If your application makes heavy use of sockets, you may avoid using these methods to improve its performance.
For more details, check out totalcross.net.Socket JavaDoc.

56 ServerSocket

This class implements server sockets. A server socket waits for requests to come in over the network. It may then accept the incoming TCP/IP connection and perform some operation based on that request, possibly returning a result to the requester.
ServerSocket constructors:
  • ServerSocket(int port) Attempts to open a server socket at the specified port number. By default, the maximum number of simultaneous connections allowed is DEFAULT_BACKLOG and the default timeout for accept is DEFAULT_SOTIMEOUT, and the server is not bound to any specific local address. The port number must be between 0 and 65535.
  • ServerSocket(int port, int timeout) Same as the above, but you may also specify the timeout value, in milliseconds, for the accept operation. This value must be a positive value, or 0 to wait forever.
  • ServerSocket(int port, int timeout, String addr) Same as the above, but you may also specify a local address, which the server should bind to. If the argument addr has a null value, it is ignored and the server is not bind to any address.
  • ServerSocket(int port, int timeout, int backlog, String addr)
    Same as the above, but you may also specify the maximum number of simultaneous connections allowed with the argument backlog, which must have a positive value.
You may retrieve the address and port values of this ServerSocket with getHost() and
getLocalPort()
.
After creating a server socket, you may use the method accept() to wait for incoming connections. This method blocks the thread for the amount of time specified by the timeout value passed to the constructor, returning a null value when the timeout is over, or until a connection request is received and accepted, returning a socket instance representing the new connection.
The returned object is always a valid Socket instance, that may be used to transfer data between this server and the client that requested the connection, and that should be closed when no longer needed.
You should never use blocking operations on threads handling events and/or the graphical interface, otherwise the user won’t be able to interact with the application. Take a look at the source code of the sample ServerSocketTest.
Finally, you may use the method close() to close this server socket, releasing any associated resources.
Remember to close any sockets associated to this server socket before closing it. Otherwise all open sockets will throw an IOException.
For more details, check out totalcross.net.ServerSocket JavaDoc.

57 SOAP Webservices

“SOAP Version 1.2 (SOAP) is a lightweight protocol intended for exchanging structured information in a decentralized, distributed environment. It uses XML technologies to define an extensible messaging framework providing a message construct that can be exchanged over a variety of underlying protocols. The framework has been designed to be independent of any particular programming model and other implementation specific semantics.” - Definition from SOAP Version 1.2 Part 1: Messaging Framework (Second Edition) - W3C Recommendation 27 April 2007
Because of its implementation independence, the SOAP protocol is widely used on the implementation of Web Services.
For more details, check out the totalcross.xml.soap package JavaDocs.

The SOAP class

This class represents a SOAP message that, when executed, is sent to the server in a HTTP request. The server response is then received, processed, and the answer made available.
Before creating a instance of SOAP, you may set the following class fields:
  • prefix The prefix string used to start the request. Note that it uses UTF-8, so unicode characters are not supported. Its default value is:
    <?xml version="1.0" encoding="UTF-8"?>
    <soapenv:Envelope xmlns:soapenv="
    http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <soapenv:Body>
  • suffix The suffix string used to finish the request. Its default value is:
    </soapenv:Body>
    </soapenv:Envelope>
  • debug Changing this value to true enables debug mode, which writes XML parsing information on the debug console (or the default error output when running on JDK). You may also set HttpStream.debugHeader = true.
    Caution: don’t use this on device because it increases a lot the memory usage.
  • disableEncoding The SOAP request will ask the server for GZip or ZLib encoded response by default. To disable encoding, set this field to true.
To create a new SOAP instance, you must use the following constructor:
  • SOAP(String mtd, String uri) Creates a new SOAP request where mtd specifies the remote method to be executed, and uri, the address of the Web Service. The default namespace will be used, along with an open timeout of 25 seconds, and a read and write timeout of 60 seconds.
After creating a new SOAP object, you may set some of its following instance fields:
  • wasCompressionUsed A flag that indicates if the SOAP connection was using either GZip or ZLib. This is a ready-only flag, set during the execute() method, and changing its value has no effect.
  • alternativeReturnTag By default, the XML parser used by SOAP will recognize as an answer tag any tags whose name ends with “result” or “return” (ignoring the case). This field, if set, specifies an alternative answer tag name, recognizing any XML element that ends with the specified value as an answer tag.
    alternativeReturnTag IS CASE SENSITIVE, unlike the SOAP default tag names. Also, alternativeReturnTag does not replace the default values. It’s just a new value with higher priority over the default ones.
  • namespace String that identifies the service’s namespace. Its default value is:
    http://schemas.xmlsoap.org/soap/
    .
  • openTimeout Specifies the connection open timeout value in milliseconds. Its default value is defined by the constant DEFAULT_OPEN_TIMEOUT (25 s).
  • readTimeout Specifies the connection read timeout value in milliseconds. Its default value is defined by the constant DEFAULT_READ_TIMEOUT (60 s).
  • writeTimeout Specifies the connection write timeout value in milliseconds. Its default value is defined by the constant DEFAULT_WRITE_TIMEOUT (60 s).
  • mtd Stores the name of the remote method.
  • uri Stores the address to the Web Service.
You may change both the mtd and the uri values before executing the request. Although this seems to be pointless, because these values are passed to the constructor.
If the remote method expects any arguments, you must set them using the setParam() method. However, there are several versions of this method to cover different argument types. Listing all of them would be pointless, so we’ll define a generic type that we’ll refer as <type>, and may be one of the of the following:
  • int
  • boolean
  • double
  • String
So, whenever a SOAP method is described like setParam(<type> param), you can safely assume there are four versions of this method, one for each type listed above. Other type of parameters can be passed using similar methods. Unicode characters are not accepted because the default header uses UTF-8.
SOAP methods for parameters setting:
  • setParam(<type> param) Sets the given value in the method’s argument order.
  • setParam(<type>[] param) Sets the given array in the method’s argument order.
  • setParam(<type> param, String paramName) Sets the given value, identifying it with the given parameter name.
  • setParam(<type>[] param, String paramName) Sets the given array value, identifying it with the given parameter name.
  • setParam(byte[] param, String paramName) Sets a byte array value, identifying it with the given parameter name.
  • setParam(String param, String paramName, String paramType) Sets a
    String
    parameter in the method, identifying it with the given name and specifying its type as the given one.
  • setParam(String[] param, String paramName, String paramType) Sets a String array value, identifying it with the given parameter name and specifying its type as the given one.
  • setObjectParam(String paramName,String[] fieldNames,
    String[] fieldValues)
    Sets an Object parameter value, by specifying its parameter name, field names and field values.
  • setObjectArrayParam(String paramName,String[] fieldNames,
    Vector fieldValues)
    Sets an Object array parameter value, by specifying its parameter name, field names and field values.
The only thing left to do now is to execute the request and check the service’s answer:
  • execute() This method simply executes the prepared SOAP request.
  • getAnswer() To retrieve the method’s answer, you must call this method after execute(). The return type of this method is Object, but it may return only three different values (the values may be escaped; use totalcross.ui.html.EscapeHtml.unescape() to convert it back). The remote method return type is known, so you may just typecast the Object returned by getAnswer() to String or String array, converting its values if necessary. The possible returns are:
    • null When the remote method has no return value or the execution failed for any reason.
    • String When the remote method returns a single value. If the expected value is not String, you must convert the received String to the right type (e.g. if you’re expecting an int value, you can use Convert.toInt()).
    • String[] When the remote method returns an array or an Object. If the expected value is not a String array, you must convert each value of the array to the right type. If it’s an object, the array contains its field values.
  • useProxy(String address, int port, String username,
    String password)
    Sets the proxy settings to be used by the SOAP connection. You may optionally set the username and password for basic proxy authorization. Proxy authorization is disabled if either username or password is null. In this method, address is the proxy address port and port is the proxy port.

58 HTTPS / SSL

Overview

The TotalCross SSL native library is a wrapper library of the great axTLS package. The axTLS embedded SSL project written by Cameron Rich is a highly configurable client/server TLSv1 library designed for platforms with small memory requirements (see http://axtls.cerocclub.com.au/index.htm for more details).
The original package supports Linux, Win32, Solaris & Cygwin.
This native library adds support for SSL (Secured Sockets Layer) communications to secure data transfers between authenticated devices and/or servers.
For more details, check out the totalcross.net.ssl package JavaDocs.

Security background

For general information about the features of TLS (Transport Layer Security) and its usage, you may read the Wiki page available at http://en.wikipedia.org/wiki/Transport_Layer_Security. If you are lucky, you may even read a good translation in your personal language.
The english version provides a basic protocol description for everyone. For those who want to go further, the reference is the TLS protocol version 1.0 RFC 2246 (http://tools.ietf.org/html/rfc2246).
Basically, TLS allows secured and authenticated communication between two components generally so-called client & server. It relies on X509 certificates, their associated private keys to encrypt and associated public keys to decrypt exchanged data. The certificates could be self signed or signed by an Authority known as CA (Certification Authority) that have to be trusted.
Insofar the subject of SSL based security is well documented on the web, we won’t go further in the TLS description and invite people interrested in diving more deeply in secured communications to read the plenty of articles, books and HOWTOs available on the Internet.

Generating security material

We will concentrate on the more general deployment of X509 client or server certificates signed by a Certification Authority (CA) (see http://en.wikipedia.org/wiki/X.509 for more details). First we will have to create our own CA, that could be replaced by any "well known" commercial CA such as Verisign, Thawte, etc, if you have the need for a public authority.
We will use openssl (www.openssl.org) as security engine to generate the security material involved in certificate based authentication/encryption. It’s a well spread SSL implementation providing powerfull tools to create and manage all kinds of security materials available on many platforms.

Create a private CA

Generate a self signed certificate that will be used as Certification Authority (CA). The authority will be valid for 10 years (approx 3650 days).
bash$ openssl req -new -x509 -days 3650 -keyout cakey.pem -out cacert.pem
Generating a 1024 bit RSA private key
.++++++
.....++++++
writing new private key to ’cakey.pem’
Enter PEM pass phrase: xxxxx
Verifying - Enter PEM pass phrase: xxxxx
-----
You are about to be asked to enter information that will be incorporated into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter ’.’, the field will be left blank.
-----
Country Name (2 letter code) [AU]:BR
State or Province Name (full name) [Some-State]:Rio de Janeiro state
Locality Name (eg, city) []:Rio de Janeiro
Organization Name (eg, company) [Internet Widgits Pty Ltd]:SuperWaba Ltda
Organizational Unit Name (eg, section) []:SuperWaba dev. department
Common Name (eg, YOUR name) []:SuperWaba Sample CA
Email Address []:guich@superwaba.com.br
The first entry is the private key password. The private key is used to sign other certificates to assert they are authentic. The private key is protected by a password as a further security because the CA private key is a main secret that have to be protected.
Next you will have to fill in X500 attributes describing the certificate subject. In our case, we enter information describing the SuperWaba CA (Certification Authority). Any agent trusting this CA, will authenticate certificates that have been signed by it through the CA embedded public key.
We now have two files, cakey.pem containing an encrypted version of the CA private key protected by a password and cacert.pem containing an X509 certificate embedding the CA public key that could be redistributed.
Finally, you have to create a text file named ca.srl with the content "00" for certicate signing counting, just execute the following command :
bash$ echo “00” > ca.srl

Create a client or server X509 certificate

First, you have to generate a new private key. SSL supports unencrypted and aes128/256 encrypted private keys.
bash$ openssl genrsa -aes128 -out mykey.pem 512
You may replace -aes128 by -aes256 for a stronger cipher, or remove -aes128 at all to generate a private key that is not encrypted. When you ask for an encrypted key, you have to enter a password used in the ciphering. The last option represents the key size in bits, values between 512 and 4096 bits for a higher security are accepted, but always keep in mind that higher security implies longer processing times especially critical on embedded devices.
Next you have to generate a certificate request. This file could be transmitted to one of the commercial CA companies for signing or could be signed by our previously created private CA.
bash$ openssl req -new -out my.req -key mykey.pem
Add -x509 if you want to generate a self signed certificate if you don’t want to use a CA at all (in this case, you may name the file mycert.pem rather than my.req). Insofar, the out file will contain a finalized self-signed certificate rather than a certificate request.
Enter all information describing your client or your server component. The certificate request will be stored in the file my.req.
Finally, we will sign the certificate with the CA.
bash$ openssl x509 -CA cacert.pem -CAkey cakey.pem -CAserial ca.srl -req -in my.req -out mycert.pem -days 1460
The cacert.pem is the CA certificate, cakey.pem its above created PEM encrypted private key, my.req is the certificate request file generated previously and mycert.pem is the resulting signed certificate that can be used for authentication and encryption during 1460 days (4 years).

Principle of an X509 authentication/encryption

X509 authentication/encryption is based on public/private key encryption that have a great characteristic. Indeed, the data ciphered by either key could only be deciphered by the other one.
A software component that has to be authenticated, such as a secured server, may now be configured to deliver to any client the previously generated certificate (contained in mycert.pem). It also has to load the associated certificate private key. That private key is used to cipher data transmited to the client. On the other side, the client uses the public key embedded in the accepted server certificate to decipher the data from the server and cipher the data to be sent back to the server so that the server can decipher with its private key. If the client is configured to trust any certificate that have been signed by the signing CA, it will be able to authenticate any certificate using the CA public key.

SSL features

The SSL package supports both PEM and DER encrypted materials.
DER (http://en.wikipedia.org/wiki/Distinguished_Encoding_Rules) is an ASN.1 encoding of information, PEM (http://en.wikipedia.org/wiki/Privacy_Enhanced_Mail) is a base64 encoding of a DER encoded data with a header "-----BEGIN " and a trailer "-----END " followed by a material type name. A PEM file may be de-encrypted or encrypted with AES128 or AES256 ciphers only.
LiteSSL also supports the pkcs8 encoding that is a private key encryption format. But it supports only one ciphering algorithm that is PBE-SHA1-RC4-128. Here is the command line to convert a PEM encoded private key into a pkcs8 encoded format. Always use the .p8 file suffix to identify the pkcs8 format.
bash$ openssl pkcs8 -topk8 -in mykey.pem -inform PEM -out mykey.p8
-outform DER -v1 PBE-SHA1-RC4-128
You will have to enter a password, that will be required to use the private key.
LiteSSL also supports pkcs12 that is a certificate/private key encryption format. But it supports only one ciphering algorithm that is PBE-SHA1-RC4-128. Here is the command line to convert certificate and its associate encoded private key into a pkcs12 encoded format. Always use the .p12 file suffix to identify the pkcs12 format.
bash$ openssl pkcs12 -export -in server.pem -out server.p12 -name
"myserver" -inkey server.key -certpbe PBE-SHA1-RC4-128 -keypbe
PBE-SHA1-RC4-128
You will have to enter a password, that will be required to use the private key embedded in the pkcs12 encoded file.

Restrictions

The Applet version is implemented on top of SUN’s JSSE. This TLS implementation has some limitations that prevent the use of some security material formats supported by SSL on devices. Thus, private keys have to be in pkcs8 format only. Moreover, they can’t be password protected. You have to add the -nocrypt option to the command line provided above to convert a PEM encoded private key to pkcs8 encoding.

SSL usage

The SSLUtil class provides functions to get information about the TLS stack layer.
The first class to instantiate is SSLClient or SSLServer (not currently supported). This class represents an SSL client or server context both inheriting from the SSLCTX class that provides many SSL context common services. The main feature concerns the security material loading. Use objLoad() to load material from files or memory. The arguments of this function are the material type (CA, X509 certificate, private keys, etc), the filename or the memory containing the material, and finally a password for private keys loading if they are password based encrypted.
To succeed the handshake with a server, you have to trust its self-signed certificate or trust the CA certificate who signed the server’s certificate. Use objLoad(SSL_OBJ_X509_CACERT, “cacert.pem”, null) to trust the server’s signing CA. If the server requires client authentication, you will have to send your own client certificate. Use objLoad(SSL_OBJ_X509_CERT, “mycert.pem”, null) to load your client certificate and objLoad(SSL_OBJ_RSA_KEY,
“mykey.pem”, “pass”)
to load the client certificate associated private key protected by the pass password.
Next, you have to call connect() on the context instance to create an SSL instance linked with a previously created socket. The SSL handshake starts immediatly to try to establish an authenticated/ciphered communication.
The SSL handshake succeeded if the connect() call returns an SSL instance and the
handshakeStatus()
function call on that instance returns SSL_OK. Consequently, you may check the subject of the peer certificate with the getCertificateDN() call to identify it and the context could be used to write and read ciphered data until the “dispose” call terminates the SSL communication. The peer receives a protocol alert to signal a link shutdown.
The SSL write of data returns the amount of bytes written or an error if the writing failed. The SSL read of data may return SSL_OK that indicates that the read is not yet terminated and may be called again to achieve the reading of a block of decipherable data.

59 e-mail

This chapter describes all classes in the totalcross.net.mail package, which provides support for sending and receiving e-mails.
First you need to configure the SMTP properties:
MailSession session = MailSession.getDefaultInstance();
session.put(MailSession.SMTP_HOST, new Properties.Str("smtp.sample.com")); // SMTP host address
session.put(MailSession.SMTP_PORT, new Properties.Int(25)); // unless noticed otherwise by the server, SMTP uses port 25
session.put(MailSession.SMTP_USER, new Properties.Str("foo@bar.com")); // usually the full e-mail address
session.put(MailSession.SMTP_PASS, new Properties.Str("VerySecurePassword")); // e-mail account password 
Sending a simple e-mail message:
Message myMessage = new Message(); // our new message
myMessage.from = new Address("foo@bar.com", "Mr. Foo"); // the e-mail address I want to be visible to the recipient and a personal name.
myMessage.addRecipient(RecipientType.TO, new Address("someone@somewhere.com", null)); // the recipient of the e-mail 
myMessage.subject = "Testing SMTP with authentication"; // e-mail subject
myMessage.setText("This is just a test message"); // simple e-mail with plain text
Transport.send(myMessage); // sends our message using the property Transport for this Message, and the properties defined on the default MailSession.
Sending an e-mail with attachments:
// text part 
Part textPart = new Part();
textPart.setText("Plain text"); // image part
Part imagePart = new Part();
File image = new File("/myImage.png", File.READ_WRITE); // keep file open!
imagePart.setContent(image, "image/png");
imagePart.fileName = "myImage.png"; 
​
// create the multiparts
Multipart multipart = new Multipart();
multipart.addPart(textPart);
multipart.addPart(imagePart); 
​
// create the message
Message myMessage = new Message();
myMessage.setContent(multipart);
myMessage.subject = "E-mail with attachment";
myMessage.from = new Address("from@someone.com", "Someone name");
myMessage.addRecipient(Message.RecipientType.TO, new Address("to@url.com", "ToMe")); 
​
Transport.send(myMessage); // send the message
image.close(); // close the image file AFTER the message is sent
Receive a message, and open the first one available:
MailSession session = MailSession.getDefaultInstance();
session.put(MailSession.POP3_HOST, new Properties.Str("pop3.sample.com"));
session.put(MailSession.POP3_PORT, new Properties.Int(110));
session.put(MailSession.POP3_USER, new Properties.Str("foo@bar.com"));
session.put(MailSession.POP3_PASS, new Properties.Str("VerySecurePassword"));
​
Store store = MailSession.getDefaultInstance().getStore("pop3");
store.connect();
Folder folder = store.getFolder("INBOX");
folder.open();
int messageCount = folder.getMessageCount();
if (messageCount > 0)
{
	Message msg = folder.getMessage(1);
	String msgContent = (String) msg.getContent();
	if (msgContent != null) 
		new MessageBox(msg.from.address, msgContent).popup();
} 
folder.close(true);
store.close();
This package has the following classes, whose JavaDocs in totalcross.net.mail should be read:

Address

This class represents an Internet e-mail address using the syntax of RFC822. Typical address syntax is of the form user@host.domain or Personal Name <user@host.domain>.

DataContentHandler

This is an abstract class that defines the basic interface for implementations of
DataContentHandler
.

BinaryContentHandler

This class is an implementation of DataContentHandler that handles MIME types handled as base64 encoded byte arrays. It also handle streams, reading from the input stream on demand to avoid excessive memory load. This will usually be slower than reading the whole content of the stream to a byte array and using it as the Part content.

TextContentHandler

This class is an implementation of DataContentHandler that handles textual (not encoded) MIME types. This class is similar to the previous one, except for the fact that if obj is not a Part in the writeTo() method, it will be transformed into a String.

DataHandler

This class maps a MIME type into an instance of a DataContentHandler.

MailSession

This class is used to store properties used by the messaging API.
The SMTP protocol provider supports the following properties, which may be set in the MailSession object. The properties are always set as strings; the Type column describes how the string is interpreted. For example, use
props.put("mail.smtp.port", "888"); 
to set the mail.smtp.port property, which is of type int.
Name Type Description
mail.smtp.user String Default user name for SMTP.
mail.smtp.password String SMTP Password for the default user name.
mail.smtp.host String The SMTP server to connect to.
mail.smtp.port int The SMTP server port to connect to, if the connect() method doesn’t explicitly specify one. Defaults to 25.
mail.smtp .connectiontimeout int Socket connection timeout value in milliseconds. Default is infinite timeout.
mail.smtp.timeout int Socket I/O timeout value in milliseconds. Default is infinite timeout.
mail.smtp.from String Email address to use for SMTP MAIL command. This sets the envelope return address. Defaults to msg.getFrom()[0] or ConnectionManager.getLocalHost(). NOTE: mail.smtp.user was previously used for this.
mail.smtp.auth boolean If true, attempt to authenticate the user using the AUTH command. Defaults to false.
mail.smtp.starttls .enable boolean If true, enables the use of the STARTTLS command (if supported by the server) to switch the connection to a TLS-protected connection before issuing any login commands. Note that an appropriate trust store must be configured so that the client will trust the server’s certificate. Defaults to false.
mail.smtp.starttls .required boolean If true, requires the use of the STARTTLS command. If the server doesn’t support the STARTTLS command, or the command fails, the connect method will fail. Defaults to false.
mail.smtp.ssl.port int The SMTP server port to connect to when STARTTLS is enabled, if the connect() method doesn’t explicitly specify one. Defaults to 587.
mail.smtp.ssl .socketFactory.class String If set, specifies the name of a class that extends the totalcross.net.ssl.SSLSocketFactory class. This class will be used to create SMTP SSL sockets.
The POP3 protocol provider supports the following properties, which may be set in the
MailSession
object. The properties are always set as strings; the Type column describes how the string is interpreted. For example, use
props.put("mail.pop3.port", "888");
to set the mail.pop3.port property, which is of type int.
Name Type Description
mail.pop3.user String Default user name for POP3.
mail.pop3.host String The POP3 server to connect to.
mail.pop3.port int The POP3 server port to connect to, if the connect() method doesn’t explicitly specify one. Defaults to 110.
mail.pop3 .connectiontimeout int Socket connection timeout value in milliseconds. Default is infinite timeout.
mail.pop3.timeout int Socket I/O timeout value in milliseconds. Default is infinite timeout.

Message

This class represents a MIME style e-mail message.

Multipart

Multipart is a container that holds multiple body parts.

Part

This class represents a MIME body part, which are contained by Multipart objects.

Folder

Folder is an abstract class that represents a folder for mail messages. Subclasses implement protocol specific Folders.

Pop3Folder

A POP3 Folder (can only be "INBOX").

Store

An abstract class that models a message store and its access protocol, for storing and retrieving messages. Subclasses provide actual implementations.

POP3Store

A POP3 Message Store. Contains only one folder, "INBOX". This class extends Store with the given difference, where in getFolder(String name), only the name "INBOX" is supported. If another name is passed, it will return null.

Service

An abstract class that models a messaging service and its access protocol, for accessing a mail session. Subclasses provide actual implementations.

Transport

An abstract class that models a message transport which extends Service. Subclasses provide actual implementations.

SMTPTransport

This class implements the Transport abstract class using SMTP for message submission and transport. It also doesn’t have a public constructor.

SMTPSSLTransport

This class implements the Transport abstract class using SMTP for message submission and transport over secure sockets. It extends SMTPTransport.

Part VII. PHONE

60 Dial

Used to dial a number in a smartphone. A single listener can receive messages from the system informing the current status. Currently works on Windows Phone 8, Android, and iOS. It is not possible to complete a call if there is no chip. The application goes to background and another window is opened.
For more details, check out totalcross.phone.Dial JavaDoc.

61 SMS

Class used to send and receive SMS messages. Currently supports Windows Phone 8 only.
For more details, check out totalcross.phone.SMS JavaDoc.

Part VIII. CRYPTOGRAPHY

Overview

TotalCross has support to cryptography in the crypto package. For more details, take a look at the totalcross.crypto package JavaDocs. It has three subpackages:
  • cipher Contains the cipher classes that will actually encrypt and decrypt data using AES and RSA. It has the classes:
    • AESCipher
    • AESKey
    • Cipher
    • Key
    • RSACipher
    • RSAPrivateKey
    • RSAPublicKey
  • digest Implements MD5 and SHA digest algorithms. It has the classes:
    • Digest
    • MD5Digest
    • SHA1Digest
    • SHA256Digest
  • signature Implements PKCS1 signature algorithm. It has the classes:
    • PKCS1Signature
    • Signature

62 Cipher

Cipher

This class provides the functionality of a cryptographic cipher for encryption and decryption.
If you get a CryptoException: Illegal key size, you must download strong cryptography files AFTER understanding that you are elligible to do so as stated in
http://java.sun.com/j2se/1.4.2/jre/README (search for ’Unlimited Strength Java Cryptography Extension’ - installation instructions are inside that topic).
There is a sample for the class Cipher, its subclasses and auxiliar classes in tc.samples. crypto.cipher.CipherTest.

AESCipher

This class implements the AES cryptographic cipher.
The Advanced Encryption Standard (AES) is a specification for the encryption of electronic data established by the U.S. National Institute of Standards and Technology (NIST) in 2002. The algorithm described by AES is a symmetric-key algorithm, meaning the same key is used for both encrypting and decrypting the data. AES is based on a design principle known as a substitution-permutation network, and is fast in both software and hardware. More information can be found in http://en.wikipedia.org/wiki/Advanced_Encryption_Standard.

RSACipher

This class implements the RSA cryptographic cipher. It is similar to AESCipher.
RSA is an algorithm for public-key cryptography that is based on the presumed difficulty of factoring large integers, the factoring problem. RSA stands for Ron Rivest, Adi Shamir and Leonard Adleman, who first publicly described it in 1977. A user of RSA creates and then publishes the product of two large prime numbers, along with an auxiliary value, as their public key. The prime factors must be kept secret. Anyone can use the public key to encrypt a message, but with currently published methods, if the public key is large enough, only someone with knowledge of the prime factors can feasibly decode the message. Whether breaking RSA encryption is as hard as factoring is an open question known as the RSA problem. More information can be found in http://en.wikipedia.org/wiki/RSA.

Key

This class defines a cryptographic cipher key for type safety.

AESKey

This class implements the AES cryptographic cipher key. It extends Key.

RSAPrivateKey

This class implements the RSA cryptographic cipher private key. It extends Key.

RSAPublicKey

This class implements the RSA cryptographic cipher public key. It extends Key.

63 Digest

Digest

This class provides the functionality of a message digest algorithm.
There is a sample for its classes in tc.samples.crypto.digest.DigestTest.

MD5Digest

This class implements the MD5 message digest algorithm.
It extends Digest.
The MD5 Message-Digest Algorithm is a widely used cryptographic hash function that produces a 128-bit (16-byte) hash value. MD5 has been employed in a wide variety of security applications, and is also commonly used to check data integrity.

SHA1Digest

This class implements the SHA-1 message digest algorithm. It is similar to MD5Digest.
In cryptography, SHA-1 is a cryptographic hash function. SHA stands for "secure hash algorithm". The three SHA algorithms are structured differently and are distinguished as SHA-0, SHA-1, and SHA-2. SHA-1 is very similar to SHA-0, but corrects an error in the original SHA hash specification that led to significant weaknesses. The SHA-0 algorithm was not adopted by many applications. SHA-2 on the other hand significantly differs from the SHA-1 hash function.
SHA-1 is the most widely used of the existing SHA hash functions, and is employed in several widely used applications and protocols, including TLS and SSL, PGP, SSH, S/MIME, and IPsec.

SHA256Digest

This class implements the SHA-256 message digest algorithm. It is similar to MD5Digest.
SHA-256 is part of a family of two similar hash functions with different block sizes, known as SHA-256 and SHA-512. They differ in the word size; SHA-256 uses 32-bit words where SHA-512 uses 64-bit words. There are also truncated versions of each standardized, known as SHA-224 and SHA-384. All of them are part of the SHA-2.

64 Signature

Signature

This class provides the functionality of a signature algorithm for signing and verifying.

PKCS1Signature

This class implements the PKCS #1 signature algorithm.
It extends Signature and there is a sample for its use in tc.samples.crypto.signature.
SignatureTest
.

Part IX. BLUETOOTH

Overview

This package describes the classes to use Bluetooth.
In what follows, the classes SerialPortClient and SerialPortServer are not described since they are just simple subclasses of Stream and StreamConnectionNotifier, respectively.
For more details, check out the totalcross.io.device.bluetooth package JavaDocs.

65 DataElement

This class is defined by the JSR-82 specification Java™ APIs for Bluetooth™ Wireless Technology, Version 1.1.

66 DeviceClass

DeviceClass represents the class of device (CoD) record as defined by the Bluetooth specification. This record is defined in the Bluetooth Assigned Numbers document and contains information on the type of the device and the type of services available on the device.
The Bluetooth Assigned Numbers document
(http://www.bluetooth.org/assigned-numbers/sdp.htm) defines the service class, major device class, and minor device class. The table below provides some examples of possible return values and their meaning:
Method Return Value Class of Device
getServiceClasses() 0x22000 Networking and Limited Discoverable Major Service Classes
getServiceClasses() 0x100000 Object Transfer Major Service Class
getMajorDeviceClass() 0x00 Miscellaneous Major Device Class
getMajorDeviceClass() 0x200 Phone Major Device Class
getMinorDeviceClass() 0x0C With a Computer Major Device Class, Laptop Minor Device Class
getMinorDeviceClass() 0x04 With a Phone Major Device Class, Cellular Minor Device Class

67 DiscoveryAgent

The DiscoveryAgent class provides methods to perform device and service discovery. A local device must have only one DiscoveryAgent object. This object must be retrieved by a call to getDiscoveryAgent() on the LocalDevice object.

68 RemoteDevice

The RemoteDevice class represents a remote Bluetooth device. It provides basic information about a remote device including the device’s Bluetooth address and its friendly name.

69 ServiceRecord

The ServiceRecord class describes characteristics of a Bluetooth service. A ServiceRecord object contains a set of service attributes, where each service attribute is an (ID, value) pair. A Bluetooth attribute ID is a 16-bit unsigned integer, and an attribute value is a DataElement.
The structure and use of service records is specified by the Bluetooth specification in the Service Discovery Protocol (SDP) document. Most of the Bluetooth Profile specifications also describe the structure of the service records used by the Bluetooth services that conform to the profile.
An SDP Server maintains a Service Discovery Database (SDDB) of service records that describe the services on the local device. Remote SDP clients can use the SDP to query an SDP server for any service records of interest. A service record provides sufficient information to allow an SDP client to connect to the Bluetooth service on the SDP server’s device.
ServiceRecords are made available to a client application via an argument of the
DiscoveryListener.servicesDiscovered(int, ServiceRecord[])
method of the
DiscoveryListener
interface. ServiceRecords are available to server applications via the method LocalDevice.getRecord(Connection).
There might be many service attributes in a service record, and the SDP protocol makes it possible to specify the subset of the service attributes that an SDP client wants to retrieve from a remote service record. The ServiceRecord interface treats certain service attribute IDs as default IDs, and, if present, these service attributes are automatically retrieved during service searches.
The Bluetooth Assigned Numbers document
(http://www.bluetooth.org/assigned-numbers/sdp.htm) defines a large number of service attribute IDs. Here is a subset of the most common service attribute IDs and their types.
Attribute Name Attribute ID Attribute Value Type
ServiceRecordHandle 0x0000 32-bit unsigned integer
ServiceClassIDList 0x0001 DATSEQ of UUIDs
ServiceRecordState 0x0002 32-bit unsigned integer
ServiceID 0x0003 UUID
ProtocolDescriptorList 0x0004
DATSEQ of DATSEQ of
UUID and optional parameters
BrowseGroupList 0x0005 DATSEQ of UUIDs
LanguageBasedAttributeIDList 0x0006 DATSEQ of DATSEQ triples
ServiceInfoTimeToLive 0x0007 32-bit unsigned integer
ServiceAvailability 0x0008 8-bit unsigned integer
BluetoothProfileDescriptorList 0x0009 DATSEQ of DATSEQ pairs
DocumentationURL 0x000A URL
ClientExecutableURL 0x000B URL
IconURL 0x000C URL
VersionNumberList 0x0200 DATSEQ of 16-bit unsigned integers
ServiceDatabaseState 0x0201 32-bit unsigned integer
The following table lists the common string-valued attribute ID offsets used in a
ServiceRecord
. These offsets must be added to a base value to obtain the actual service ID (for more information, see the Service Discovery Protocol Specification located in the Bluetooth Core Specification):
Attribute Name Attribute ID Offset Attribute Value Type
ServiceName 0x0000 String
ServiceDescription 0x0001 String
ProviderName 0x0002 String

70 UUID

The UUID class defines universally unique identifiers. These 128-bit unsigned integers are guaranteed to be unique across all time and space. Accordingly, an instance of this class is immutable. The Bluetooth specification provides an algorithm describing how a 16-bit or 32-bit UUID could be promoted to a 128-bit UUID. Accordingly, this class provides an interface that assists applications in creating 16-bit, 32-bit, and 128-bit long UUIDs. The methods supported by this class allow equality testing of two UUID objects.
The Bluetooth Assigned Numbers document
(http://www.bluetooth.org/assigned-numbers/sdp.htm) defines a large number of UUIDs for protocols and service classes. The table below provides a short list of the most common UUIDs defined in the Bluetooth Assigned Numbers document.
Name Value Size
Base UUID Value (Used in
promoting 16-bit and 32-bit
UUIDs to 128-bit UUIDs)
0x0000000000001000800000805F9B34FB 128-bit
SDP 0x0001 16-bit
RFCOMM 0x0003 16-bit
OBEX 0x0008 16-bit
HTTP 0x000C 16-bit
L2CAP 0x0100 16-bit
BNEP 0x000F 16-bit
Serial Port 0x1101 16-bit
ServiceDiscoveryServerServiceClassID 0x1000 16-bit
BrowseGroupDescriptorServiceClassID 0x1001 16-bit
PublicBrowseGroup 0x1002 16-bit
OBEX Object Push Profile 0x1105 16-bit
OBEX File Transfer Profile 0x1106 16-bit
Personal Area Networking User 0x1115 16-bit
Network Access Point 0x1116 16-bit
Group Network 0x1117 16-bit

Part X. BLUETOOTH PRINTERS

Overview

This part describes classes used to send commands to Bluetooth printers. For more details, check out the totalcross.io.device.printer package JavaDocs.

71 BluetoothPrinter

Used as interface to printers that uses Bluetooth as default to communicate with the device. Altough the class has this name, you can use any port a PortConnector and your device and printer support, such as USB.
Note: during tests, we found that some printers require to fill their buffer to start printing. So, if you have problems printing, try the following code after you instantiate this class:
cp = new BluetoothPrinter(); 
cp.write(new byte[2048]); // fill the buffer 
cp.newLine(); 
... 

72 CitizenPrinter

This class extends BluetoothPrinter to send special Citizen-like commands to the printer. Tested with Citizen CMP-10 thermal printer. Similar to its superclass, the connection with the printer may use any supported port, not necessarily Bluetooth.
Instructions of how to setup the devices to work with the printer.
  1. First, run the self-test: turning the printer off, and pressing the LF + ON button at the same time and then releasing the LF button.
  2. Write down the last 2 bytes (4 letters) of the ADDRESS (e.g.: A4 08)
  3. Discover the "Citizen Systems" printer with the device.
  4. When asked for the PIN (password), write the last 4 letters of the address in UPPER CASE (e.g.: A408); if it fails, write it in lower case (e.g.: a408).
  5. That’s it. On some devices, you can choose to always use this printer as default bluetooth device.

73 MonoImage

Image class that can be used to save 1-bpp images and print on monochromatic printers. Only black pixels are printed, non-black are ignored.

Part XI. XML

Overview

The following chapters describes the totalcross.xml and totalcross.xml.rpc packages. For more details, check out their classes JavaDocs.

74 totalcross.xml

The root of the package totalcross.xml has classes for tokenizing a XML file. It has the following classes:

XmlTokenizer

A Tokenizer for XML input. In non-strict mode (default), it recognizes HTML constructs as well, e.g.: unquoted attributes value, unterminated references, etc.
Four tokenize() methods are provided: one takes a byte[] array; another takes a byte[] array with offset and count; another one for an HTML document which is embedded within an HTTP stream; and the last takes a (byte) Stream.
Tokenization events are reported via overridable methods:
  • foundStartOfInput()
  • foundStartTagName()
  • foundEndTagName()
  • foundEndEmptyTag()
  • foundCharacterData()
  • foundCharacter()
  • foundAttributeName()
  • foundAttributeValue()
  • foundComment()
  • foundProcessingInstruction()
  • foundDeclaration()
  • foundReference()
  • foundEndOfInput()
Some of these methods pass the parameters pertinent to the kind of tokenized events: tag name, attribute name and value... These values are only valid for the time the event is reported. Never assume that, after returning from a foundXxx() method, the information that was reported is still available! Persistent values are however provided through the getAbsoluteOffset() method, which returns the absolute offset of the current parameters of the foundXxx() method.
Typical invocation taken from its dumb subclass DumpXml, ommited from this manual for obvious reasons:
class XmlTokenizerTest
{
	static class MyXmlTokenizer extends XmlTokenizer
	{
		public void foundStartOfInput(byte buffer[], int offset, int count)
		{
			Vm.debug("Start: " + new String(buffer, offset, count));
		}
​
		public void foundStartTagName(byte buffer[], int offset, int count)
		{
			Vm.debug("StartTagName: " + new String(buffer, offset, count));
		}
​
		public void foundEndTagName(byte buffer[], int offset, int count)
		{
			Vm.debug("EndTagName: " + new String(buffer, offset, count));
		}
​
		public void foundEndEmptyTag()
		{
			Vm.debug("EndEmptyTag");
		}
​
		public void foundCharacterData(byte buffer[], int offset, int count)
		{
			Vm.debug("Content: " + new String(buffer, offset, count));
		}
​
		public void foundCharacter(char charFound)
		{
			Vm.debug("Content Ref  |" + charFound + ’|’);
		}
	
		public void foundAttributeName(byte buffer[], int offset, int count)
		{
			Vm.debug("AttributeName: " + new String(buffer, offset, count));
		}
​
		public void foundAttributeValue(byte buffer[], int offset, int count, byte dlm)
		{
			Vm.debug("AttributeValue: " + new String(buffer, offset, count));
		}
​
		public void foundEndOfInput(int count)
		{
			Vm.debug("Ended: " + count + " bytes parsed.");
		}
	}
​
	public static void testMe()
	{
		String input = "<p>Hello<i>World!</i></p>";
		MyXmlTokenizer xtk = new MyXmlTokenizer();
		try
		{
			xtk.tokenize(input.getBytes());
		}
		catch (SyntaxException ex)
		{
			Vm.debug(ex.getMessage());
		}
	}
}  
Note: A Tokenizer is not a Parser. The correctness of the tag structure (stack) is not examined. Ex: the dangling markup <foo><bar>opop</foo> is syntactically valid. As a result, a Tokenizer can work on document fragments.

XmlReader

This class extends XmlTokenizer and is used to read HTML or XML documents, reporting events to handlers (for example, the class ContentHandler described below).
Note: While in the SAX 2.0 spirit, this implementation is not fully compliant. Speed and footprint took precedence over what the author judged being details.
Unlike SAX, reporting tag names, like in ContentHandler.startElement(), passes an integral tag code rather than the name itself. This is, again, for performance reasons. Comparing integers vs. string is notably more efficient and tag name comparison is heavily used for XML applications.
The tag code must uniquely identify the name of the tag. The default implementation — see getTagCode() — simply consists to hash the tag name. It can be overriden to suit specific needs.
Tag names should be translated to tag codes as soon are they are known, when reading the DTD for instance, or computed in advance and saved into a static correspondence table.

XmlReadable

XmlReadable is an interface that abstracts any sequential resource that can be passed to an XmlReader. A class that implements this interface must have the following methods:
  • readXml(XmlReader rdr) Reads the XmlReadable through an XmlReader parser, where rdr is the XmlReader that will report the SAX events.
  • getBaseURI() Gets the base URI attached to the XmlReadable.
  • setCaseInsensitive(boolean caseInsensitive) Pass true if you want the
    XmlReader
    attached object to be case insensitive.

XmlReadableSocket

An XmlReadableSocket implements XmlReadable and has a Socket stream that takes care of the HTTP headers and starts reading at the message body.

XmlReadableByteArray

This class makes an XmlReadable from a byte array.

XmlReadableFile

This class makes an XmlReadable from a File extending XmlReadableByteArray.
Example:
XmlReader rdr = new XmlReader();
rdr.setContentHandler(...);
File f = new File(...);
rdr.parse(new XmlReadableFile(f)); 

XmlReadablePort

This class makes an XmlReadable from a PortConnector extending XmlReadableByteArray. It can only be used on Windows 32 and Java SE.
Example:
XmlReader rdr = new XmlReader();
rdr.setContentHandler(...);
rdr.parse(new XmlReadablePort(PortConnector.DEFAULT, 19200)); 

XmlReadableString

This class makes an XmlReadable from a String extending XmlReadableByteArray.
Example:
XmlReader rdr = new XmlReader();
rdr.setContentHandler(...);
rdr.parse(new XmlReadableString("Hello World!")); 

AttributeList

This class describes the attributes attached to a start-tag. Tags are case-insensitive.
The AttributeListIterator class provides an iterator over the components of each attribute in an AttributeList instance:
  • the attribute name
  • the unquoted value
  • the quote, if one exist

ContentHandler

This class receives notification of the logical content of a document.
This is the main interface that most SAX applications implement: if the application needs to be informed of basic parsing events, it implements this interface and registers an instance with the XML reader using the XmlReader.setContentHandler(ContentHandler) method. The XML reader uses the instance to report basic document-related events like the start and end of elements and character data.
The order of events in this interface is very important, and mirrors the order of information in the document itself. For example, all of an element’s content (character data, processing instructions, and/or subelements) will appear, in order, between the startElement() event and the corresponding endElement() event.

75 totalcross.xml.rpc

This chapter describes classes for a remote procedure call (RPC) client that uses XML.

StandardHttpClient

This class provides client-side HTTP communication.

CompressedHttpClient

This is a subclass of the above class and has a similar behavior, except that it uses compression.

XmlRpcClient

This class is an XmlReader and handles XML-RPCs to a server. Its objects cannot be reused.

XmlRpcContentHandler

This class extends ContentHandler and its methods are called by the XML parser used in XmlRpcClient.

XmlRpcValue

This class is used to represent an XML-RPC value while a request is being parsed.

XmlWriter

This class correctly formats strings in XML syntax. Currently only supports ISO-8859-1 encoding of XML.

Part XII. BARCODE SCANNER

Overview

In this part, two ways of decoding information from a barcode will be seen.

76 Scanner

The totalcross.io.device.Scanner class allows the usage of the barcode reader of some of the most popular scanners.
For TotalCross 3.0 only Intermec Android scanner is supported.
Scanner access is only available when running on a native TotalCross VM; it is not supported when running on Java. It is, though, emulated on Java: a popup dialog will ask you for a barcode symbol which will be returned every time the getData() method is called.
Since there is only one scanner per device, the Scanner class is static. Activating the scanner causes the physical scanner to be powered, and enables the trigger. Deactivating the scanner removes the power from the scanner and prevents scans from taking place. The scanner should always be deactivated at the end of processing to prevent excessive battery drain.
Note that if the scanner is deactivate in the device settings, you won’t be able to activate it using the application.
When the scanner is activated, scan events will appear in the MainWindow’s onEvent() method. The scan events will contain a string describing either the item scanned or a battery error. Deactivating the scanner prevents scan events from being delivered. If you want that another class fetches the scanner events, you should pass a control as a listener and fetch them in its onEvent() method. If it is a window, it should be opened using popupNonBlocking().
You can change this behaviour on some devices by calling setContinuousScanning(
false)
, which will cause each call to activate() to only allow a single scan.
Since barcodes can have many formats, this class includes a method to register the barcode types with the scanner. These types must each be individually set as parameters to the scanner. After all the parameter types have been set, the commitBarcodeParams() method must be called to register the parameters with the scanner (this is not necessary on Intermec Android). Once this is done the scanner is able to decode the specified barcode types.
A typical processing sequence is given below:
if (!Scanner.activate() 
 || !Scanner.setBarcodeParam(Scanner...., true)
 || !Scanner.setBarcodeParam(Scanner...., true)
 || !Scanner.commitBarcodeParams()) 
	return; 
... 
if (!Scanner.deactivate()) 
	return;
  • Note: the Java emulation works the same as on device. On Java activating the scanner only schedules a single scan.
    We obviously need this class to behave in the same way on both platforms. Rather than forcing one choice on the user, the continuousScanning flag allows the user to choose which mode they want.
For more details, take a look at totalcross.io.device.scanner.Scanner and totalcross.
io.device.scanner.ScanEvent
JavaDocs.

77 ZXing

The classes in the package totalcross.zxing can be used to decode an image of a barcode in some format into the string it encodes. The sample tc.samples.io.device.zxing.
ZXingScanner
shows how to use it.

Part XIII. OPTIMIZATION TRICKS

Overview

The purpose of this small, but highly important chapter, is to teach you how to improve your Java application’s speed.
The TotalCross VM has an interpreter, which makes it a little slow for certain types of applications. It is not feasible to program in the same way for 1 GHz (a typical mobile device processor) and 3 GHz processors (a typical desktop processor). Moreover, a desktop has much more RAM. By following the tricks presented in this tutorial, you’ll be able to improve your application’s performance.
These tips and tricks can make a very big difference in speed not only when running your program on mobile devices, but also on desktop systems. Most of them can be used in any Java environment, and are actually referenced in other books and tutorials, but others are explicitly TotalCross optimization tricks.
There are some proof timings (measured in microseconds – µ = 1 second / 1,000,000) near a clock bullet. They are always shown per iteration, ie, taken the total time and divided by the number or iterations. All tests were performed on the following devices:
  • Windows 8.1, iCore 5, 2 x 2.80 GHz, 5 Gb RAM, 192 Gb disk size - named Win
  • Android 4.4.2, Motorola Moto G, 4 x 1.2 GHz, 1 Gb RAM, 16 Gb disk size - named Android
  • iPhone 7.0.6, iPhone 4 S, 2 x 800 MHz, 512 Mb RAM, 8 Gb disk size - named iPhone

78 The Tricks

  1. Avoid method calls. Method calls in TotalCross (and, actually, in any platform or virtual machine) are extremely expensive when comparing to in-lined code. This is the most important optimization trick and several other tricks inherit from this one. Lets see some common mistakes that people make:
    1. Don’t use method calls as the stop condition in for loops (assume v is a Vector):
      for (int i =0; i < v.size(); i++)
      
      Win: 00.00, Android: 00.67, iPhone: 00.47
      You can change it in a couple of ways:
      int n = v.size();
      for (int i=0; i < n; i++)
      
      Win: 00.00, Android: 00.27, iPhone: 00.20
      ... if the order of the loop matters, or
      for (int i=v.size()-1; i >= 0; i--)
      
      Win: 00.40, Android: 00.20, iPhone: 00.13
      ... if the order of the loop doesn’t matter.
      The same care can be taken with an array’s length property. It always takes extra time to get the field, so it’s better to assign it to a temporary variable and use it in the loop, or use the previous suggestions.
      for (int i =0; i < items.length; i++)
      
      Win: 00.00, Android: 00.20, iPhone: 00.20
    2. Avoid using get and set methods. This may go against standard object oriented practices, but in small devices it is necessary that you avoid this. If you have a simple member that is protected or private, and is set and get by using something like:
      protected String s;
      public void setS(String newS) {s = newS;}
      
      Win: 00.11, Android: 00.51, iPhone: 00.44
      public String getS() {return s;}
      
      Win: 00.08, Android: 00.46, iPhone: 00.38
      It will be better to make the member public and access it directly:
      public String s;
      
      direct set (s = ...): Win: 00.02, Android: 00.14, iPhone: 00.13
      direct get (... = s): Win: 00.02, Android: 00.19, iPhone: 00.15
      Note about final classes: in the first JDK versions (up to 1.1.x), making the get/set methods final or making the whole class final would make the compiler inline the methods when using the -O compiler option. From JDK 1.2.x and beyond this no longer holds true. We plan, however, to make the TotalCross’ deployer inline methods, thus re-allowing the programmer to use get/set methods.
    3. Any other methods that are used in a loop are good candidates to be inlined directly. But please have good sense when doing this. There’s no need to inline all your program in one big block. Just make sure you optimize the very inner loop and that’s it.
  2. Avoid using Interfaces. If you have an interface that is frequently used (maybe in a loop) in your application, change it to an abstract class, if possible. This is necessary because interface methods cannot be dynamically bound by the VM, thus each method call must be looked up in the methods table each time it is called. In normal and in abstract classes, the method is looked up only once and then its address is bound and used in all subsequent calls.
    interfaces Win: 00.20, Android: 01.16, iPhone: 01.76
    abstract classes Win: 00.05, Android: 00.49, iPhone: 00.40
    normal classes Win: 00.06, Android: 00.48, iPhone: 00.40
  3. Use arithmetic shifts instead of power of 2 multiplications. For example, x*2 can be changed to x<<1, x*4 = x<<2, etc. The same is valid for divisions: x/2 = x>>1, x/4 = x>>2, etc. But be careful: this only works for positive numbers.
    */ Win: 00.03, Android: 00.20, iPhone: 00.33
    <<>> Win: 00.02, Android: 00.19, iPhone: 00.17
  4. Use & instead of % (modulus) when possible. The situations where this is possible is when you have x%2, x%4, x%8, x%16, ..., x%(2n). You can replace it by x&1, x&3, x&7, x&15, ..., x&(2n-1).
    % Win: 00.02, Android: 00.17, iPhone: 00.21
    & Win: 00.02, Android: 00.14, iPhone: 00.11
  5. Use aliases when dealing with an array that stores classes. Instead of
    centerX = pos[i].x;
    centerY = pos[i].y;
    
    Win: 00.06, Android: 00.33, iPhone: 00.29
    use:
    Coord c = pos[i];
    centerX = c.x;
    centerY = c.y;
    
    Win: 00.03, Android: 00.30, iPhone: 00.26
    Because each time you access an array position, the VM checks for
    NullPointerException
    s, ArrayIndexOutOfBoundsExceptions, and does a multiply to access the memory address, which expends time.
  6. Use an array (e. g.: String[], int[], etc) instead of Vector (or IntVector) when you know in advance the size and it will not grow or shrink. Adding elements to Vector require the call of addElement() hundreds of times, which affects performance.
  7. If you need to do lots of String concatenations, use a StringBuffer instead of direct String additions (+). This saves time and, especially, memory! It can avoid out of memory errors in some situations, specially when parsing text and/or xml. When in loop, remember to reuse StringBuffer objects, using StringBuffer.setLength(0). But note that when using StringBuffer to concatenate only a few Strings, there’s no performance gain at all. In other words, optimize your program to use StringBuffer only when doing things in loops.
    String s = "";
    for (int i=0; i < LOOP3; i++) 
    {
    	s += i;
    	if (n-- == 0)
    	{
    		n = 100;
    		s = "";
    	} 
    }
    
    Win: 03.00, Android: 16.80, iPhone: 23.80
    for (int i=0; i < LOOP3; i++)
    {
    	sb.append(i);
    	if (n-- == 0)
    	{
    		n = 100;
    		sb.setLength(0);
    	}
    }
    
    Win: 00.00, Android: 01.00, iPhone: 00.60
  8. When using ListBox and ComboBox, don’t initialize them by calling add() repeatedly. Instead, create a new array with the data and assign it directly to the ListBox/ComboBox using the constructor that receives an array of Objects.
    for (int i=0; i < 10000; i++)
    	lb.add(Convert.toString(i));
    
    Win: 01.60, Android: 06.80, iPhone: 08.80
    String []s = new String[10000];
    for (int i=0; i < 10000; i++)
    	s[i] = Convert.toString(i);
    lb.add(s);
    
    Win: 00.00, Android: 02.90, iPhone: 05.20
  9. Use int instead of long and double whenever possible. The long type has to be emulated on 32-bit processors. Currently, almost all device processors have a math co-processor and thus double is a quite fast operation, although still slower than int.
    long Win: 00.06, Android: 00.51, iPhone: 00.33
    double Win: 00.25, Android: 00.27, iPhone: 00.26
    int Win: 00.03, Android: 00.19, iPhone: 00.37
  10. It is always better to use a switch/case instead of lots of ifs/elses. Even if the code isn’t called much, a switch/case makes the code cleaner (like the suggested approach for the onEvent() method – see Event Handling↑).
    if/else Win: 00.05, Android: 00.25, iPhone: 00.26
    switch Win: 00.03, Android: 00.21, iPhone: 00.21
  11. When using (Int)Vector and (Int)Hashtable and you have an estimate of the final size, construct it with that estimated size and not with the default constructor, to avoid buffer reallocation. Each time you add an element and the vector is filled, it allocates a new buffer 20% larger than the current one and copies all the contents to it, thus spending time and memory.
    starting from 10 Win: 00.00, Android: 01.10, iPhone: 00.90
    starting from 10000 Win: 00.00, Android: 01.00, iPhone: 00.80
  12. When using String.indexOf(), use the method that receives an int value instead of the one that receives a String value if the String being searched has a length of one char.
    int Win: 00.09, Android: 00.69, iPhone: 00.67
    String Win: 00.13, Android: 00.91, iPhone: 00.90
  13. Use aliases when accessing objects that are accessible by methods. For example, instead of:
    db.getResizeRecord().restartRecord(index);
    ...
    db.getResizeRecord().endRecord();
    
    Use:
    ResizeRecord rs = db.getResizeRecord();
    rs.restartRecord(index);
    ...
    rs.endRecord();
    
  14. Use the arithmetic shortcuts like x+=y, x-=y, x*=y, x/=y, x&=y, etc, instead of x=x+y, x=x-y, x=x*y, etc.
    x = x+y Win: 00.02, Android: 00.15, iPhone: 00.13
    x += y Win: 00.02, Android: 00.13, iPhone: 00.13
  15. Use local variables whenever possible, instead of instance variables, specially when they are used in loops. For example:
    public class Test
    {
    	int loopVar;
    	public void testLoop()
    	{
    		for (loopVar = 0; loopVar < 1000; loopVar++)
    			...
    	}
    }
    
    Instead, make loopVar a local variable inside the testLoop() method, moving it to inside the method.
    instance variable Win: 00.02, Android: 00.27, iPhone: 00.25
    local variable Win: 00.02, Android: 00.12, iPhone: 00.13
    Another trick is to create a local copy of a class variable in the method when it is used in a loop. For instance:
    StringBuffer sb = new StringBuffer(1024);
    public void processSomething()
    {
    	StringBuffer sb = this.sb;
    	sb.setLength(0);
    	...
    }
    
  16. When iterating over a String, be careful to choose the correct approach for the target platform. Not always the best approach for one platform is also the best for the others. In this case, you have three choices: iterate using charAt(), iterate over the char array provided by toCharArray(), or iterate over the byte array provided by getBytes(). The last two approaches increase the memory usage because they create arrays (the char array takes twice the memory of a byte array).
    Using toCharArray():
    String s = "a string";
    char []c = s.toCharArray(); 
    int n = c.length;
    for (int i =0; i < n; i++)
    	... c[i] ...
    
    Win: 03.15, Android: 25.75, iPhone: 26.60
    Using getBytes():
    byte []b = s.getBytes();
    int n = b.length;
    for (int j =0; j < n; j++)
    	if (b[j] == (byte)’g’)
    
    Win: 03.90, Android: 26.35, iPhone: 28.25
    Now, using charAt():
    int n = s.length();
    for (int i =0; i < n; i++)
    	... s.charAt(i) ...
    
    Win: 09.35, Android: 78.00, iPhone: 64.40
    getBytes() convert chars to bytes using the totalcross.sys.
    CharacterConvertion
    class. It is faster in TotalCross because it detects the standard CharacterConvertion class being used and, in this case, the native implementation of chars2bytes() is used.
  17. The use of short and byte data types are slower than using int and also saves no memory at all. This happens because boolean, short, and byte are internally stored as int and they have to be converted to/from int when are operated on. They should only be used when creating arrays; in such situation they do save memory.
    int Win: 00.02, Android: 00.16, iPhone: 00.13
    short Win: 00.03, Android: 00.17, iPhone: 00.15
    byte Win: 00.02, Android: 00.17, iPhone: 00.15
Remember that although the time of each operation is small, in your whole program every speed improvement gained using the approaches described above will count a lot. Notice also that the tricks also improve performance on desktop.

Part XIV. APPENDIXES

A Copyright

All contents of this tutorial, including text, programs, applets, source code, and images are copyrighted and owned by TotalCross Ltda, all rights reserved. No material can be reproduced and/or distributed electronically or in print without written permission.

B Platform specific known features/flaws

iOS and Android

When an application crashes, it is usually closed by the system. But on Android the system might reboot.
On iOS, your application cannot access another application folder. On Android is also possible for it to access a memory card.

C Tutorials for third-party tools

Deploying with Ant

Apache Ant is a Java-based build tool, similar to Make, that may be executed from the shell or from an IDE, like Eclipse.
The TotalCross SDK comes with a sample Ant build file. Just copy the build.xml file located in TotalCrossSDK/docs/companion_resources/ant_build to your application’s directory, and change its properties as described below:
Property Name Example Value Description
project name “ACME” Project’s name
sdk.root “C:/TotalCrossSDK” TotalCross SDK installation path
app.name “C:/ACME/main/Live.class” The path and name of the class that extends MainWindow (must end with .class)
app.name “C:/ACME/Live.jar” The path to the .jar that contains all classes and resources.
app.name “C:/ACME/main” The path to where the class that extends MainWindow is located.
platforms “-wince -win32” Platforms to deploy.
extra.args “/k /v” Any extra arguments you want to pass to the deployer.
target.dir “.” (single dot - current path) The target directory where the files will be created.

Configuring Eclipse to Run and Debug TotalCross Programs

This is a guide to help you to setup the Eclipse IDE (www.eclipse.org) to develop TotalCross programs.
Because this isn’t a guide for Eclipse or Java, it assumes the following:
  • Java JDK 8 is installed and configured.
  • The Eclipse IDE with Java Development Tools installed. Please notice this guide was created using the Eclipse Luna (version 4.4.x) package “Eclipse IDE for Java EE Developers”, and the steps presented here may not be valid for other versions.
  • We’ll refer to the TotalCross location as TotalCrossSDK.
We’ll use the TotalCrossAPI sample that comes with the SDK to skip the programming and focus on the project setup:
  1. Create a new Java Project: Open the menu File and click on New > Java Project.
  2. Choose a project name for your project and click on Finish. We’ll use TotalCrossAPI. Then click on “Finish”.
  3. Expand the newly created project and right-click on the folder “src” and choose “Import...”.
  4. On the new “Import” dialog shown, expand the folder “General”, select “File System” and click “Next >”.
  5. Now write the complete file path to the TotalCrossAPI example, or use “Browse” to navigate to the file system. Our source is located at TotalCrossSDK/src/tc/samples/api/.
  6. On the left box, check the folder api.
  7. On the edit “Into folder:”, write TotalCrossAPI/src/tc/samples.
  8. Now we can see our project is displaying several build errors. To fix this, right-click on the project and choose “Properties”.
  9. On the left panel, select “Java Build Path”; Then, on the right panel, click on the “Libraries” tab and on the button “Add External JARs...”; Browse your file system to TotalCrossSDK/dist and select the file tc.jar.
  10. Now the “Libraries” tab should also show the tc.jar. Expand it and select “Javadoc location”. Now click on the button “Edit...” to open a new dialog, then browse your file system and select the TotalCrossSDK/docs/html/ directory and press “Ok” to confirm.
  11. Back to the “Libraries” tab, select “Source attachment” and click on the button “Edit...” to open a new dialog, then browse your file system and select the TotalCrossSDK/src directory and press “Ok” to confirm. This finishes the build path configuration.
  12. Look again at the left panel and select “Java Compiler”. Check the option “Enable project specific settings” and change the “Compiler compliance level” to “1.8”.
  13. Press “Ok” to confirm and close the “Properties” dialog.
  14. Re-build the project and the build errors should all be gone (skip this step if your Eclipse is configured to build automatically in Preferences-->Workspace).

D Logging errors Example

We have developped a way to automatically generate an error log. In our case, the log is sent by email on each data synchronization. Thus, it is possible to catch errors that occur when the application is in production and the user can’t send the device to us. The code written here is NOT in the SDK.
Firstly, you need to centralize all exception processing to throw them into a file. We must also find out if the program aborted for some reason. With the class below you can do this for Android. You can change it for other devices:
public class GerenciamentoDeErros 
{    
	public static String retornaRaizTemp()    
	{       
		if (Settings.onJavaSE)          
			return Settings.appPath+"/../";              
		
		try        
		{          
			if (new File("/sdcard/DCIM").exists())             
				return "/sdcard";       
		} 
		catch (Exception e) {}        		
		return Settings.appPath;    
	}
	
	public static String NOME_ARQUIVO = retornaRaizTemp()+"/errolm.log";        
	
	public static void finalizouCorretamente()    
	{       
		try       
		{          
			File fin = new File("device/finalizou",File.CREATE_EMPTY);
			fin.writeBytes(new byte[]{(byte)1},0,1);          
			fin.close();       
		}       
		catch (Exception e)       
		{          
			e.printStackTrace();       
		}    
	}
​
	public static void verificaSeFinalizou()    
	{       
		try       
		{          
			File fin = new File("device/finalizou",File.READ_WRITE);
			byte[] b = new byte[1];          
			int leu = fin.readBytes(b,0,1);          
			if (leu != 1 || b[0] != 1)                       
				if (Settings.platform.equals(Settings.ANDROID))             
				{                
					TelaBase.mensagem("The program has not been finalized properly. Synchronize the device as soon as possible so that the error report can be sent");     					
					processaLogCat();             
				}                    					
				fin.setPos(0);          
				fin.writeBytes(new byte[]{(byte)0},0,1);          
				fin.close();       
		}       
		catch (FileNotFoundException fnfe)       
		{          
			Vm.debug("finalizou does not exist");       
		}       
		catch (Exception e)       
		{          
			e.printStackTrace();       
		}    
	}
​
	public static void log(Throwable t)    
	{
		if (!Settings.onJavaSE)
			log(t.getClass().getName()+" "+t.getMessage());
		log(Vm.getStackTrace(t));
		if (Settings.onJavaSE)
			t.printStackTrace();
		TelaBase.mensagem("An error has occurred. Synchronize the device to send the error report.");    
	}
​
	public static void log(String s)
	{
		byte[] bytes = s.getBytes();
		log(bytes,0,bytes.length);
	}
​
	public static void log(byte[] bytes, int ofs, int len)
	{
		try
		{
			File f = new File(NOME_ARQUIVO, File.CREATE);
			f.setPos(f.getSize());
			f.writeBytes(("========== "+new Date()+" "+new Time()+" TC: "+Settings.versionStr+" LM: "+Settings.appVersion+" ==========").getBytes());
			f.writeBytes(bytes, ofs, len);
			f.close();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
	
	private static void processaLogCat()
	{
		if (!Settings.platform.equals(Settings.ANDROID))
			return;
		String saida1 = "/sdcard/errocheio.log";
		try
		{
			new File(saida1).delete();
		}
		catch (Exception e) {}
		Vm.exec("cmd","logcat -d -f "+saida1+" -v time *:I",0,true);
		try
		{
			byte[] enter = {’\n’,’ ’};
			File f = new File(saida1);
			while (!f.exists())
				Vm.sleep(500);
			File fi = new File(saida1, File.READ_WRITE);
			ByteArrayStream bas = new ByteArrayStream(f.getSize()/2);
			LineReader lr = new LineReader(fi);
			bas.writeBytes(enter,0,enter.length);
			String s;
			while ((s = lr.readLine()) != null)
			{
				if (s.indexOf(" W/ResourceType") != -1) // remove useless tags
					continue;
				if (s.indexOf(" I/DEBUG") != -1 && s.endsWith("code around pc:"))
					while ((s = lr.readLine()) != null && s.indexOf(" I/DEBUG")!= -1) {} // skips the stack, which is useless (and consumes 20% of log file!)
				if (s != null)
				{
					bas.writeBytes(s);
					bas.writeBytes(enter,0,enter.length);
				}
			}
			fi.close();
			log(bas.getBuffer(),0,bas.getPos());
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
} 
How to use it:
  1. On the application initUI(), call:
    GerenciamentoDeErros.verificaSeFinalizou(); 
    
  2. On the application onExit(), call:
    GerenciamentoDeErros.finalizouCorretamente(); 
    
  3. Whenever treating exceptions, call:
    catch (Exception e)
    {
    	GerenciamentoDeErros.log(e);
    } 
    
  4. When synchronizing, check if the error file exists, load it, send it, and erase it:
    String nomeLog = GerenciamentoDeErros.NOME_ARQUIVO;
    if (new File(nomeLog).exists())
    {
    	sinc.log("Sending error report.");
    	File log = new File(nomeLog,File.READ_WRITE);
    	byte[] lbuf = new byte[2048];
    	int tam = log.getSize();
    	final int TAM_MAX = 100000;
    	if (tam > TAM_MAX) // If the file is too big, only sends the last 100 K
    	{
    		log.setPos(tam-TAM_MAX);
    		tam = TAM_MAX;
    	}
    	ds.writeInt(tam);
    	int nn;
    	while ((nn = log.readBytes(lbuf,0,lbuf.length)) > 0)
    		ds.writeBytes(lbuf,0,nn);
    	log.delete();
    }
    else ds.writeInt(0);
    
Observations:
  1. The GerenciamentoDeErros class allows logging everything. AVOID THAT: only errors should be there.
  2. In our synchronization, we send the log error using a servlet; if you preffer using a webservice, you must implement something different.
  3. In the server, we get the report and send it by e-mail directly to the developer support service. If the report has errors in the VM, you should open a request in our support service with the error log and addicional information to help us to reproduce the errors.
  4. forum.totalcross.net