MobileJava MEUsing Alerts, Images, Timers, and Gauges in MIDlets

Using Alerts, Images, Timers, and Gauges in MIDlets

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Java Programming Notes # 2580


Preface

This is one in a series of tutorial lessons designed to teach you how to write programs using the Sun Java Wireless Toolkit for CLDC. The first lesson was titled Getting Started with MIDlets and the Sun Java Wireless Toolkit for CLDC. The previous lesson was titled Introduction to the MIDlet User Interface, Part 2 (see Resources).

A MIDlet development framework

For this lesson, you will need the updated version of the MIDlet development framework named WTKFramework03 that is provided in Listing 13. This is a minor update to the framework relative to the previous version, but it is required for the MIDlets presented in this lesson that use images. This version preserves image files when it is cleaning up after itself.

What you learned in the previous lesson

In Part 1 and Part 2 of the previous lesson, you learned:

  • The fundamentals of user interfaces for MIDlets.
  • How to instantiate user interface components.
  • How to cause user interface components to become visible on the cell phone screen.
  • The differences between a Screen and a Display.
  • About restrictive constraints and modifier flags.
  • About the MIDlet user interface class hierarchy.
  • About the methods of various classes that can be used to manipulate user input and output.
  • How to program MIDlet user interfaces that minimize the MIDlet’s memory footprint while the MIDlet is paused making consistent use of the MIDlet class methods to satisfy the life-cycle requirements for MIDlets.

What you will learn in this lesson

In this lesson you will learn:

  • How to program an Alert for a MIDlet.
  • How to add an image to the Alert.
  • How to add a Gauge to the Alert to act as a progress or activity meter.
  • How to use a Timer to control when an Alert becomes visible on the screen.

Viewing tip

I recommend that you open another copy of this document in a separate browser window and use the following links to easily find and view the figures and listings while you are reading about them.

Figures

  • Figure 1. Partial class hierarchy for MIDP 2.0.
  • Figure 2. Sun cell phone emulator display of MIDlet named Alert01.
  • Figure 3. Sun cell phone emulator display of MIDlet named Alert02.
  • Figure 4. Selected methods of the Alert class as of MIDP 2.0.
  • Figure 5. Methods of the TimerTask class as of MIDP 2.0.

Listings

  • Listing 1. Beginning of the MIDlet class named Alert01.
  • Listing 2. Beginning of the startApp method in Alert01.
  • Listing 3. Schedule a TimerTask.
  • Listing 4. Remaining code in startApp method in Alert01.
  • Listing 5. The pauseApp and destroyApp methods in Alert01.
  • Listing 6. Beginning of the class named MyTimerTask in Alert01.
  • Listing 7. Create and program an Alert object in Alert01.
  • Listing 8. Create and service the Gauge in Alert01.
  • Listing 9. Make the Alert visible in the MIDlet named Alert01.
  • Listing 10. Create an Alert object for the MIDlet named Alert02.
  • Listing 11. Create a Gauge for the MIDlet named Alert02.
  • Listing 12. Set the current value of the Gauge to be count mod 3.
  • Listing 13. The updated MIDlet development framework named WTKFramework03.
  • Listing 14. Source code for the MIDlet named Alert01.
  • Listing 15. Source code for the MIDlet named Alert02.

Supplementary material

I recommend that you also study the other lessons in my extensive collection of online Java tutorials. You will find a consolidated index at www.DickBaldwin.com.

General background information

A partial class hierarchy

In the earlier lesson titled Introduction to the MIDlet User Interface, Part 1 (see Resources), I presented a partial class hierarchy for MIDP 2.0. Figure 1 shows an expanded version of that class hierarchy.

Figure 1. Partial class hierarchy for MIDP 2.0.

  • Object
    • Displayable
      • Screen
        • TextBox
        • Alert
        • Form
        • List
      • Canvas (abstract)
        • GameCanvas (abstract)
    • Display
    • Ticker
    • AlertType
    • Image
    • Item
      • Gauge
      • TextField
    • Timer
    • TimerTask

I discussed and explained the classes shown in bold Italics in earlier lessons. I will discuss and explain the classes shown in boldface (non Italics) in this lesson.I will discuss the remainder of the classes shown in Figure 1 (plus other classes) in future lessons.

The Alert class

You may recall from the earlier lessons that an object of the class Alert is one of four types of high-level user interface (UI) components that you can place on the cell phone screen to communicate with the user. The four high-level UI component classes are all subclasses of the Screen class shown in Figure 1.

A temporary screen display

An Alert is a Screen that takes over the cell phone display and shows some data for a specified time period. Then it disappears and the display reverts to the Screen that would normally be showing. The contents of the Alert on the screen can include a title, a string, an image, and a Gauge.

An AlertType

An Alert may optionally have an AlertType associated with it to indicate the nature of the alert. (If you pass null to the corresponding constructor parameter, there is no alert type associated with the alert.) The cell phone can use the AlertType value to play an appropriate sound when the Alert is displayed. According to Sun,

“The actual sound made by the device, if any, is determined by the device. The device may ignore the request, use the same sound for several AlertTypes or use any other means suitable to alert the user.”

An Image

As mentioned above, an Alert can contain an Image in addition to the title, the string, and the Gauge. The Image data can come from a variety of sources including graphics files of types gif, png, and jpeg. At least that is true with regard to the Sun cell phone emulator.

In general, an Image used with a MIDlet can be either mutable or immutable. In this lesson, we will confine the discussion to immutable images only.

A Gauge

Optionally, an Alert can contain a Gauge object. The Gauge object must be operating in a non-interactive manner and must comply with some other restrictions as well. One use for a Gauge object that meets all of the restrictions is as an activity or progress indicator.

Sample screen shots

Figure 2 and Figure 3 show the Sun cell phone emulator output for the MIDlets named Alert01 and Alert02.

Figure 2. Sun cell phone emulator display of MIDlet named Alert01.

Explanation of the screen shots

In both images, the Alert title is shown in the gray bar across the top. The text string portion of the alert is on the left side of the cell phone screen slightly below the center. The red and blue balls are the images. The vertical bars in Figure 2 constitute the gauge in that MIDlet. The image of Duke in Figure 3 is one of three successive images of Duke that constitute the gauge in that MIDlet. I will explain the differences between these two MIDlets later.

Figure 3. Sun cell phone emulator display of MIDlet named Alert02.

Missing screen shots

When the Alerts are not showing on the cell phone screens in the two sample MIDlets in this lesson, a TextBox screen is showing. However, because the behavior of each MIDlet is dynamic and the TextBox screen doesn’t stay on the display very long, I was unable to capture a screen shot of the TextBox screen.

This shouldn’t be a problem, however. You have seen screen shots of TextBox objects before. The TextBox screens in these MIDlets look very much like the screen shown in Figure 1 of the earlier lesson titled Introduction to the MIDlet User Interface, Part 1 (see Resources).

The Alert class

The constructor for the Alert class

As of MIDP 2.0, there are two overloaded constructors for the Alert class. The constructor that I will use in this lesson takes four parameters:

  • String title
  • String alertText
  • Image alertImage
  • AlertType alertType

The first three of the parameters match three items in Figure 2 and Figure 3. However, as near as I can tell, the fourth parameter (alertType) doesn’t have a visible manifestation in the Sun cell phone emulator. I will explain the audio manifestation of this parameter later when I explain the code.

The other overloaded constructor takes only two parameters and they match the first two parameters in the above list.

Methods of the Alert class

As of MIDP 2.0, the Alert class defines fourteen methods. Three of those methods are associated with interactive programming and commands, which I will be discussing in a future lesson. Six of the methods are relatively standard getter methods, which should not require any explanation or discussion. The remaining five methods are shown in Figure 4.

Figure 4. Selected methods of the Alert class as of MIDP 2.0.

  • setImage(Image img) – Sets the Image used in the Alert.
  • setIndicator(Gauge indicator) – Sets an activity indicator on this Alert.
  • setString(String str) – Sets the text string used in the Alert.
  • setTimeout(int time) – Set the time for which the Alert is to be shown.
  • setType(AlertType type) – Sets the type of the Alert.

Methods that I did or could have used

These are methods that I either did use, or could have used in the two sample MIDlets in this lesson. By this I mean that these are methods that I could have used to set the important properties of the Alert objects had I not elected to specify these property values using constructor parameters. Note, however that the setIndicator method does not have a constructor parameter counterpart and the method must be called to attach a Gauge to the Alert.

The six getter methods that I didn’t list above can be used to get the current values of the same five properties as the setter methods in Figure 4, plus an additional property named defaultTimeout.

The AlertType class

An object of the AlertType class can be passed as the fourth parameter to the Alert constructor to establish the type of the Alert. Optionally, a value of null can be passed meaning that the Alert is of no specific type.

Five types of alerts

Alert objects may be used by a MIDlet to present various kinds of information to the user. The AlertType object can be used to cause the type of the alert to be more specific. The predefined types in MIDP 2.0 are:

  • INFO
  • WARNING
  • ERROR
  • ALARM
  • CONFIRMATION

One of the sample MIDlets in this lesson uses an alert of type ALARM. The other uses an alert of type CONFIRMATION.

Playing AlertType sounds without changing the display

Although this capability is not demonstrated by either of the sample MIDlets in this lesson, according to Sun,

“An AlertType may be used to directly signal the user without changing the current Displayable. The playSound method can be used to spontaneously generate a sound to alert the user. For example, a game using a Canvas can use playSound to indicate success or progress.”

A very simple class

The AlertType class is a very simple class. It defines a public static final variable for each of the five alert types in the above list. It defines one constructor that takes no parameters, and it defines the playSound method mentioned above. That’s about all there is to the AlertType class.

The Image class

An object of the Image class is used to hold graphical image data. Images can be placed within Alert, Choice, Form, or ImageItem objects. In this lesson, we will be concerned only with placing Image objects in Alert objects. I will discuss and explain the other types of objects in future lessons.

Mutable or immutable

Images may be either mutable or immutable depending upon how they are created. Immutable images are usually created by loading image data from various resources such as image files. Once they have been created, they cannot be modified.

Mutable images are created as blank images containing only white pixels. They may be modified after they are created. In this lesson, we will be concerned only with immutable images.will discuss mutable images in a future lesson on graphics and the Canvas class.

Image file formats

The Sun documentation for the Image class would lead one to believe that only image files of type PNG are supported.  However, I have confirmed experimentally that at least when using the Sun cell phone emulator, the sample MIDlets in this lesson will also work properly with GIF image files and JPEG image files in addition to PNG image files. In any event, there is a link in Resources to a free online image file conversion service that can be used to convert numerous image file formats to PNG format if you want to stick strictly with PNG image files.

No constructor and no fields

The Image class has no public constructor and no fields. What it does have is a set of six public static overloaded methods named createImage that can be called to create an object of type Image. In addition, there is a public static method named createRGBImage that can also be called to create an object of type Image.

Each of these methods lets you specify the source of the image data in a different way. I will use only one of the overloaded methods in this lesson. The version of the createImage method that I will use accepts the path and name of an image file (png, gif, or jpeg) and uses the image data in that file to produce the Image object.

Five additional methods

In addition to the overloaded methods mentioned above, the Image class defines five additional methods, most of which are probably most useful when dealing with graphics and the Canvas class.

The Gauge class

According to Sun, the Gauge class

“Implements a graphical display, such as a bar graph, of an integer value.”

The vertical bars in Figure 2 and the image of Duke in Figure 3 are two different manifestations of a Gauge object.

The Gauge constructor

The Gauge class has a single constructor that requires the following parameters:

  • String label
  • boolean interactive
  • int maxValue
  • int initialValue

The first parameter is used to create a label for the Gauge. The second parameter specifies whether or not the Gauge is interactive. For example, an interactive Gauge could be used as an interactive volume control.

A graphical representation of numeric values

The purpose of a Gauge is to provide a graphical representation of integer values between zero and a maximum value specified by the third parameter to the constructor. The fourth parameter specifies the initial value that will be graphed.

At any point in time, the Gauge object contains a current value. That value must lie between zero and the specified maximum value, inclusive. Otherwise, the MIDlet will throw a runtime exception.

The maximum value and the current value are within the control of the MIDlet code. However, the display provided by the cell phone may be more granular than the data. In that case, a given display may represent more than one current value.

Restrictions for Gauge objects with Alerts

When a Gauge object is used as a progress or activity indicator for an Alert, it must satisfy the following restrictions:

  1. It must not be interactive.
  2. It must not be owned by another container (Alert or Form).
  3. It must not have any Commands.
  4. It must not have an ItemCommandListener.
  5. It must not have a label (that is, its label must be null).
  6. Its preferred width and height must both be unlocked.
  7. Its layout value must be LAYOUT_DEFAULT.

The Gauge objects that I will use with the sample MIDlets in this lesson will satisfy those requirements.

A non-interactive Gauge

The Gauge objects will be used in non-interactive mode in the sample MIDlets in this lesson. (I plan to illustrate the use of an interactive Gauge in a future lesson.) The non-interactive mode will be used in the sample MIDlets to provide progress or activity feedback to the user.

Progress and activity indicators

In the MIDlet shown in Figure 2, the Gauge object is used to show progress toward a specific goal (six bars). In the MIDlet shown in Figure 3, the Gauge is used to show that the process is active, but there is no indication of percent of completion. In this case, the Gauge is represented by Duke cycling among three different images.

A definite or indefinite range

A non-interactive Gauge can have a definite or indefinite range, and this is where things get a little more complicated. For the easy case where a Gauge has definite range, it will have current value between zero and the maximum value set by the application, inclusive. The implementation will provide a graphical representation of this value similar to that shown in Figure 2.

Four states for an indefinite range

According to Sun, a non-interactive Gauge that has indefinite range will exist in one of four states:

  • CONTINUOUS_IDLE
  • INCREMENTAL_IDLE
  • CONTINUOUS_RUNNING
  • INCREMENTAL_UPDATING

These states are intended to indicate to the user that some level of activity is occurring.

The incremental-updating state can be used to indicate that progress is being made even though there is no known endpoint to the activity. For example, the MIDlet may be loading a file of an unknown size.

The continuous-running state can be used to indicate simply that the process is busy with no specific indication of progress, indefinite or otherwise.

The continuous-idle and incremental-idle states can be used to indicate that no activity is occurring.

Methods of the Gauge class

As of MIDP 2.0, the Gauge class defines eleven methods. However, primarily because of the restricted nature of a Gauge that is attached to an Alert, the sample MIDlets in this lesson use only one of the eleven methods: setValue. As you will see later, this method is used to set the current value of the Gauge each time the Alert is displayed.

The Timer class

An object of the Timer class has no special relationship with the Alert class. You may recall that in previous tutorials I have launched a Worker thread to do the work of a MIDlet and have launched a Toggler thread to control timing and to exercise the MIDlet. An object of the Timer class along with an associated object of the TimerTask class provides a quick and easy way to accomplish the same thing if the requirements aren’t too demanding.

According to Sun, a Timer object provides:

“A facility for threads to schedule tasks for future execution in a background thread. Tasks may be scheduled for one-time execution, or for repeated execution at regular intervals.”

How does it work?

Basically, you instantiate an object of the Timer class to control the timing. Then you use that object to schedule the run method of an object of the TimerTask class to be executed either once at some specified time in the future, or repeatedly with a specified repetition rate beginning at some specified time in the future.

A relatively simple class

The Timer class is a relatively simple class. As of MIDP 2.0 it has a single constructor that takes no parameters and seven methods. One of the methods is a cancel method that terminates the timer discarding any currently scheduled tasks.

The other six methods are overloaded schedule methods or overloaded scheduleAtFixedRate methods. As the names imply, all of these methods are used to schedule future tasks and the overloaded versions simply provide different ways to do that. Regardless of which method you use, you will need an object of the TimerTask class. That brings us to the next topic.

The TimerTask class

The TimerTask class has a single constructor that takes no parameters along with the three methods shown in Figure 5.

Figure 5. Methods of the TimerTask class as of MIDP 2.0.

  • cancel() – Cancels this timer task.
  • run() – The action to be performed by this timer task.
  • scheduledExecutionTime() – Returns the scheduled execution time
    of the most recent actual execution of this task

The names and the brief descriptions of the methods in Figure 5 are self-explanatory. We will be primarily interested in the abstract run method. We will extend the TimerTask class into a new class and override the run method to cause it to perform the desired action at the scheduled points in time.

That concludes the discussion and explanation of the boldface (non Italic) classes shown in Figure 1 that I promised earlier. Now it’s time to examine the code that puts these classes to work.

Preview

In the remainder of this lesson, I will present and explain two MIDlets named Alert01 and Alert02. The primary differences between the two will be in the areas of alert type and Gauge mode.

Discussion and sample code

 
MIDlet testing
The MIDlets in this lesson were tested using a Java SE 6 compiler, targeted at a V1.4 virtual machine, and WTK 2.5.2 running under Windows XP.

The MIDlet named Alert01

The purpose of this MIDlet is to illustrate:

  • The use of an Alert user interface object.
  • The use of image files in conjunction with the Alert.
  • The use of a Timer to schedule the Alert for repeatedly becoming visible.
  • The use of a non-interactive Gauge as a progress meter.

Each time the Alert becomes visible, it obscures a TextBox object that is also being displayed by the MIDlet. When the Alert disappears, the TextBox reappears.

Requirements

This MIDlet requires:

  • The use of the newly updated MIDlet development framework named WTKFramework03.
  • An image file named redball.PNG in the source code directory.
  • An image file named blueball.PNG in the source code directory.

Image files

You can substitute any image files containing small images for the two image files listed above. You will have to make the names of your image files match the references to the image files in the code. Remember that file names referred to by Java code are case sensitive. The case of the reference to the image files in the code must match the actual case of the characters in the image file names.

The MIDlet will run properly in Sun’s cell phone emulator for PNG, GIF, and JPEG image files so long as they are in the directory with the source code and they are properly referenced in the code. This restriction on location is imposed by the framework program named WTKFramework03 and is not imposed by MIDlet technology in general.

The alert type

This MIDlet uses an alert of type ALARM. This results in an audible alert that sounds like a telephone ringing when the MIDlet is run in the Sun cell phone emulator.

Different images are displayed

Each time the Alert becomes visible, it displays either a red ball or a blue ball, depending on whether the time in seconds is even or odd. A red ball is displayed for even values of time and a blue ball is displayed for odd values of time.

The Gauge

A Gauge is displayed with a definite range of zero to six bars as shown in Figure 2. A new bar changes from white to gray each time the Alert becomes visible.

Will discuss in fragments

As usual, I will discuss and explain this MIDlet in fragments. A complete listing of the MIDlet is provided in Listing 14.

The beginning of the MIDlet class named Alert01 including the constructor is shown in Listing 1.

Listing 1. Beginning of the MIDlet class named Alert01.

public class Alert01 extends MIDlet{
  Alert01 theMidlet;
  Image image;
  int count = 0;
  long baseTime;
  
  public Alert01(){
    System.out.println("Construct MIDlet");
    theMidlet = this;
    baseTime = new Date().getTime()/1000;
  }//end constructor

The baseTime variable is populated with the number of seconds that have elapsed since January 1, 1970.  This value will be used to compute elapsed time since the MIDlet started running at several locations in the MIDlet.  Otherwise, the code in Listing 1 is straightforward and shouldn’t require further explanation.

Beginning of the startApp method in Alert01

The startApp method begins in Listing 2. Recall that this method is called by the Application Management Software (AMS) to cause the MIDlet to transition from the paused state to the active state. The MIDlet begins in the paused state when it is launched and constructed. Then the AMS calls the startApp method to cause the MIDlet to enter the paused state.

Listing 2. Beginning of the startApp method in Alert01.

  public void startApp(){
    System.out.println("Create and display a TextBox");

    TextBox textBox = new TextBox("TextBox Title",
                                  "TextBox contents",
                                  50,//width
                                  TextField.ANY);

    //Make the TextBox the current Displayable object.
    Display.getDisplay(this).setCurrent(textBox);

Create a TextBox and make it visible

The code in Listing 2 creates a TextBox object and causes it to be the current Displayable object.  The TextBox will be visible any time that the Alert is not visible.

Schedule a TimerTask

Listing 3 creates and schedules a TimerTask that will display and sound an alert repeatedly. The Alert first appears at two seconds (2000 milliseconds) in the future and repeats every three seconds (3000 milliseconds).

Listing 3. Schedule a TimerTask.

    Timer myTimer = new Timer();
    myTimer.schedule(new MyTimerTask(),2000,3000);

Remaining code in startApp method in Alert01

Then the startApp method sleeps for twenty seconds, following which it cancels the TimerTask and calls the destroyApp method to cause the MIDlet to transition to the destroyed state.

Listing 4. Remaining code in startApp method in Alert01.

    //Sleep for 20 seconds.
    try{Thread.currentThread().sleep(20000);
    } catch(Exception e){}

    //Cancel the timer.
    myTimer.cancel();

    //Enter the destroyed state.
    this.destroyApp(true);
  }//end startApp

The Alert will be displayed several times on a different thread during the twenty seconds that the thread containing the startApp method is sleeping.

The pauseApp and destroyApp methods in Alert01

The pauseApp and destroyApp methods are shown in Listing 5.

Listing 5. The pauseApp and destroyApp methods in Alert01.

  public void pauseApp(){
  }//end pauseApp

  public void destroyApp(boolean unconditional){
    System.out.println("Destroy MIDlet");
    notifyDestroyed();
  }//end destroyApp

For the sake of simplicity, this MIDlet makes no provisions for dealing with the possibility that the pauseApp method might be called by the AMS. Therefore, the pauseApp method is empty.

The destroyApp method displays a terminating message on the standard output device and calls the notifyDestroyed method to signal to the AMS that the MIDlet is entering the destroyed state.

Beginning of the MyTimerTask class

Listing 6 shows the beginning of the class named MyTimerTask, which extends the class named TimerTask including the beginning of the overridden run method. The run method in an object of this class is scheduled to run repeatedly by the code in Listing 3.

Listing 6. Beginning of the class named MyTimerTask in Alert01.

  class MyTimerTask extends TimerTask{
    long time;

    public void run(){
      System.out.println("Display an Alert");

      try{
        //Select among two image files on the basis of
        // whether the current time in seconds is odd
        // or even.
        time = new Date().getTime()/1000 - baseTime;

        //Note that the following file names are case
        // sensitive.
        if((time % 2) == 0){//Even value
          image = Image.createImage(
                                  "/Alert01/redball.PNG");
        }else{//Odd value
          image = Image.createImage(
                                 "/Alert01/blueball.PNG");
        }//end else

Listing 6 begins by computing and saving the time in seconds that have elapsed since the MIDlet object was first constructed when the MIDlet was launched.

Select an image file based on even or odd time value

Then, depending on whether that time value is even or odd, the code in Listing 6 creates an Image object based on one or the other of two image files of type PNG. If the time is even, the Image is created from the data in the file named redball.PNG. Otherwise, the Image is created from the data in the image file named blueball.PNG. It is important at this point to note that the file names used in the code are case sensitive and must match the actual file names.

Create and program an Alert object

Listing 7 creates an Alert object of type ALARM. This alert type results in an audible alarm that sounds like an old-fashioned telephone ringing when the MIDlet is executed in the Sun cell phone emulator.

Listing 7. Create and program an Alert object in Alert01.

        Alert alert = new Alert("Alert Title",
                                "",
                                image,
                                AlertType.ALARM);

        //Cause the alert to display the time in seconds.
        alert.setString("Time in seconds:" + time);

        //Cause the alert to be visible for two seconds.
        alert.setTimeout(2000);

After creating the Alert object, Listing 7 calls the setString method to set the text value displayed by the Alert to include some text plus the elapsed time since the MIDlet was constructed. Thus, each time the Alert becomes visible and the run method is executed, the time that is displayed is different.

Finally, Listing 7 calls the setTimeout method to establish the amount of time that the Alert will remain visible to 2000 milliseconds or two seconds.

The visible/non-visible cycle

Referring back to Listing 3, the Alert will be made visible every three seconds following an initial visibility at two seconds. Referring to Listing 7, the Alert will remain visible for two seconds each time it is made visible.

The TextBox object should initially be visible for two seconds. Then there should be a repeating pattern of the Alert being visible for two seconds followed by the TextBox being visible for one second.

At the end of twenty seconds:

  • The thread containing the startApp method will wake up.
  • The Timer will be cancelled
  • The MIDlet will enter the destroyed state.

If you run the MIDlet and watch closely, you should see time values of 2, 5, 8, 11, 14, and 17 displayed by the text in the Alert.

Create and service the Gauge

Listing 8 begins by creating a Gauge that shows six bars as shown in Figure 2.

Listing 8. Create and service the Gauge in Alert01.

        Gauge gauge = new Gauge(null,false,6,0);

        //Set the number of Gauge bars to be illuminated.
        gauge.setValue(++count);

        //Attach the Gauge to the alert.
        alert.setIndicator(gauge);

The variable named count is used to count the number of times that the run method of the TimerTask object has been executed. Each time the method is executed, it increments the counter by one and calls the setValue method on the Gauge to set the number of vertical bars that should be displayed in gray instead of white as shown in Figure 2.

Finally, Listing 8 calls the setIndicator method on the Alert object to attach the Gauge to the Alert object, producing the visual output shown in Figure 2.

Make the Alert visible

Listing 9 calls the setCurrent method on the Display object (that represents the cell phone display) to cause the Alert object to become the current Displayable object. This causes the Alert to be displayed on the cell phone screen.

Listing 9. Make the Alert visible in the MIDlet named Alert01.

        Display.getDisplay(theMidlet).setCurrent(alert);
      }catch(Exception e){
        e.printStackTrace();
      }//end catch
    }//end run
  }//end class MyTimerTask
}//end class Alert01

Listing 9 also signals the end of the run method, the end of the member class named MyTimerTask, and the end of the MIDlet class named Alert01.

The MIDlet named Alert02

As mentioned earlier, this MIDlet is very similar to the MIDlet named Alert01. Therefore, I will confine my explanation to the code that is different between the two and the results imparted by those code differences.

The purpose of this MIDlet is to illustrate:

  • The use of an Alert user interface object.
  • The use of image files in conjunction with the Alert object.
  • The use of a Timer to schedule the Alert for repeatedly becoming visible.
  • The use of a non-interactive Gauge as an activity meter.

A progress meter versus an activity meter

The big difference between the purpose of this MIDlet and the earlier MIDlet named Alert01 is in the difference between a progress meter and an activity meter. Whereas a progress meter shows progress toward a known goal as in Figure 2, an activity meter simply shows that the MIDlet is active toward reaching an undefined goal as in Figure 3.

A CONFIRMATION alert

This MIDlet uses an alert of type CONFIRMATION whereas the MIDlet named Alert01 uses an alert of type ALARM. When run in the Sun cell phone emulator, an alert type of type CONFIRMATION results in an audible alert consisting of a series of three chimes whereas an alert type ALARM results in the sound of an old-fashioned telephone ringing.

The GAUGE

For this MIDlet, a Gauge is displayed as:

Gauge.INDEFINITE, Gauge.INCREMENTAL_UPDATING

This requires that a maximum value of three be passed to the setValue method because only three graphics are displayed and they need to repeat for values greater than three. A new graphic is displayed each time the alert becomes visible. The first graphic has Duke doing a cartwheel. The second graphic has Duke rendered in black, white, and red. The third graphic has Duke rendered in gray, white, and red as shown in Figure 3.

Create an Alert object for the MIDlet named Alert02

The first major difference between the code in this MIDlet and the code in the earlier MIDlet named Alert01 occurs in Listing 10.

Listing 10. Create an Alert object for the MIDlet named Alert02.

        //Create an Alert object of type CONFIRMATION.
        // This results in an audible alert that is three
        // chimes.
        Alert alert = new Alert("Alert Title",
                                "",
                                image,
                                AlertType.CONFIRMATION);

As you can see in Listing 10, this MIDlet sets the alert type to CONFIRMATION. This results in an audible alert produced by the Sun cell phone emulator being a series of three chimes.

Referring back to Listing 7, you can see that the alert type for the previous MIDlet was ALARM. This results in an audible alert produced by the Sun cell phone emulator that sounds like an old-fashioned telephone ringing.

Create a Gauge for the MIDlet named Alert02

The next major difference in code between the two MIDlets is shown in Listing 11.

Listing 11. Create a Gauge for the MIDlet named Alert02.

        Gauge gauge = new Gauge(
                              null,
                              false,
                              Gauge.INDEFINITE,
                              Gauge.INCREMENTAL_UPDATING);

Whereas the previous MIDlet created a Gauge with a definite maximum value of 6 (see Listing 8), this MIDlet creates a Gauge object with a maximum value of INDEFINITE. This makes it possible to specify one of the values in the earlier list for the final parameter.

The last two parameters passed to the constructor for the Gauge class in Figure 11 cause a Gauge to be constructed that shows three repeating images of Duke when the MIDlet is run in the Sun cell phone emulator. The first image has Duke doing a cartwheel. The second image has Duke in black, white, and red. The third image has Duke in gray, white, and red as shown in Figure 3. Then the sequence repeats.

Set the current value of the Gauge to be count mod 3

The final difference in the code between the two MIDlets is shown in Listing 12. Whereas the code in the run method in the earlier MIDlet simply set the current value of the Gauge to be the value of count (see Listing 8), Listing 12 sets the current value to be count mod 3. This causes the current value to count from 0 to 2 and then repeat the sequence each time the value reaches 3.

Listing 12. Set the current value of the Gauge to be count mod 3.

        gauge.setValue(++count % 3);

The remaining code in the MIDlet named Alert02 is the same as the code in the MIDlet named Alert01.

Run the programs

I encourage you to copy the code from Listing 13, Listing 14, and Listing 15. Run the two MIDlets in the updated MIDlet development framework named WTKFramework03 that is provided Listing 13. Experiment with the MIDlet code, making changes and running your modified MIDlets in the framework program. See if you can explain the results produced by your changes.

Don’t forget that you will need to download and install the latest version of the Sun Java Wireless Toolkit for CLDC (see Resources). As of the date this lesson is being written, the latest version of the toolkit is WTK2.5.2.

You will also need two small image files. You can substitute any image files containing small images for the two image files listed above. You will have to make the names of your image files match the references to the image files in the code (see Listing 6).

Summary

In this lesson you learned

  • How to program an Alert for a MIDlet.
  • How to add an image to the Alert.
  • How to add a Gauge to the Alert to act as a progress or activity meter.
  • How to use a Timer to control when an Alert becomes visible on the screen.

What’s next?

In the next lesson, you will learn about the Choice interface and the List class, which implements the Choice interface. You will learn about the composition of a Choice object. You will learn about implicit, exclusive, and multiple Choice types. You will learn about the selected state of a Choice element of each of the different types. Finally, you will learn how to create a List, how to display it in the Sun cell phone emulator, and how to determine which elements in the List are selected.

Resources

Complete program listings

Complete listings of the programs discussed in this lesson are shown in Listing 13, Listing 14, and Listing 15 below:

Listing 13. The updated MIDlet development framework named WTKFramework03.

/*WTKFramework03.java
Copyright 2007, R.G.Baldwin

Updated: December 17, 2007

Version: WTKFramework03.java
Upgraded to prevent the deletion of image files and other 
resource files when the program cleans up after itself.
This results in resource files being included in the JAR 
file.  The resource files should be in the same directory 
as the source files.

Version: WTKFramework02.java
Upgraded to capture and display standard output and error
output from child processes.

Also upgraded to allow user to enter MIDlet name on the
command line.  This is particularly useful when repeatedly
running this program from a batch file during MIDlet
development.

Version: WTKFramework01.java
The purpose of this program is to provide a framework that
makes it easy to experiment with Java MIDlets written to
run on small mobile devices using the Sun Java Wireless
Toolkit (WTK2.5.2).  The framework not only makes such
experimentation easy, it also cleans up after itself by
automatically deleting all of the extraneous files created
during the development of the JAR and JAD files, which
are required for the deployment of the MIDlet program.

Given a file containing the source code for the MIDlet,
a single click of the mouse causes this framework to
automatically cycle through the following steps:

Compilation (targeted to Java v1.4 virtual machine)
Pre-verification
Creation of the manifest file
Creation of the JAR file
Creation of the JAD file
Deletion of extraneous files, saving the JAR and JAD files
Deployment and execution in Sun's cell phone emulator

The MIDlet being processed must be stored in a folder
having the same name as the main MIDlet class.  The
folder containing the MIDlet must be a child of the
folder in which the framework is being executed.

Note: When you transfer control to a new process window by
calling the exec method, the path environment variable
doesn't go along for the ride.  Therefore, you must
provide the full path for programs that you call in that
new process.

Tested using Java SE 6 and WTK2.5.2 running under
Windows XP.
*********************************************************/

import java.io.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class WTKFramework03{
  String toolkit = "M:/WTK2.5.2";//Path to toolkit root
  String vendor = "Dick Baldwin";//Default vendor name
  String midletVersion = "1.0.0";
  String profile = "MIDP-2.0";
  String profileJar = "/lib/midpapi20.jar";
  String config = "CLDC-1.1";
  String configJar = "/lib/cldcapi11.jar";
  //Path to the bin folder of the Java installation
  String javaPath = "C:/Program Files/Java/jdk1.6.0/bin";
  String prog = "WTK001";

  int initialCleanupOK = 1;//Success = 0
  int compileOK = 1;//Compiler success = 0
  int preverifyOK = 1;//Preverify success = 0
  int deleteClassFilesOK = 1;//Delete success = 0
  int moveFilesOK = 1;//Move success = 0
  int manifestFileOK = 1;//Manifest success = 0
  int jarFileOK = 1;//Jar file success = 0
  int jadFileOK = 1;//Jad file success = 0
  int cleanupOK = 1;//Cleanup success = 0

  long jarFileSize = 0;

  JTextField progName;
  JTextField WTKroot;
  JTextField vendorText;
  JTextField midletVersionText;
  JTextField javaPathText;

  JRadioButton pButton10;
  JRadioButton pButton20;
  JRadioButton pButton21;

  JRadioButton cButton10;
  JRadioButton cButton11;

  static WTKFramework03 thisObj;
  //----------------------------------------------------//

  public static void main(String[] args){
    //Allow user to enter the MIDlet name on the command
    // line.  Useful when running from a batch file.
    thisObj = new WTKFramework03();
    if(args.length != 0)thisObj.prog = args[0];
    thisObj.new GUI();
  }//end main
  //----------------------------------------------------//

  void runTheProgram(){
    //This method is called when the user clicks the Run
    // button on the GUI.
    System.out.println("PROGRESS REPORT");
    System.out.println("Running program named: " + prog);

    //This code calls several methods in sequence to
    // accomplish the needed actions. If there is a
    // failure at any step along the way, the
    // framework will terminate at that point with a
    // suitable error message.

    //Delete leftover files from a previous run, if any
    // exist
    deleteOldStuff();
    if(initialCleanupOK != 0){//Test for success
      System.out.println("Initial cleanup error");
      System.out.println("Terminating");
      System.exit(1);
    }//end if

    compile();//compile the MIDlet
    if(compileOK != 0){//Test for successful compilation.
      System.out.println("Terminating");
      System.exit(1);
    }//end if

    preverify();//Pre-verify the MIDlet class files
    if(preverifyOK != 0){
      System.out.println("Terminating");
      System.exit(1);
    }//end if

    //Delete the class files from the original program
    // folder
    deleteClassFilesOK = deleteProgClassFiles();
    if(deleteClassFilesOK != 0){
      System.out.println("Terminating");
      System.exit(1);
    }//end if

    //Move the preverified files back to the original
    // program folder
    movePreverifiedFiles();
    if(moveFilesOK != 0){
      System.out.println("Terminating");
      System.exit(1);
    }//end if

    //Make manifest file
    makeManifestFile();
    if(manifestFileOK != 0){
      System.out.println("Manifest file error");
      System.out.println("Terminating");
      System.exit(1);
    }//end if

    //Make Jar file
    makeJarFile();
    if(jarFileOK != 0){
      System.out.println("JAR file error");
      System.out.println("Terminating");
      System.exit(1);
    }//end if

    //Make Jad file
    makeJadFile();
    if(jadFileOK != 0){
      System.out.println("Terminating");
      System.exit(1);
    }//end if

    //Delete extraneous files
    cleanup();
    if(cleanupOK != 0){
      System.out.println("Terminating");
      System.exit(1);
    }//end if

    //Run emulator
    runEmulator();

    //Reset success flags
    initialCleanupOK = 1;//Success = 0
    compileOK = 1;//Compiler success = 0
    preverifyOK = 1;//Preverify success = 0
    deleteClassFilesOK = 1;//Delete success = 0
    moveFilesOK = 1;//Move success = 0
    manifestFileOK = 1;//Manifest success = 0
    jarFileOK = 1;//Jar file success = 0
    jadFileOK = 1;//Jad file success = 0
    cleanupOK = 1;//Cleanup success = 0

    //Control returns to here when the user terminates
    // the cell phone emulator.
    System.out.println(
      "nClick the Run button to run another MIDlet.");
    System.out.println();//blank line

  }//end runTheProgram
  //----------------------------------------------------//

  //Purpose: Delete leftover files at startup
  void deleteOldStuff(){
    System.out.println(
           "Deleting leftover files from a previous run");

    //Delete subdirectory from output folder if it exists.
    int successFlag = deleteOutputSubDir();

    //Delete manifest file if it exists.
    File manifestFile = new File("output/Manifest.mf");
    if(manifestFile.exists()){
      boolean success = manifestFile.delete();
      if(success){
        System.out.println("   Manifest file deleted");
      }else{
        successFlag = 1;
      }//end else
    }//end if

    //Delete old JAR file if it exists.
    File jarFile = new File("output/" + prog + ".jar");
    if(jarFile.exists()){
      boolean success = jarFile.delete();
      if(success){
        System.out.println("   Old jar file deleted");
      }else{
        successFlag = 1;
      }//end else
    }//end if

    //Delete old JAD file if it exists.
    File jadFile = new File("output/" + prog + ".jad");
    if(jadFile.exists()){
      boolean success = jadFile.delete();
      if(success){
        System.out.println("   Old jad file deleted");
      }else{
        successFlag = 1;
      }//end else
    }//end if

    //Delete class files from program folder, if any exist
    int temp = deleteProgClassFiles();
    if(temp != 0){
      successFlag = temp;
    }//end if

    if(successFlag == 0){
      System.out.println("nLeftover files deleted");
      initialCleanupOK = 0;//success
    }else{
    }//end else

  }//end deleteOldStuff
  //----------------------------------------------------//

  //This method compiles the MIDlet.  Note that the
  // compilation generates class files for a Java v1.4
  // virtual machine.  Apparently WTK2.5.2 is not
  // compatible with Java 1.6.  Also note that the
  // compiler uses the classes in the WTK2.5.2 library
  // JAR files instead of using the classes in the
  // J2SE v1.6 libraries.
  void compile(){
    try{
      String cmdString =
           javaPath + "/javac -target 1.4 -source 1.4 "
           + "-bootclasspath " + toolkit + configJar + ";"
           + toolkit + profileJar
           + " " + prog + "/" + prog + ".java";

      System.out.println("nCompile command:");
      System.out.println(cmdString);
      Process proc = Runtime.getRuntime().exec(cmdString);

      //Get and display the standard output and/or the
      // error output produced by the child process.
      getChildOutput(proc);

      //Delay until compilation is complete. Then test the
      // exit value of the compiler for success.  A value
      // of 0 indicates success.  Any other value
      // indicates that the compilation was not
      // successful.  Also note that this framework does
      // not display the compiler error messages.  If a
      // compilation fails, the next step is to use the
      // same approach from a batch file or from the
      // command line to perform the compilation on a
      // stand-alone basis.  That will expose the error
      // messages and allow for identifying and fixing the
      // problem in the MIDlet code.
      int val = proc.waitFor();
      compileOK = val;
      if(val == 0){
        System.out.println("Compile OK");
      }else{
        System.out.println("Compile error");
      }//end else

    }catch( Exception e ){
      e.printStackTrace();
    }//end catch

  }//end compile
  //----------------------------------------------------//

  //Purpose:  To preverify the class files.
  void preverify(){

    System.out.println(
        "nPreverifying class files.");
    File dir = new File(prog);
    //Get a list of the files in the folder.
    String[] children = dir.list();
    for(int cnt = 0;cnt < children.length;cnt++){
      //Only preverify files with an extension of .class
      String aFile = children[cnt];
      if(aFile.endsWith(".class")){
        //This is a class file
        try{
          //Remove the .class extension from the file name
          aFile = aFile.substring(0,aFile.lastIndexOf(
                                               ".class"));

          String cmdString =
                toolkit + "/bin/preverify.exe -classpath "
                + toolkit + configJar + ";"
                + toolkit + profileJar + " "
                + prog + "/" + aFile;

          System.out.println("nPreverify command");
          System.out.println(cmdString);
          Process proc = Runtime.getRuntime().exec(
                                               cmdString);
          getChildOutput(proc);

          //Delay until preverification is complete
          int val = proc.waitFor();
          preverifyOK = val;
          if(val == 0){
            System.out.println("Pre-verify OK");
          }else{
            System.out.println("Pre-verify error");
            return;//return prematurely
          }//end else

        }catch( Exception e ){
          e.printStackTrace();
        }//end catch
      }//end if
    }//end for loop
  }//end preverify
  //----------------------------------------------------//

    //Purpose: Move preverified files back to original
    // program folder to make it easier to put them into
    // a JAR file with the correct path.
    void movePreverifiedFiles(){

    System.out.println(
         "nMoving preverified files to program folder.");
    File dir = new File("output/" + prog);
    //Get a list of the files in the folder.
    String[] children = dir.list();

    //Destination directory
    File dest = new File(prog);

    for(int cnt = 0;cnt < children.length;cnt++){
      String filename = children[cnt];

      try{
        // File to be moved
        String temp = "output/" + prog + "/" + filename;
        File file = new File(temp);

        // Move file to destination directory
        boolean success = file.renameTo(
                           new File(dest,file.getName()));
        if (!success){
          System.out.println("File move error");
          return;//return prematurely
        }else{
         System.out.println("   " + temp + " moved");
        }//end else

      }catch( Exception e ){
        e.printStackTrace();
      }//end catch
    }//end for loop

    moveFilesOK = 0;//Successful move
  }//end movePreverifiedFiles
  //----------------------------------------------------//

  void makeManifestFile(){
    try{
      BufferedWriter out = new BufferedWriter(
                    new FileWriter("output/Manifest.mf"));
      out.write("MIDlet-Name: " + prog + "n");
      out.write(
               "MIDlet-Version: " + midletVersion + "n");
      out.write("MIDlet-Vendor: " + vendor + "n");
      out.close();
      System.out.println("nManifest file written");
      manifestFileOK = 0;
    }catch( Exception e ){
      e.printStackTrace();
    }//end catch

  }//end makeManifestFile
  //----------------------------------------------------//

  void makeJarFile(){
    try{
      String cmdString =
                    javaPath + "/jar cvfm output/" + prog
                    + ".jar output/Manifest.mf ./" + prog;

      System.out.println("nJAR command");
      System.out.println(cmdString);
      Process proc = Runtime.getRuntime().exec(cmdString);

      getChildOutput(proc);

      //Delay until complete
      int val = proc.waitFor();
      jarFileOK = val;
      if(val == 0){
        System.out.println("Jar file written");
      }else{
        System.out.println("Jar file error");
        return;//Return prematurely on error.
      }//end else

      //Get and save file size in bytes
      File file = new File("output/" + prog + ".jar");
      jarFileSize = file.length();
      System.out.println(
                     "   Jar file size = " + jarFileSize);

    }catch( Exception e ){
      e.printStackTrace();
    }//end catch

  }//end makeJarFile
  //----------------------------------------------------//

  void makeJadFile(){
    try{
      BufferedWriter out = new BufferedWriter(
               new FileWriter("output/" + prog + ".jad"));

      out.write("MIDlet-1: " + prog + ", , " + prog
                                + "." + prog + "" + "n");

      out.write("MIDlet-Name: " + prog + "n");
      out.write("MIDlet-Version: "
                                  + midletVersion + "n");
      out.write("MIDlet-Vendor: " + vendor + "n");
      out.write("MIDlet-Jar-URL: " + prog + ".jarn");
      out.write("MIDlet-Jar-Size: " + jarFileSize + "n");
      out.write("MicroEdition-Profile: "
                                        + profile + "n");
      out.write("MicroEdition-Configuration: "
                                         + config + "n");
      out.close();

      System.out.println("nJad file written");
      jadFileOK = 0;
    }catch( Exception e ){
      e.printStackTrace();
    }//end catch
  }//end makeJadFile
  //----------------------------------------------------//

  //Purpose: To run Sun's cell phone emulator with this
  // MIDlet.
  void runEmulator(){
    try{
      String cmdString = toolkit + "/bin/emulator.exe "
                + "-Xdescriptor output/" + prog + ".jad";

      System.out.println("nEmulator command");
      System.out.println(cmdString);
      Process proc = Runtime.getRuntime().exec(cmdString);

      getChildOutput(proc);

      //Delay until complete
      int val = proc.waitFor();
      if(val == 0){
        System.out.println("Emulator finished");
      }else{
        System.out.println("Emulator error");
      }//end else

    }catch( Exception e ){
      e.printStackTrace();
    }//end catch

  }//end runEmulator
  //----------------------------------------------------//

  //Purpose:  Delete all files and folders in the output
  // folder other than the jar and jad files. Also delete
  // all class files in the program folder.
  void cleanup(){
    //Delete subdirectory from output folder.
    int successFlag = deleteOutputSubDir();

    //Delete manifest file from output folder.
    File manifestFile = new File("output/Manifest.mf");
    if(manifestFile.exists()){
      boolean success = manifestFile.delete();
      if(success){
        System.out.println("   Manifest file deleted");
      }else{
        successFlag = 1;
      }//end else
    }//end if

    //Delete class files from program folder
    int temp = deleteProgClassFiles();
    if(temp != 0){
      successFlag = temp;
    }//end if

    if(successFlag == 0){
      System.out.println("nExtraneous files deleted");
      cleanupOK = 0;//success
    }else{
    }//end else

  }//end cleanup
  //----------------------------------------------------//

  //Purpose: To delete the folder contained in the output
  // directory and all of its files.
  int deleteOutputSubDir(){
    int returnVal = 0;

    System.out.println(
                  "nDeleting files from output folder.");
    //First delete the files in the subdirectory
    File subDir = new File("output/" + prog);
    //Get a list of the files in the folder.
    String[] children = subDir.list();
    if(children != null){
      for(int cnt = 0;cnt < children.length;cnt++){

        boolean success = (new File("output/" + prog + "/"
                               + children[cnt])).delete();
        if(!success){
          // Deletion failed
          returnVal = 1;
        }else{
          System.out.println(
                      "   " + children[cnt] + " deleted");
        }//end else
      }//end for loop
    }//end if !null

    //Now delete the subdirectory
    if(subDir.exists()){
      boolean success = subDir.delete();
      if(!success){
        // Deletion failed
        returnVal = 1;
      }else{
        System.out.println("   Empty directory named "
                         + "output/" + prog + " deleted");
      }//end else
    }//end if

    return returnVal;
  }//end deleteOutputSubDir
  //----------------------------------------------------//

  //The purpose of this method is to delete the compiled
  // class files from the program folder.
  int deleteProgClassFiles(){
    int returnVal = 0;

    System.out.println(
        "nDeleting class files from program folder.");
    File dir = new File(prog);
    //Get a list of the files in the folder.
    String[] children = dir.list();
    for(int cnt = 0;cnt < children.length;cnt++){
      //Only delete files with an extension of .class
      String aFile = children[cnt];
      if(aFile.indexOf(".class") >= 0 ){
        //This is a class file, delete it.
        boolean success = (
                   new File(prog + "/" + aFile).delete());
        if(!success){
          // Deletion failed
          returnVal = 1;
        }else{
          System.out.println("   " + aFile + " deleted");
        }//end else
      }else{
        //This is not a class file.
        System.out.println("   " + aFile + " saved");
      }//end else
      
    }//end for loop

    return returnVal;
  }//end deleteProgClassFiles
  //----------------------------------------------------//

  //Purpose: To get and display standard output and error
  // output from child processes.
  void getChildOutput(Process proc){
    try{
      //Connect an input stream to child's standard
      // output and child's error output.  Then
      // instantiate a thread to get and display each
      // type of output data from the child process.

      //Spawn two threads.
      ChildDataHandler errorHandler = 
                           new ChildDataHandler(
                           proc.getErrorStream(),"ERR: ");
      ChildDataHandler outputHandler = 
                           new ChildDataHandler(
                           proc.getInputStream(),"OUT: ");

      //Start them running
      errorHandler.start();
      outputHandler.start();

    }catch( Exception e ){
      e.printStackTrace();
    }//end catch
  }//end getChildOutput
  //====================================================//

  //This is an inner class that controls the interactive
  // behavior of the framework.
  class GUI extends JFrame{

    GUI(){//constructor
      setSize(400,250);
      setTitle("Copyright 2007, R.G.Baldwin");
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      JButton runButton = new JButton("Run");
      JPanel runPanel = new JPanel();
      runPanel.add(runButton);
      getContentPane().add(runPanel,"South");

      //Construct and populate fields for entry of text
      // data.
      JPanel textData = new JPanel();
      textData.setLayout(new GridLayout(0,2));
      getContentPane().add(textData,"North");

      //Populate first row of grid with default values.
      textData.add(new JLabel("  Program Name"));
      progName = new JTextField(prog);
      textData.add(progName);

      //Populate second row, etc.
      textData.add(new JLabel("  WTK Root"));
      WTKroot = new JTextField(toolkit);
      textData.add(WTKroot);

      //Populate third row ...
      textData.add(new JLabel("  Vendor"));
      vendorText = new JTextField(vendor);
      textData.add(vendorText);

      //Populate fourth row ...
      textData.add(new JLabel("  Midlet Version"));
      midletVersionText = new JTextField(midletVersion);
      textData.add(midletVersionText);

      //Populate fifth row
      textData.add(new JLabel(
                            "  Path to Java bin folder"));
      javaPathText = new JTextField(javaPath);
      textData.add(javaPathText);

      //Create column titles for profile and configuration
      // buttons
      textData.add(new JLabel(" "));//spacer
      textData.add(new JLabel(" "));//spacer
      textData.add(new JLabel("  Profile"));
      textData.add(new JLabel("  Configuration"));

      //Construct and populate radio buttons for
      // selection of profile and configuration.
      JPanel proCon = new JPanel();
      proCon.setLayout(new GridLayout(0,2));
      getContentPane().add(proCon,"Center");

      //Construct JPanel with radio buttons for selection
      // of profile.
      JPanel proButtons = new JPanel();
      proButtons.setLayout(new GridLayout(0,1));
      pButton10 = new JRadioButton("MIDP-1.0");
      pButton20 = new JRadioButton("MIDP-2.0",true);
      pButton21 = new JRadioButton("MIDP-2.1");

      //Make the buttons mutually exclusive.
      ButtonGroup profileGroup = new ButtonGroup();
      profileGroup.add(pButton10);
      profileGroup.add(pButton20);
      profileGroup.add(pButton21);

      //Add the radio buttons to the GUI
      proButtons.add(pButton10);
      proButtons.add(pButton20);
      proButtons.add(pButton21);
      proCon.add(proButtons);

      //Construct JPanel with radio buttons for selection
      // of configuration.
      JPanel configButtons = new JPanel();
      configButtons.setLayout(new GridLayout(0,1));
      cButton10 = new JRadioButton("CLDC-1.0");
      cButton11 = new JRadioButton("CLDC-1.1",true);

      //Make the buttons mutually exclusive.
      ButtonGroup configGroup = new ButtonGroup();
      configGroup.add(cButton10);
      configGroup.add(cButton11);

      //Add the radio buttons to the GUI
      configButtons.add(cButton10);
      configButtons.add(cButton11);
      proCon.add(configButtons);

      //Register an action listener on the Run button
      runButton.addActionListener(
        new ActionListener(){
          public void actionPerformed(ActionEvent e){
            //Get user inputs from text fields
            prog = progName.getText();
            toolkit = WTKroot.getText();
            vendor = vendorText.getText();
            midletVersion = midletVersionText.getText();
            javaPath = javaPathText.getText();

            //Set the profile based on which radio button
            // was selected by the user
            if(pButton10.isSelected()){
              profile = "MIDP-1.0";
              profileJar = "/lib/midpapi10.jar";
            }else if (pButton20.isSelected()){
              profile = "MIDP-2.0";
              profileJar = "/lib/midpapi20.jar";
            }else{//no other choice available
              profile = "MIDP-2.1";
              profileJar = "/lib/midpapi21.jar";
            }//end else

            //Set the configuration based on which radio
            // button was selected by the user
            if(cButton10.isSelected()){
              config = "CLDC-1.0";
              configJar = "/lib/cldcapi10.jar";
            }else{//no other choice available
              config = "CLDC-1.1";
              configJar = "/lib/cldcapi11.jar";
            }//end else

            //Now run the program.
            runTheProgram();

          }//end actionPerformed
        }//end new ActionListener
      );//end addActionListener

      setVisible(true);
    }//end constructor
  }//end class GUI
  //====================================================//
  
  //This is a member class.  Thanks to "When Runtime.
  // exec() won't" By Michael C. Daconta, JavaWorld.com
  // The purpose of an object of this class is to get and
  // display either the standard output or the error
  // output produced by a child process.
  class ChildDataHandler extends Thread{
    InputStream inputStream;
    String type;

    ChildDataHandler(InputStream inputStream,String type){
      this.inputStream = inputStream;
      this.type = type;
    }//end constructor

    public void run(){
      try{
        InputStreamReader inputStreamReader = 
                       new InputStreamReader(inputStream);
        BufferedReader bufferedReader = 
                    new BufferedReader(inputStreamReader);
        String line=null;
        while (
              (line = bufferedReader.readLine()) != null){
          System.out.println(type + line);
        }//end while
      }catch (Exception e){
        e.printStackTrace();  
      }//end catch
    }//end run
  }//end class ChildDataHandler  
  
  //====================================================//
}//end class WTKFramework03
//======================================================//

 

Listing 14. Source code for the MIDlet named Alert01.

/*Alert01.java
Copyright 2007, R.G.Baldwin

The purpose of this MIDlet is to illustrate:
a. The use of an Alert user interface object.
b. The use of image files in conjunction with the Alert.
c. The use of a Timer to schedule the Alert for 
   repeatedly becoming visible.
d. The use of a non-interactive Gauge as a progress
   meter.
   
Each time the Alert becomes visible, it obscures a 
TextBox object that is also being displayed.
   

This MIDlet requires:
a. The use of WTKFramework03
b. An image file named redball.PNG in the source code
   directory
c. An image file named blueball.PNG in the source code
   directory

Will work properly for .png, .gif, and .jpeg image
files so long as they are in the directory with the
source code. This restriction on location is imposed
by the framework program named WTKFramework03 and not
by MIDlet technology in general.

This version uses an alert of type ALARM. This results
in an audible alert that sounds like a telephone ringing.

Each time the alert becomes visible, it displays either
a red ball or a blue ball, depending on whether the time
in seconds is even or odd. A red ball is displayed for
even values of time.

A Gauge is displayed with a definite range of zero to six
bars.  A new bar is illuminated each time the alert 
becomes visible.

Tested using a Java SE 6 compiler, targeted at a V1.4
virtual machine, and WTK 2.5.2 running under Windows XP.
*********************************************************/

package Alert01;

import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Gauge;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.TextBox;
import javax.microedition.lcdui.TextField;
import javax.microedition.midlet.MIDlet;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Date;

public class Alert01 extends MIDlet{

  Alert01 theMidlet;
  Image image;
  int count = 0;
  long baseTime;

  public Alert01(){
    System.out.println("Construct MIDlet");
    theMidlet = this;
    baseTime = new Date().getTime()/1000;
  }//end constructor
  //----------------------------------------------------//

  public void startApp(){
    System.out.println("Create and display a TextBox");

    TextBox textBox = new TextBox("TextBox Title",
                                  "TextBox contents",
                                  50,//width
                                  TextField.ANY);
                                  
    //Make the TextBox the current Displayable object.
    Display.getDisplay(this).setCurrent(textBox);

    //Now create and schedule a TimerTask that will 
    // display and sound an alert repeatedly. The Alert
    // first appears at two seconds and repeats every
    // three seconds.
    Timer myTimer = new Timer();
    myTimer.schedule(new MyTimerTask(),2000,3000);

    //Sleep for 20 seconds.
    try{Thread.currentThread().sleep(20000);
    } catch(Exception e){}

    //Cancel the timer.
    myTimer.cancel();

    //Enter the destroyed state.
    this.destroyApp(true);
  }//end startApp

  public void pauseApp(){
  }//end pauseApp

  public void destroyApp(boolean unconditional){
    System.out.println("Destroy MIDlet");
    notifyDestroyed();
  }//end destroyApp
  //----------------------------------------------------//

  //This is a member class
  class MyTimerTask extends TimerTask{
    long time;

    public void run(){
      System.out.println("Display an Alert");

      try{
        //Select among two image files on the basis of
        // whether the current time in seconds is odd
        // or even.
        time = new Date().getTime()/1000 - baseTime;

        //Note that the following file names are case
        // sensitive.
        if((time % 2) == 0){//Even value
          image = Image.createImage(
                                  "/Alert01/redball.PNG");
        }else{//Odd value
          image = Image.createImage(
                                 "/Alert01/blueball.PNG");
        }//end else


        //Create an Alert object of type ALARM. This 
        // results in an audible alarm that sounds like a
        // telephone ringing.
        Alert alert = new Alert("Alert Title",
                                "",
                                image,
                                AlertType.ALARM);
                                
        //Cause the alert to display the time in seconds.
        alert.setString("Time in seconds:" + time);

        //Cause the alert to be visible for two seconds.
        alert.setTimeout(2000);

        //Create a Gauge that shows six bars.
        Gauge gauge = new Gauge(null,false,6,0);

        //Set the number of Gauge bars to be illuminated.
        gauge.setValue(++count);

        //Attach the Gauge to the alert.
        alert.setIndicator(gauge);

        //Make the Alert the current Displayable object.
        Display.getDisplay(theMidlet).setCurrent(alert);
      }catch(Exception e){
        e.printStackTrace();
      }//end catch
    }//end run
  }//end class MyTimerTask
  //====================================================//
}//end class Alert01
//======================================================//

 

Listing 15. Source code for the MIDlet named Alert02.

/*Alert02.java
Copyright 2007, R.G.Baldwin

The purpose of this MIDlet is to illustrate:
a. The use of an Alert user interface object.
b. The use of image files in conjunction with the Alert.
c. The use of a Timer to schedule the Alert for 
   repeatedly becoming visible.
d. The use of a non-interactive Gauge as a progress
   meter.

Each time the Alert becomes visible, it obscures a 
TextBox object that is also being displayed.


This MIDlet requires:
a. The use of WTKFramework03
b. An image file named redball.PNG in the source code
   directory
c. An image file named blueball.PNG in the source code
   directory

Will work properly for .png, .gif, and .jpeg image
files so long as they are in the directory with the
source code. This restriction on location is imposed
by the framework program named WTKFramework03 and not
by MIDlet technology in general.

This version uses an alert of type CONFIRMATION. This
results in an audible alert consisting of three chimes.

Each time the alert becomes visible, it displays either
a red ball or a blue ball, depending on whether the time
in seconds is even or odd. A red ball is displayed for
even values of time.

A Gauge is displayed as Gauge.INDEFINITE,
Gauge.INCREMENTAL_UPDATING.  This requires that a 
maximum value of three be passed to the setValue method
because only three graphics are displayed and they need
to repeat for values greater than three.  A new graphic
is displayed each time the alert becomes visible.

Tested using a Java SE 6 compiler, targeted at a V1.4
virtual machine, and WTK 2.5.2 running under Windows XP.
*********************************************************/

package Alert02;

import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Gauge;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.TextBox;
import javax.microedition.lcdui.TextField;
import javax.microedition.midlet.MIDlet;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Date;

public class Alert02 extends MIDlet{

  Alert02 theMidlet;
  Image image;
  int count = 0;
  long baseTime;

  public Alert02(){
    System.out.println("Construct MIDlet");
    theMidlet = this;
    baseTime = new Date().getTime()/1000;
  }//end constructor
  //----------------------------------------------------//

  public void startApp(){
    System.out.println("Create and display a TextBox");

    TextBox textBox = new TextBox("TextBox Title",
                                  "TextBox contents",
                                  50,//width
                                  TextField.ANY);
                                  
    //Make the TextBox the current Displayable object.
    Display.getDisplay(this).setCurrent(textBox);

    //Now create and schedule a TimerTask that will 
    // display and sound an alert repeatedly, The Alert
    // first appears at two seconds and repeats every
    // three seconds.
    Timer myTimer = new Timer();
    myTimer.schedule(new MyTimerTask(),2000,3000);

    //Sleep for 20 seconds.
    try{Thread.currentThread().sleep(20000);
    } catch(Exception e){}

    //Cancel the timer.
    myTimer.cancel();

    //Enter the destroyed state.
    this.destroyApp(true);
  }//end startApp

  public void pauseApp(){
  }//end pauseApp

  public void destroyApp(boolean unconditional){
    System.out.println("Destroy MIDlet");
    notifyDestroyed();
  }//end destroyApp
  //----------------------------------------------------//

  //This is a member class
  class MyTimerTask extends TimerTask{
    long time;

    public void run(){
      System.out.println("Display an Alert");

      try{
        //Select among two image files on the basis of
        // whether the current time in seconds is odd
        // or even.
        time = new Date().getTime()/1000 - baseTime;

        //Note that the following file names are case
        // sensitive.
        if((time % 2) == 0){//Even value
          image = Image.createImage(
                                  "/Alert02/redball.PNG");
        }else{//Odd value
          image = Image.createImage(
                                 "/Alert02/blueball.PNG");
        }//end else

        //Create an Alert object of type CONFIRMATION.
        // This results in an audible alert that is three
        // chimes.
        Alert alert = new Alert("Alert Title",
                                "",
                                image,
                                AlertType.CONFIRMATION);

        //Cause the alert to display the time in seconds.
        alert.setString("Time in seconds:" + time);

        //Cause the alert to be visible for two seconds.
        alert.setTimeout(2000);
        
        //Create a Gauge that shows three repeating images
        // of Duke. The first has Duke doing a cartwheel.
        // The second has Duke in black, white, and red.
        // The third has Duke in gray, white, and red.
        Gauge gauge = new Gauge(
                              null,
                              false,
                              Gauge.INDEFINITE,
                              Gauge.INCREMENTAL_UPDATING);

        //Set the number of Gauge graphics to be
        // displayed. The limit for INCREMENTAL_UPDATING
        // is 3.
        gauge.setValue(++count % 3);
        //Attach the Gauge to the alert.
        alert.setIndicator(gauge);

        //Make the Alert the current Displayable object.
        Display.getDisplay(theMidlet).setCurrent(alert);
      }catch(Exception e){
        e.printStackTrace();
      }//end catch
    }//end run
  }//end class MyTimerTask
  //====================================================//
}//end class Alert02
//======================================================//

 


Copyright

Copyright 2008, Richard G. Baldwin. Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas. He is the author of Baldwin’s Programming Tutorials, which have gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.

In addition to his programming expertise, Richard has many years of practical experience in Digital Signal Processing (DSP). His first job after he earned his Bachelor’s degree was doing DSP in the Seismic Research Department of Texas Instruments. (TI is still a world leader in DSP.) In the following years, he applied his programming and DSP expertise to other interesting areas including sonar and underwater acoustics.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

Baldwin@DickBaldwin.com

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories