This class contains the code specific to the MIDlet application model.
The class extends javax.microedition.midlet.MIDlet
and
implements its abstract methods startApp
, destroyApp
,
and pauseApp
.
The implementations are very simple. In startApp
the
eSWT UI thread is created and started. In destroyApp
the
UI thread is terminated. The implementation of pauseApp
is
left empty since it’s never called on S60.
private MovieBooking bookingApp; private Thread UIThread; protected void startApp() { // note that we use a different thread to // return from start quickly and at // the same time run the eSWT event loop if(UIThread == null) { bookingApp = new MovieBooking(this); UIThread = new Thread(bookingApp); UIThread.start(); } } public void destroyApp(boolean unconditional) { // Application must exit, // this stops the eSWT event loop bookingApp.exit(); // Wait for the UI thread to die. try { UIThread.join(); } catch(InterruptedException e) {} } protected void pauseApp() { }
The MovieBooking
class is the starting point of
the application. This class contains the eSWT event loop, creates the top-level
shell and controls the other classes that build the user interface. All the
state changes on the user interface are done through this class. Finally,
it takes care of handling the movie database.
For more information on methods offered by the Display
class
and its Constructor
, see the class Display
.
The Shell
class is used for constructing windows. For
more information on the methods provided by the Shell
class
and its constructors, see the class Shell
.
In the example, the user interface thread, the top-level Shell
,
the event loop, and other example specific logic is in the MovieBooking
class.
This class is the core of the example application. It implements the Display
and Shell
classes,
and controls the other classes that build the user interface. Note that the
entire code for the MovieBooking
class is given in this
section. Code that implements basic features or application specific logic
is not extensively commented. However, code that implements aspects of the Display
and Shell
classes
is more extensively commented.
Create the MovieBooking
class
file.
Import the
required classes and assign this class to the MovieBooking.ui
package.
package moviebooking.ui; import java.util.Vector; import javax.microedition.midlet.MIDlet; import moviebooking.moviedb.BookingTicket; import moviebooking.moviedb.Movie; import moviebooking.moviedb.MovieDB; import moviebooking.moviedb.MovieDBEvent; import moviebooking.moviedb.MovieDBListener; import moviebooking.moviedb.Showing; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.MessageBox;
public class MovieBooking implements Runnable, MovieDBListener { private MovieDB movieDB; private Shell mainShell; private SplashScreen splashScreen; private Display display; private ReservationForm reservationForm; private boolean running = false; private MIDlet midlet; public MovieBooking(MIDlet midlet) { this.midlet = midlet; } private MovieBooking() {}
Implement
the run
method, which controls the user interface thread,
the event loop, accesses the system resources, and the main Display
(which
is the parent of all other Display
s).
public void run() { running = true; // Note that Display has to be created here since that // determines the UI thread display = new Display();
In the code above, display = new Display()
constructs
a new instance of the Display
class. Note that this thread
must dispose of the instance before it can construct another one, although
it should not be done until the MIDlet is about to enter 'destroyed' state.
mainShell = new Shell(display, SWT.NONE);
In the code above, mainShell = new Shell(display, SWT.NONE)
creates
a top-level Shell
. Note that when using eSWT, it is strongly
recommended that you only have one top-level Shell
. Note
that SWT.NONE
is provided by class SWT
and
is an optional, appearance related constant, which may differ between implementations.
For more information on Shell
constructors, see the class Shell
.
// Create a layout for the shell GridLayout layout = new GridLayout(); // Zero the margins that are non-zero by default layout.horizontalSpacing = 0; layout.verticalSpacing = 0; layout.marginWidth = 0; layout.marginHeight = 0; mainShell.setLayout(layout); mainShell.setText("MovieBooking"); // open first this shell to be in the background mainShell.open();
In the code above, mainShell.open()
, which is a Shell
class
method, places mainShell
at the top of the drawing order,
marks it as visible, sets the focus, and asks to make the Shell
active.
// Creates the splash screen splashScreen = new SplashScreen(this, mainShell); // Create the MovieDB and request that it starts loading the data movieDB = new MovieDB(getClass().getResourceAsStream( "/res/movieDB.properties")); movieDB.setDBListener(this); // This starts loading the data on a separate thread movieDB.loadData();// eSWT event loop. Run until the main shell is disposed // eSWT event loop runs until the main shell is disposed // or the running flag is set to false. // The flag can get set due to a user initiated exit or due to // a system request to terminate the application. while (running && !mainShell.isDisposed()) { // Check if there are pending events if (!display.readAndDispatch()) { // otherwise sleep display.sleep(); } } // Dispose the display, this will also dispose mainShell display.dispose(); // Destroy the MIDlet midlet.notifyDestroyed(); }
The event loop is created and run until the main Shell
is
disposed. An eSWT event loop is executed in the run
method
and must be explicitly made visible. After the user interface has been built,
the event loop is started. The event loop continuously checks that the running
variable is true and that the top Shell
has not been
disposed off. If the event loop finds events waiting in the event queue, it
uses the readAndDispatch
method of the Display
class
to process them and sends them to their respective listeners. When there are
no pending events left, the sleep
method of the Display
class
is called to release the main processor.
Implement an asynchronous event.
// This is called when new data is loaded by MovieDB public void handleEvent(MovieDBEvent event) { // check anyway if the mainShell is still alive if (!mainShell.isDisposed()) { // this is a non-GUI thread. we need to do actions // in the GUI thread and so asyncExec is used SplashScreenRunnable runnable = new SplashScreenRunnable(event); display.asyncExec(runnable); } } // Exit method public void exit() { // close the DB and dispose the main shell movieDB.close(); running = false; display.wake(); }
In the code above, display.wake()
is a Display
class
method. If the user-interface thread is in sleep mode, this method can be
used to wake it up and start it running again.
// Show the reservation form when coming back from seatingShell void setSelectedSeats(int[][] selected) { reservationForm.setSelectedSeats(selected); } void cancelSeatSelection() { reservationForm.cancelSeatSelection(); } // Show the reservation form void startReserve(Movie movie) { reservationForm = new ReservationForm(MovieBooking.this, mainShell, movie); } // Shown when the reservation is being sent to movieDB void confirmReservation(String firstNameText, String familyNameText, Showing showing, int[][] selectedSeats) { // Ask MovieDB to request some seats dataLoaded(); int result = displayMessageBox(SWT.ICON_INFORMATION | SWT.YES | SWT.NO, "Booking", "Are you sure you want to reserve?"); if (result == SWT.YES) { // return to the movie list movieDB.sendBookingRequest(firstNameText, familyNameText, showing, selectedSeats); } } // Displays a message box int displayMessageBox(int type, String text, String message) { if (Thread.currentThread() == display.getThread()) { MessageBox box = new MessageBox(mainShell, type); box.setText(text); box.setMessage(message); return box.open(); } else { return -1; } }
In the code above, the eSWT API class MessageBox
is
used to provide the user some information on the ongoing reservation process.
// Shows the seating dialog void showSeatingDialog(Showing showing, int[][] selectedSeats, int ticketsCount) { new SeatingScreen(this, mainShell, showing, selectedSeats, ticketsCount); } // Advance the progress bar in SplashShell private void advanceSplashShellProgress(String message) { splashScreen.setLabel(message); splashScreen.advance(); } // Called if an error occured in movieDB private void movieDBDataError() { displayMessageBox(SWT.ICON_ERROR, "Information", "Error loading the database"); // Exit the swt loop mainShell.dispose(); }
In the code above, the eSWT API class MessageBox
is
used to warn the user about an error that has happened with the MovieDB
.
// Set the total amount of movies to display private void setMovieCount(int count) { splashScreen.setMoviesCount(count); } // Show a dialog box when a reservation has been confirmed private void displayReservationConfirmed(BookingTicket booking) { displayMessageBox(SWT.ICON_INFORMATION | SWT.OK, "Booking", "Reservation confirmed number " + booking.getCode() + " total: " + booking.getPrice()); } // Called when all the data in MovieDB is // available. We close splashShell and // start Movie Display private void dataLoaded() { if (splashScreen != null) { splashScreen.destroy(); splashScreen = null; } Vector movies = movieDB.getMovies(); if (movies.size() == 0) { // No movie found displayMessageBox(SWT.ICON_INFORMATION | SWT.OK, "Information", "No movies available"); // Exit the swt loop mainShell.dispose(); } else { // Start the new MovieDisplay screen new MoviesListScreen(MovieBooking.this, mainShell, movies); } } // Inner class used to call actions on the GUI // being initiated in MovieDB private class SplashScreenRunnable implements Runnable { private MovieDBEvent event; SplashScreenRunnable(MovieDBEvent event) { this.event = event; } public void run() { // Note that this is called from the GUI thread switch (event.getEvent()) { case MovieDBEvent.CONNECTING: // When connecting update the progress label // in the splash shell advanceSplashShellProgress("Loading from " + (String) event.getData()); break; case MovieDBEvent.DATA_ERROR: movieDBDataError(); break; case MovieDBEvent.MOVIES_COUNT: setMovieCount(((Integer) event.getData()).intValue() + 1); break; case MovieDBEvent.MOVIE_LOADED: // Called when data for a new movie is available advanceSplashShellProgress("Movie " + (((Integer) event.getData()).intValue() + 1)); break; case MovieDBEvent.DATA_LOADED: dataLoaded(); break; case MovieDBEvent.MOVIE_RESERVED: displayReservationConfirmed((BookingTicket) event.getData()); break; } } } }