Show MIDui.java syntax highlighted
/*
* Copyright (c) 2004
* Helsinki Institute of Physics
* see LICENSE file for details
*
* MIDui.java
* Created on Aug 19, 2003
*/
package fi.hip.gb.client;
import java.util.Stack;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.StringItem;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
import javax.microedition.rms.RecordStoreException;
import fi.hip.gb.bluetooth.BTService;
import fi.hip.gb.midlet.core.LiteDescription;
import fi.hip.gb.midlet.core.LiteResult;
import fi.hip.gb.midlet.core.LiteStorage;
import fi.hip.gb.midlet.net.BinaryClient;
/**
* J2ME client MIDlet. Uses {@link BinaryClient } to talk with
* the GBAgent server (<code>fi.hip.gb.server.J2meService</code>).
* Currently can be used to send new jobs, receive the status messages
* and finally the results.
*
* @author Juho Karppinen
*/
public abstract class MIDui extends MIDlet {
/** our display */
private static Display display;
/** singleton instance of the main midlet */
public static MIDui instance;
/** flood the screen with debug messages */
public static boolean debug;
/** configuration storage and user interface for them */
private static Configs configs;
/** talks with GBAgent server using GPRS connection */
private static BinaryClient client;
/** bluetooth network and server */
private BluetoothBrowser btBrowser;
/** login routines and screen for typing username and password */
private static Logging login;
/** store and show the list of jobs */
protected JobsForm jobs;
/** main screen which shows the menu for different actions */
private static Form mainForm;
/** shows status of the job on the screen */
private StatusForm statusForm;
/** thumbnails of saved pictures */
private Thumbnails thumbnailForm;
/** list of available servers */
protected String[] serverList = new String[0];
/**
* Recently used <code>Displayable</code> objects, current is almost
* on the top */
public static Stack history = new Stack();
/**
* Constructs the Midlet
*/
public MIDui() {
MIDui.instance = this;
}
/**
* Network operation is started. Inform this to the subclass.
*
*/
protected abstract void operationStarted();
/**
* Network operation in progress was completed. Informs this
* to the subclass.
*/
protected abstract void operationCompleted();
/**
* Set the main screen
*
* @param mainApplicationForm the main form which will be shown to the user
* after midlet has started
*/
protected void setMainForm(Form mainApplicationForm) {
MIDui.mainForm = mainApplicationForm;
}
/**
* Get the main screen
*
* @return mainApplicationForm the main form which will be shown to the user
* after midlet has started
*/
protected Form getMainForm() {
return MIDui.mainForm;
}
protected void startApp() throws MIDletStateChangeException {
display = Display.getDisplay(this);
if(this.getAppProperty("Debug") != null) {
debug = Integer.parseInt(this.getAppProperty("Debug")) != 0;
}
System.out.println("Starting");
// read the configurations from memory
configs = new Configs();
// initialize communication to the GBAgent server
client = new BinaryClient(configs.getServerURL(), configs.getMyProxyServer());
login = new Logging();
// authentication info goes here
mainForm.append(new StringItem("", null));
// stores the list of jobs
jobs = new JobsForm();
mainForm.append(jobs.getChoiceGroup());
// show configurations if program started first time
if (configs.loadFailed()) {
showConfigs();
} else {
showMainForm();
}
btBrowser = new BluetoothBrowser(configs.getBluetoothServer());
System.out.println(btBrowser);
}
protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {
mainForm.set(0, new StringItem("Closing application...", ""));
// attemp to close the server connection before existing...
BTService.getInstance().stopServer();
ProgressForm.cancel();
}
protected void pauseApp() {
}
/**
* Gets the display of the MIDlet
* @return Display object
*/
public static Display getDisplay() {
return display;
}
/**
* Sets the new displayable to the screen. If screen is not
* progress form or already on the screen,
* it will be pushed into history stack.
*
* @param d displayable object to be shown
*/
public static void next(Displayable d) {
// progress form is not inserted into the history, and no duplicates
if(display.getCurrent() == null
|| (display.getCurrent() != null
&& ! d.getClass().getName().equals("fi.hip.gb.client.ProgressForm")
&& (history.empty() || current().equals(d) == false))) {
System.out.println("next screen " + d.getClass().getName());
history.push(d);
}
display.setCurrent(d);
}
/**
* Switch back to old form. If form history is empty, go to
* main screen.
* @param show switch to previous form
* @return instance of previous form
*/
public static Displayable back(boolean show) {
System.out.println("back to " + current().getClass().getName());
// let main screen be on the bottom
if(history.size() > 1) {
history.pop();
}
if(show) {
if(current() == MIDui.mainForm)
showMainForm();
else
display.setCurrent(current());
}
return current();
}
/**
* Gets last known real display from the history. This doesn't
* return any progress forms.
* @return current display
*/
public static Displayable current() {
return (Displayable)history.peek();
}
/**
* Shows the main form. The history of screens is cleared.
*/
public static void showMainForm() {
String time = client.getExpiration();
if(time == null) {
mainForm.set(0, new StringItem("Not logged in", ""));
} else {
StringItem message;
try {
// throws exception if not logged in
client.isLogged();
message = new StringItem("Session is valid", "Time left " + time);
} catch(Exception e) {
message = new StringItem("Session has expired", time + " ago");
}
mainForm.set(0, message);
}
// clear the history
history.removeAllElements();
next(mainForm);
}
/**
* Gets the connection to the GBAgent server
* @return instance of the client
*/
public static BinaryClient getClient() {
return client;
}
/**
* Gets the configurations and config form
* @return instance of the configuration
*/
public static Configs getConfig() {
return configs;
}
/**
* Gets the login name for the user
* @return login name
*/
public static String getLoginName() {
return login.getLoginName();
}
/**
* Shows login screen with message
* @param message to be passed to the user, can be for example an
* cause of failed login
*/
protected static void showLogin(String message) {
login.set(0, new StringItem("info:", message));
next(login);
}
/**
* Shows configuration form
*/
protected static void showConfigs() {
next(configs);
}
/**
* Shows the bluetooth network form
*/
protected static void showBluetooth() {
next(instance.btBrowser);
}
/**
* Opens the bluetooth network and allowes
* the sending of items into some or all
* endpoints.
*
* @param item item to be sended
*/
protected void sendItem(LiteStorage item) {
btBrowser.addToQueue(item);
showBluetooth();
}
/**
* Shows the camera viewfinder on screen for taking
* new pictures.
*/
protected static void showCamera() {
next(new CameraCanvas());
}
/**
* Shows saved pictures as thumbnails
*/
protected static void showThumbnails() {
if(instance.thumbnailForm == null)
instance.thumbnailForm = new Thumbnails();
next(instance.thumbnailForm);
}
/**
* Shows the results stored on the memory
*/
protected static void showSavedResults() {
next(new Results());
}
/**
* Saves the job status with results into the memory.
* Make sure that all result files are already downloaded.
* @param storage container to be saved
*/
protected static void saveJob(LiteStorage storage) {
next(new ProgressForm("Saving result into memory"));
try {
storage.store();
showAlert("Result saved",
storage.getDescription().getJobname() + " saved succesfully",
AlertType.INFO);
} catch(Exception rse) {
showException("Cannot save result " + storage.getDescription().getJobname(), rse);
}
}
/**
* Saves the result into the memory
* @param result result to be saved
*/
protected static void saveResult(LiteResult result) {
LiteStorage storage = new LiteStorage(new Long(-1), result);
storage.getDescription().setJobname(result.getName());
saveJob(storage);
}
/**
* Saves the picture to the memory
* @param imageContainer image to be saved with all metadata included
*/
protected static void savePicture(LiteResult imageContainer) {
next( new ProgressForm("Saving picture into memory"));
if(instance.thumbnailForm == null)
instance.thumbnailForm = new Thumbnails();
try {
instance.thumbnailForm.addImage(imageContainer);
showAlert("Image saved",
imageContainer.getName() + " saved succesfully",
AlertType.INFO);
} catch(Exception rse) {
showException("Cannot save picture " + imageContainer.getName(), rse);
}
}
/**
* Shows the image on the screen
* @param image image to be shown
*/
protected static void showPicture(LiteResult image) {
next(new PictureCanvas(image));
}
/**
* Deletes the image from the memory
* @param image image data
* @param moveBack move back in the history
*/
protected static void deletePicture(LiteResult image, boolean moveBack) {
try {
if(moveBack) {
back(false);
}
instance.thumbnailForm.deleteImage(image);
showAlert("Image deleted",
image.getName() + " deleted succesfully",
AlertType.INFO);
} catch (RecordStoreException rse) {
showException("Cannot delete image " + image.getName(), rse);
}
}
/**
* Shows dispatch options for creating a new job.
* Form returns to the main form
*
* @param jobID append the job of this ID, or in case of null creates a new job.
* @param attachmentImage images to be attached into description,
* or null if no attachments
*/
protected static void showDispatch(Long jobID, LiteResult attachmentImage) {
DispatchForm df = null;
if(attachmentImage != null)
df = new DispatchForm(attachmentImage, jobID, MIDui.instance.serverList);
else
df = new DispatchForm(jobID, MIDui.instance.serverList);
next(df);
}
/**
* Dispatch the job to the server.
* After dispathing is done, the job id is added to the list of jobs.
* @param wds description to be sent to the server.
*/
protected static void dispatchJob(final LiteDescription wds) {
MIDui.next(new ProgressForm("Sending job"));
new Thread( new Runnable() {
public void run() {
MIDui.instance.operationStarted();
try {
DispatchForm.storeWds(wds);
System.out.println("dispatch: " + wds.toString());
MIDui.getClient().dispatch(wds);
System.out.println("Sent new ID " + wds.getJobID());
Long jobID = wds.getJobID();
if(wds.getParentID() != null) {
jobID = wds.getParentID();
}
MIDui.instance.jobs.addJob(jobID, wds.getJobname());
MIDui.downloadStatus(jobID, wds, true);
} catch (Exception e) {
MIDui.showException(
"Cannot send job to server "
+ MIDui.getClient().getServiceURL(),
e);
}
MIDui.instance.operationCompleted();
}
}).start();
}
/**
* Synchronize state with the server. Updates the list of jobs running on the server
* and dowloads list of knowns services if requested.
* <p>
* This is executed in a new background thread. The user should
* already be logged in beforecalling this method.
*
* @param filterString filter jobs by ClassName, use asterix to get all
*/
public void sync(final String filterString) {
next(new ProgressForm("Synchronizing"));
new Thread( new Runnable() {
public void run() {
operationStarted();
jobs.clearJobs("[sync in progress]");
try {
// update the list of jobs
String[] jobNames = client.getJoblist(true, filterString);
jobs.clearJobs();
if(jobNames.length > 0) {
for(int i=0; i < jobNames.length; i++) {
System.out.println("found " + jobNames[i]);
jobs.addJob(jobNames[i]);
}
}
// synchronize servers
MIDui.instance.serverList = MIDui.getClient().listServices("");
next(current());
} catch (Exception e) {
jobs.clearJobs("[sync failed]");
showException("Error while listing jobs", e);
}
operationCompleted();
}
}).start();
}
/**
* Shows status of the job inside <code>StatusForm</code>.
*
* @param storage container for the job data
*/
protected static void showStatus(LiteStorage storage) {
next(new StatusForm(storage));
}
/**
* Gets status for the job and show it inside <code>StatusForm</code>.
* Status is updated as long as job is finished or canceled.
*
* @param jobID id of the requested job
* @param description description for the job, if null it will be downloaded from the server
* @param recreate recreate existing <code>StatusForm</code> or use existing
*/
protected static void downloadStatus(final Long jobID,
final LiteDescription description,
final boolean recreate) {
next(new ProgressForm("Transfering status"));
if(recreate) {
// clear the existing form
instance.statusForm = null;
}
int refreshRate = configs.getUpdateInterval();
TimerTask timer = new TimerTask() {
private LiteStorage storage = new LiteStorage(jobID);
// set special tab
private int oldTransfer = -1;
public void run() {
try {
// runnning the loop first time?
if(oldTransfer == -1) {
if(description == null) {
// download the description from the server if not given as parameter
storage.setDescription(MIDui.client.getDescription(jobID));
} else {
storage.setDescription(description);
}
oldTransfer = 0;
}
// update the status
this.storage.setStatus(MIDui.client.getStatus(jobID));
// update the list of results if the size has changed
if(this.storage.getStatus().getTransferModel()[1] != this.oldTransfer) {
this.storage.addResults(client.getResult(jobID, new String[0], new Boolean(false)));
this.oldTransfer = this.storage.getStatus().getTransferModel()[1];
}
if(instance.statusForm == null) {
instance.statusForm = new StatusForm(storage);
// if job is already finished, cancel the thread
if(storage.getStatus().isFinished() || storage.getStatus().isWaiting()) {
//System.out.println("----------finished-------------");
cancel();
}
} else if(instance.statusForm.update(storage)){
//System.out.println("CANCEL-------------");
cancel();
}
next(instance.statusForm);
} catch (Exception e) {
showException("Error receiving status", e);
cancel();
}
}
};
// either download status messages only once, or frequently
if(recreate && refreshRate > 0) {
new Timer().schedule(timer, refreshRate, refreshRate);
} else {
new Thread(timer).start();
}
}
/**
* Shows the result, currently supported formats are
* audio, images and text results.
* @param result result container with all metadata and payload
* @param description description of the job which owns the result
*/
public static void showResult(LiteResult result, LiteDescription description) {
if(result.getType() == LiteResult.TEXT_TYPE) {
next(new TextForm(result));
} else if(result.getType() == LiteResult.IMAGE_TYPE) {
next(new GraphicsCanvas(result, description));
} else if(result.getType() == LiteResult.AUDIO_TYPE) {
next(new MediaPlayer(result));
}
}
/**
* Downloads given results from the server.
*
* @param query an array of result names to be retrieved,
* all available results will be downloaded if array is empty
* @param operation after result is downloaded:
* <ul>
* <li>0=show the first result
* <li>1= save results to the disk
* <li>2=no operation, just download the files and update UI
* @param storage storage which ID is used to download results. Results are
* also put there and work description is also used when showing results.
* @param includePayload download payload data from the server or only the status
* @param form which will be updated when results are downloaded
*/
protected static void downloadResult(final String[] query,
final int operation,
final LiteStorage storage,
final Boolean includePayload,
final StatusForm form) {
next(new ProgressForm("Transfering results"));
new Thread(new Runnable() {
public void run() {
MIDui.instance.operationStarted();
try {
Long jobID = storage.getDescription().getJobID();
if(storage.getDescription().getParentID() != null)
jobID = storage.getDescription().getParentID();
Vector results = client.getResult(jobID, query, includePayload);
if(results.size() == 0) {
throw new Exception("No results found.");
}
storage.addResults(results);
form.update(storage);
if(operation == 0) {
// show the first element
LiteResult res = (LiteResult) results.elementAt(0);
System.out.println("Returned result type of " + res.getType()
+ " for query of " + query.length + " items");
showResult(res, storage.getDescription());
} else if(operation == 1) {
saveJob(storage);
} else {
next(MIDui.current());
}
} catch (Exception e) {
showException("Error downloading results: " + e.getMessage(), e);
}
MIDui.instance.operationCompleted();
}
}).start();
}
/**
* Aborts and removes the job. Shows to job list after deletion
* @param id id of the job to be aborted and/or removed
* @param remove should job be removed permanently
* @param moveBack move back in the history
*/
protected static void abortJob(final Long id, final boolean remove, final boolean moveBack) {
if(remove == false) {
next(new ProgressForm("Aborting job"));
} else {
next(new ProgressForm("Removing job"));
}
if(moveBack) {
back(false);
}
new Thread(new Runnable() {
public void run() {
MIDui.instance.operationStarted();
String message = null;
try {
boolean succeeded = client.abortJob(id, remove);
if(remove) {
MIDui.instance.jobs.removeJob();
message = (succeeded) ? "Job was successfully removed" : "Could not remove the job";
} else {
message = remove ? "Job was succesfully aborted" : "Could not abort the job";
}
showAlert("Succeeded", message, AlertType.INFO);
} catch (Exception e) {
showException(message, e);
}
MIDui.instance.operationCompleted();
}
}).start();
}
/**
* Prints debug message into init form. Should be removed
* when GBAgent hits the market.
*
* @param message message to be printed
*/
public static void log(String message) {
mainForm.append("\n" + message);
}
/**
* Shows an exception message in an alert box
* @param title cause of the error
* @param e exception to be shown
*/
public static void showException(String title, Exception e) {
String msg = "Exception: " + e.getClass().getName() + " " + e.getMessage();
e.printStackTrace();
if(MIDui.debug) {
log(title + ":");
log(msg);
}
showAlert(title, msg, AlertType.ERROR);
}
/**
* Shows an alert to the user. Alert is modal, so user
* have to dismiss it before normal operation can be continued
* @param title title for the error screen
* @param message error message
* @param type type of the message
*/
public static void showAlert(String title,
String message,
AlertType type) {
Alert alert = new Alert(title, message, null, type);
alert.setTimeout(Alert.FOREVER);
display.setCurrent(alert, current());
}
}
See more files for this project here