Package de.esoco.j2me.midlet

Source Code of de.esoco.j2me.midlet.BasicMidlet

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// J2ME-Lib source file
// Copyright (c) 2007 Elmar Sonnenschein / esoco GmbH
// Last Change: 27.06.2007 by eso
//
// J2ME-Lib is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License as published by the Free
// Software Foundation; either version 2.1 of the License, or (at your option)
// any later version.
//
// J2ME-Lib is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
// details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with J2ME-Lib; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA or use the contact
// information from the GNU website http://www.gnu.org
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
package de.esoco.j2me.midlet;

import de.esoco.j2me.J2meLib;
import de.esoco.j2me.resource.ResourceBundle;
import de.esoco.j2me.ui.ExecutableCommand;
import de.esoco.j2me.ui.MessageScreen;
import de.esoco.j2me.ui.ScreenManager;
import de.esoco.j2me.util.Executable;
import de.esoco.j2me.util.Log;
import de.esoco.j2me.util.ProcessingQueue;
import de.esoco.j2me.util.TextUtil;
import de.esoco.j2me.util.UserNotificationException;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.midlet.MIDlet;


/********************************************************************
* A MIDlet subclass that contains standard functionality for applications and
* some infrastructure methods for J2ME-Lib. It is recommended that applications
* that are based on J2ME-Lib derive their MIDlet class from this superclass to
* make sure that all J2ME-Lib resources are initialized correctly.
*
* <p>A subclass must implement at least the abstract methods {@link #init()}
* and {@link #runApp()}. It may also choose to override other methods like
* {@link #queryQuitApp()}, {@link #cleanup()}, or {@link #about()} if it wants
* to replace or modify the default functionality. The application startup code
* will be run from a seperate thread to prevent the main MIDlet thread from
* being blocked by long-running initialization code.</p>
*
* <p>This class is intended to be used only for single-instance MIDlets because
* some J2ME-Lib functions need to statically access MIDlet-related resources
* like the LCDUI Display. If a suite of multiple MIDlets needs to be built the
* additional MIDlets should either not be derived from BasicMidlet or it must
* be made sure that only one of the MIDlets runs at a time. Else an exeception
* will be thrown from the constructor of the second BasicMidlet that is
* created.</p>
*
* <p>The method {@link J2meLib#init()} will be invoked from the static
* initializer to initialize the library resources as early as possible.</p>
*
* @author eso
*/
public abstract class BasicMidlet extends MIDlet implements Runnable
{
  //~ Static fields/initializers ---------------------------------------------

  /** The default wait time for the cleanup method */
  protected static final int CLEANUP_WAIT_TIME = 5000;

  /** The currently active BasicMidlet instance */
  private static BasicMidlet theInstance;

  static
  {
    // initialize the library as early as possible
    J2meLib.init();
  }

  //~ Constructors -----------------------------------------------------------

  /***************************************
   * Creates a new instance and initializes the internal reference to the
   * current MIDlet instance. If {@link J2meLib#DEBUG} is TRUE and the an
   * MIDlet instance already exists an exception will be thrown because only
   * one BasicMidlet instance should exist at a time.
   */
  public BasicMidlet()
  {
    if (theInstance != null)
    {
      throw new IllegalStateException("Only one MIDlet instance allowed!");
    }
    theInstance = this;
  }

  //~ Methods ----------------------------------------------------------------

  /***************************************
   * Logs a fatal error that has been signalled by an exception and displays
   * an error message box. If the exception is an instance of {@link
   * UserNotificationException} the strings for the message box will be taken
   * from the exception.
   *
   * <p>After the message box has been dismissed the midlet will be destroyed,
   * whereupon the {@link #cleanup()} method will be invoked.</p>
   *
   * @param eCause The Exception that signalled the error
   */
  public static void fatalError(Exception eCause)
  {
    Log.fatal(eCause);

    MessageScreen.showError(getErrorTitle(eCause),
                getErrorMessage(eCause),
                new Executable()
                {
                  public void execute(Object rArg)
                  {
                    theInstance.destroyApp(true);
                  }
                });
  }

  /***************************************
   * Returns the Display object for the currently running midlet instance.
   *
   * @return The instance of the Display class
   */
  public static Display getDisplay()
  {
    return Display.getDisplay(theInstance);
  }

  /***************************************
   * Because this class is intended to be used as a singleton instance this
   * method returns the current BasicMidlet instance if it has been
   * initialized already.
   *
   * @return The current BasicMidlet instance
   */
  public static BasicMidlet getInstance()
  {
    return theInstance;
  }

  /***************************************
   * Returns the property string for a certain key. This method will first
   * check the MIDlet properties by invoking {@link #getAppProperty(String)}.
   * If that is not set it will also try to read a system property through the
   * method {@link System#getProperty(String)}.
   *
   * @param  sKey The property key
   *
   * @return The property value (may be NULL)
   */
  public static String getProperty(String sKey)
  {
    String sProperty = theInstance.getAppProperty(sKey);

    if (sProperty == null)
    {
      try
      {
        sProperty = System.getProperty(sKey);
      }
      catch (Exception e)
      {
        Log.error("Could not access property " + sKey, e);
      }
    }

    return sProperty;
  }

  /***************************************
   * A shortcut method to get a string from the current resource bundle.
   *
   * @see ResourceBundle#getString(String)
   */
  public static String getString(String sKey)
  {
    return ResourceBundle.getCurrent().getString(sKey);
  }

  /***************************************
   * Logs a recoverable error that has been signalled by an exception and
   * displays an error message box. If the exception is an instance of the
   * {@link UserNotificationException} class the strings for the message box
   * will be taken from the exception. After the message box has been
   * dismissed the midlet will continue to run.
   *
   * @param eCause The Exception that signalled the error
   */
  public static void recoverableError(final Exception eCause)
  {
    Log.error(eCause);

    MessageScreen.showError(getErrorTitle(eCause),
                getErrorMessage(eCause),
                null);
  }

  /***************************************
   * Displays a confirmation dialog to ask the user if he really wants to
   * quit. If so, the quit() method will be invoked. Subclasses can invoke
   * this method to prevent the user from unintentional exiting the
   * application. Or they may override it to modify the behavior.
   *
   * <p>This implementation uses the resource strings "JL_MtQuit" and
   * "JL_MsQuit" from the J2ME-Lib resource.</p>
   */
  public void queryQuitApp()
  {
    // the executable will only be invoked if the user selects OK
    MessageScreen.showConfirmation(getString("JL_MtQuit"),
                     getString("JL_MsQuit"),
                     true,
                     new Executable()
                     {
                       public void execute(Object rArg)
                       {
                         quit();
                       }
                     });
  }

  /***************************************
   * This method quits the midlet after lettting a subclass clean up all
   * allocated resources by invoking the abstract method {@link #cleanup()}.
   * Subclasses can either invoke this method directly or call the method
   * {@link #queryQuitApp()} which will first display a confirmation dialog
   * and only call this method if the user confirms the request.
   */
  public final void quit()
  {
    try
    {
      cleanup();
    }
    catch (Exception e)
    {
      Log.fatal(e);
    }

    theInstance = null;
    notifyDestroyed();
  }

  /***************************************
   * The {@link Runnable} interface method which will be invoked from the
   * midlet thread created in the {@link #startApp()} method. It first
   * initializes the midlet with the abstract {@link #init()} method, then
   * runs it by invoking {@link #runApp()}.
   */
  public final void run()
  {
    try
    {
      init();
      runApp();
    }
    catch (Exception e)
    {
      fatalError(e);
    }
  }

  /***************************************
   * Must be implemented by subclasses to initialize resources that are needed
   * by the application to run. If a critical error occurs that would prevent
   * the midlet from running correctly a UserNotificationException may be
   * thrown. The application startup will then be cancelled and the error
   * message from the exception will be displayed to the user.
   *
   * @throws UserNotificationException If initialization fails
   */
  protected abstract void init() throws UserNotificationException;

  /***************************************
   * Must be implemented by subclasses to execute the application's main code.
   * It will be invoked directly after the {@link #init()} method has
   * finished. In most cases the app will simply display it's main screen from
   * this method which will then start to dispatch events to the application.
   */
  protected abstract void runApp();

  /***************************************
   * Internal method to return the error message for a particular exception.
   * Checks whether the exception is an instance of UserNotificationException
   * and return it's message string or a default string that contains the
   * execption's message otherwise.
   *
   * @param  eCause The exception to check
   *
   * @return String The corresponding message string
   */
  protected static String getErrorMessage(Exception eCause)
  {
    if (eCause instanceof UserNotificationException)
    {
      return ((UserNotificationException) eCause).getMessage();
    }
    else
    {
      return TextUtil.formatMessage(getString("JL_MsCmdEx"),
                      new Object[] { eCause.getMessage() });
    }
  }

  /***************************************
   * Internal method to return the error title for a particular exception.
   * Checks whether the exception is an instance of UserNotificationException
   * and return it's title or a default string otherwise.
   *
   * @param  eCause The error causing exception to check
   *
   * @return String The corresponding title string
   */
  protected static String getErrorTitle(Exception eCause)
  {
    if (eCause instanceof UserNotificationException)
    {
      return ((UserNotificationException) eCause).getTitle();
    }
    else
    {
      return getString("JL_MtError");
    }
  }

  /***************************************
   * This method shows a simple information screen for this application. It is
   * invoked automatically by the default "About" command. It uses the
   * resource strings "MtAbout" and "MsAbout" (title and text) as well as the
   * image "ImAbout" that must therefore be defined in the application
   * resource. Subclasses may override this method to provide a different
   * about screen implementation.
   */
  protected void about()
  {
    MessageScreen.showMessageBox(getString("MtAbout"),
                   "ImAbout",
                   getString("MsAbout"),
                   null,
                   true,
                   null);
  }

  /***************************************
   * Performs a cleanup of application resources that will be invoked from the
   * {@link #quit()} method. Can be overridden by by subclasses to free any
   * resources allocated by them before the midlet will be terminated. The
   * implementation may throw a {@link UserNotificationException} to indicate
   * that the user needs to be notified of a critial error condition before
   * terminating the midlet completely.
   *
   * <p>This default implementation lets the thread from which it has been
   * invoked wait a certain amount of milliseconds as defined in the {@link
   * #CLEANUP_WAIT_TIME} constant before returning and therefore terminating
   * the MIDlet. The waiting is performed by invoking the method {@link
   * ProcessingQueue#finish(int)}. Unless they don't use the task scheduling
   * of the {@link ProcessingQueue} class subclasses that override this method
   * should always either invoke super or implement the waiting for the queue
   * finishing by themselves.</p>
   *
   * @throws UserNotificationException If a cleanup operation fails
   */
  protected void cleanup() throws UserNotificationException
  {
    ProcessingQueue.finish(CLEANUP_WAIT_TIME);
  }

  /***************************************
   * This method will be invoked by the J2ME runtime to terminate the midlet.
   * This implementation invokes the method {@link #quit()} which in turn will
   * invoke the abstract method {@link #cleanup()} and then terminate the
   * MiDlet. Subclasses should preferably implement the {@link #cleanup()}
   * method only instead of overriding this method.
   *
   * @param bUnconditional If TRUE, the MIDlet must terminate in any case
   *
   * @see   MIDlet#destroyApp(boolean)
   */
  protected void destroyApp(boolean bUnconditional)
  {
    quit();
  }

  /***************************************
   * Returns a new array that contains standard application commands that
   * should be displayed on all screens. The commands are instances of the
   * class {@link ExecutableCommand} and will automatically execute the
   * corresponding BasicMidlet methods. This default implementation returns
   * the commands "Quit" and "About" (from the current resource bundle) that
   * will invoke the methods {@link #queryQuitApp()} and {@link #about()},
   * respectively.
   *
   * <p>The commands are not used by BasicMidlet directly. A subclass must
   * explicitly add them to it's screens.</p>
   *
   * @return An new Command array
   */
  protected Command[] getStandardCommands()
  {
    Command aQuitCmd = new ExecutableCommand(getString("Quit") + "...",
                         Command.EXIT,
                         999)
    {
      public void execute(Object rArg)
      {
        queryQuitApp();
      }
    };

    Command aAboutCmd = new ExecutableCommand(getString("About") + "...",
                          Command.HELP,
                          99)
    {
      public void execute(Object rArg)
      {
        about();
      }
    };

    return new Command[] { aQuitCmd, aAboutCmd };
  }

  /***************************************
   * Returns the first visible screen of the MIDlet that will be displayed
   * before the separate application thread is run from {@link #startApp()}.
   * This is necessary to prevent the MIDlet from terminating before the
   * application code (which is run in the thread) becomes a chance to display
   * a screen.
   *
   * <p>The default implementation returns an empty form with the class name
   * as the title. At least for a MIDlet that performs a long-running
   * initialization it is recommended that it overrides this method to return
   * a screen that displays an information message and some kind of progress
   * indicator.</p>
   *
   * @return A displayable instance that is used as the MIDlet's first screen
   */
  protected Displayable getStartScreen()
  {
    return new Form(TextUtil.getTypeName(getClass()));
  }

  /***************************************
   * This method must be overridden by subclasses to free as many resources as
   * possible before entering the paused state. This default implementation
   * does nothing.
   *
   * @see MIDlet#pauseApp()
   */
  protected void pauseApp()
  {
  }

  /***************************************
   * This method will be invoked by the J2ME runtime to start the midlet. It
   * first displays the applications first LCDUI screen that is queried from
   * the abstract method {@link #getStartScreen()}. Then it starts a new
   * thread that invokes the {@link #run()} method, which in turn calls the
   * abstract methods {@link #init()} and {@link #runApp()}.
   *
   * <p>A separate thread is necessary to prevent that a long-running
   * initialization will block the main event handling thread of J2ME and
   * therefore prevent screen updates. The start screen must be displayed
   * before running the thread to prevent this MIDlet from terminating before
   * one of the application methods has a chance to display a screen.</p>
   *
   * <p>Because it runs in a separate thread the initialization or main code
   * of a subclass may run arbitrary long. But long-running processes should
   * give feedback to the user, e.g. by displaying a progress bar on the first
   * screen.</p>
   *
   * <p>The start screen will be displayed on the screen by means of the
   * {@link ScreenManager#show(Displayable)} method so that it will become the
   * first element on the screen stack. When the application code wants to
   * display a different screen it should either replace the start screen by
   * invoking {@link ScreenManager#setCurrent(Displayable)} or display the new
   * screen on top of it by calling {@link ScreenManager#show(Displayable)}.
   * What to choose depends on the purpose of the start screen: is it just to
   * be displayed during the MIDlet start or does it need to be redisplayed
   * later?</p>
   *
   * @see MIDlet#startApp()
   */
  protected void startApp()
  {
    ScreenManager.show(getStartScreen());
    new Thread(this).start();
  }
}
TOP

Related Classes of de.esoco.j2me.midlet.BasicMidlet

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.