PhotoAlbumpublic class PhotoAlbum extends MIDlet implements CommandListener, Runnable, ItemStateListenerThe PhotoAlbum MIDlet provides the commands and screens
that implement a simple photograph and animation album.
The images and animations to be displayed are configured
in the descriptor file with attributes.
Options are provided to to vary the speed of display
and the frame style. |
Fields Summary |
---|
private Command | aboutCommandThe Command object for the About command | private Command | exitCommandThe Command object for the Exit command | private Command | okCommandThe Command object for the Ok command | private Command | optionsCommandThe Command object for the Options command | private Command | backCommandThe Command object for the Back command | private Command | cancelCommandThe Command object for the Cancel command | private Form | progressFormThe Form object for the Progress form | private Gauge | progressGaugeThe Gauge object for the Progress gauge | private Form | optionsFormThe Form object for the Options command | private ChoiceGroup | borderChoiceSet of choices for the border styles | private ChoiceGroup | speedChoiceSet of choices for the speeds | private Display | displayThe current display for this MIDlet | private PhotoFrame | frameThe PhotoFrame that displays images | private Alert | alertThe Alert for messages | private Vector | imageNamesContains Strings with the image names | private List | imageListList of Image titles for user to select | private String | imageNameName of current image, may be null | private Thread | threadCurrent thread loading images, may be null | private final String | optionsNameName of persistent storage | private RecordStore | optionsStorePersistent storage for options |
Constructors Summary |
---|
public PhotoAlbum()Construct a new PhotoAlbum MIDlet and initialize the base options
and main PhotoFrame to be used when the MIDlet is started.
display = Display.getDisplay(this);
exitCommand = new Command("Exit", Command.EXIT, 1);
optionsCommand = new Command("Options", Command.SCREEN, 1);
okCommand = new Command("Ok", Command.OK, 3);
backCommand = new Command("Back", Command.BACK, 3);
cancelCommand = new Command("Cancel", Command.CANCEL, 1);
aboutCommand = new Command("About", Command.HELP, 30);
frame = new PhotoFrame();
frame.setStyle(2);
frame.setSpeed(2);
frame.addCommand(optionsCommand);
frame.addCommand(backCommand);
frame.setCommandListener(this);
alert = new Alert("Warning");
setupImageList();
|
Methods Summary |
---|
void | closeOptions()Close the options store.
if (optionsStore != null) {
try {
optionsStore.closeRecordStore();
optionsStore = null;
} catch (RecordStoreException ex) {
alert.setString("Could not close options storage");
display.setCurrent(alert);
}
}
| public void | commandAction(Command c, Displayable s)Respond to commands. Commands are added to each screen as
they are created. Each screen uses the PhotoAlbum MIDlet as the
CommandListener. All commands are handled here:
- Select on Image List - display the progress form and start the thread
to read in the images.
- Options - display the options form.
- Ok on the Options form - returns to the PhotoFrame.
- Back - display the Image List, deactivating the PhotoFrame.
- Cancel - display the image List and stop the thread loading images.
- Exit - images are released and notification is given that the MIDlet
has exited.
if (c == exitCommand) {
// Cleanup and notify that the MIDlet has exited
destroyApp(false);
notifyDestroyed();
} else if (c == optionsCommand) {
// Display the options form
display.setCurrent(genOptions());
} else if (c == okCommand && s == optionsForm) {
// Return to the PhotoFrame, the option values have already
// been saved by the item state listener
display.setCurrent(frame);
} else if (c == List.SELECT_COMMAND) {
// Display the progress screen and
// start the thread to read the images
int i = imageList.getSelectedIndex();
imageName = (String)imageNames.elementAt(i);
display.setCurrent(genProgress(imageList.getString(i)));
thread = new Thread(this);
thread.start();
} else if (c == backCommand) {
// Display the list of images.
display.setCurrent(imageList);
} else if (c == cancelCommand) {
// Signal thread to stop and put an alert.
thread = null;
alert.setString("Loading images cancelled.");
display.setCurrent(alert, imageList);
} else if (c == aboutCommand) {
About.showAbout(display);
}
| private Image | createImage(java.lang.String name)Fetch the image. If the name begins with "http:"
fetch it with connector.open and http.
If it starts with "/" then load it from the
resource file.
if (name.startsWith("/")) {
// Load as a resource with Image.createImage
return Image.createImage(name);
} else if (name.startsWith("http:")) {
// Load from a ContentConnection
HttpConnection c = null;
DataInputStream is = null;
try {
c = (HttpConnection)Connector.open(name);
int status = c.getResponseCode();
if (status != 200) {
throw new IOException("HTTP Response Code = " + status);
}
int len = (int)c.getLength();
String type = c.getType();
if (!type.equals("image/png")) {
throw new IOException("Expecting image, received " + type);
}
if (len > 0) {
is = c.openDataInputStream();
byte[] data = new byte[len];
is.readFully(data);
return Image.createImage(data, 0, len);
} else {
throw new IOException("Content length is missing");
}
} finally {
if (is != null)
is.close();
if (c != null)
c.close();
}
} else {
throw new IOException("Unsupported media");
}
| protected void | destroyApp(boolean unconditional)Destroy must cleanup everything not handled by the garbage collector.
In this case there is nothing to cleanup.
Save the options for the next restart.
saveOptions();
frame.reset(); // Discard images cached in the frame.
saveOptions();
closeOptions();
| private Screen | genOptions()Generate the options form with speed and style choices.
Speed choices are stop, slow, medium, and fast.
Style choices for borders are none, plain, fancy.
if (optionsForm == null) {
optionsForm = new Form("Options");
optionsForm.addCommand(okCommand);
optionsForm.setCommandListener(this);
optionsForm.setItemStateListener(this);
speedChoice = new ChoiceGroup("Speed", Choice.EXCLUSIVE);
speedChoice.append("Stop", null);
speedChoice.append("Slow", null);
speedChoice.append("Medium", null);
speedChoice.append("Fast", null);
speedChoice.append("Unlimited", null);
speedChoice.setSelectedIndex(frame.getSpeed(), true);
optionsForm.append(speedChoice);
borderChoice = new ChoiceGroup("Borders", Choice.EXCLUSIVE);
borderChoice.append("None", null);
borderChoice.append("Plain", null);
borderChoice.append("Fancy", null);
borderChoice.setSelectedIndex(frame.getStyle(), true);
optionsForm.append(borderChoice);
}
return optionsForm;
| private Screen | genProgress(java.lang.String name)Generate the options form with image title and progress gauge.
if (progressForm == null) {
progressForm = new Form(name);
progressForm.addCommand(cancelCommand);
progressForm.setCommandListener(this);
progressGauge =
new javax.microedition.lcdui.Gauge("Loading images...",
false, 9, 0);
progressForm.append(progressGauge);
} else {
progressGauge.setValue(0);
progressForm.setTitle(name);
}
return progressForm;
| public void | itemStateChanged(Item item)Listener for changes to options.
The new values are set in the PhotoFrame.
if (item == borderChoice) {
frame.setStyle(borderChoice.getSelectedIndex());
} else if (item == speedChoice) {
frame.setSpeed(speedChoice.getSelectedIndex());
}
| void | openOptions()Open the store that holds the saved options.
If an error occurs, put up an Alert.
try {
optionsStore = RecordStore.openRecordStore(optionsName, true);
} catch (RecordStoreException ex) {
alert.setString("Could not access options storage");
display.setCurrent(alert);
optionsStore = null;
}
| protected void | pauseApp()Pause is used to release the memory used by Image.
When restarted the images will be re-created.
Save the options for the next restart.
saveOptions();
frame.reset(); // Discard images cached in the frame.
| void | restoreOptions()Restore the options from persistent storage.
The options are read from record 1 and set in
the frame and if the optionsForm has been created
in the respective ChoiceGroups.
if (optionsStore != null) {
try {
byte[] options = optionsStore.getRecord(1);
if (options.length == 2) {
frame.setStyle(options[0]);
frame.setSpeed(options[1]);
if (optionsForm != null) {
borderChoice.setSelectedIndex(options[0], true);
speedChoice.setSelectedIndex(options[1], true);
}
return; // Return all set
}
} catch (RecordStoreException ex) {
// Ignore, use normal defaults
}
}
| public void | run()The Run method is used to load the images.
A form is used to report the progress of loading images
and when the loading is complete they are displayed.
Any errors that occur are reported using an Alert.
Images previously loaded into the PhotoFrame are discarded
before loading.
Load images from resource files using Image.createImage .
Images may be in resource files or accessed using http:
The first image is loaded to determine whether it is a
single image or a sequence of images and to make sure it exists.
If the name given is the complete name of the image then
it is a singleton.
Otherwise it is assumed to be a sequence of images
with the name as a prefix. Sequence numbers (n) are
0, 1, 2, 3, .... The full resource name is the concatenation
of name + n + ".png".
If an OutOfMemoryError occurs the sequence of images is truncated
and an alert is used to inform the user. The images loaded are
displayed.
Thread mythread = Thread.currentThread();
Vector images = new Vector(5);
/* Free images and resources used by current frame. */
frame.reset();
try { // Catch OutOfMemory Errors
try {
if (imageName.startsWith("testchart:")) {
TestChart t = new TestChart(frame.getWidth(),
frame.getHeight());
images = t.generateImages();
} else {
// Try the name supplied for the single image case.
images.addElement(createImage(imageName));
}
} catch (IOException ex) {
try {
int namelen = imageName.length();
StringBuffer buf = new StringBuffer(namelen + 8);
buf.append(imageName);
Runtime rt = Runtime.getRuntime();
// Try for a sequence of images.
for (int i = 0; ; i++) {
progressGauge.setValue(i % 10);
// If cancelled, discard images and return immediately
if (thread != mythread) {
break;
}
// locate the next in the series of images.
buf.setLength(namelen);
buf.append(i);
buf.append(".png");
String name = buf.toString();
images.addElement(createImage(name));
}
} catch (IOException io_ex) {
}
} catch (SecurityException se_ex) {
// no-retry, just put up the alert
}
// If cancelled, discard images and return immediately
if (thread != mythread) {
return;
}
// If any images, setup the images and display them.
if (images.size() > 0) {
frame.setImages(images);
display.setCurrent(frame);
} else {
// Put up an alert saying image cannot be loaded
alert.setString("Images could not be loaded.");
display.setCurrent(alert, imageList);
}
} catch (OutOfMemoryError err) {
int size = images.size();
if (size > 0) {
images.setSize(size-1);
}
// If cancelled, discard images and return immediately
if (thread != mythread) {
return;
}
alert.setString("Not enough memory for all images.");
// If no images are loaded, Alert and return to the list
// Othersize, Alert and display the ones that were loaded.
if (images.size() <= 0) {
display.setCurrent(alert, imageList);
} else {
frame.setImages(images);
display.setCurrent(alert, frame);
}
}
| void | saveOptions()Save the options to persistent storage.
The options are retrieved ChoiceGroups and stored
in Record 1 of the store which is reserved for it.
The two options are stored in bytes 0 and 1 of the record.
if (optionsStore != null) {
byte[] options = new byte[2];
options[0] = (byte)frame.getStyle();
options[1] = (byte)frame.getSpeed();
try {
optionsStore.setRecord(1, options, 0, options.length);
} catch (InvalidRecordIDException ridex) {
// Record 1 did not exist, create a new record (Should be 1)
try {
int rec = optionsStore.addRecord(options,
0, options.length);
} catch (RecordStoreException ex) {
alert.setString("Could not add options record");
display.setCurrent(alert);
}
} catch (RecordStoreException ex) {
alert.setString("Could not save options");
display.setCurrent(alert);
}
}
| private void | setupImageList()Check the attributes in the descriptor that identify
images and titles and initialize the lists of imageNames
and imageList.
The attributes are named "PhotoTitle-n" and "PhotoImage-n".
The value "n" must start at "1" and increment by 1.
imageNames = new Vector();
imageList = new List("Images", List.IMPLICIT);
imageList.addCommand(exitCommand);
imageList.addCommand(aboutCommand);
imageList.setCommandListener(this);
for (int n = 1; n < 100; n++) {
String nthImage = "PhotoImage-"+ n;
String image = getAppProperty(nthImage);
if (image == null || image.length() == 0)
break;
String nthTitle = "PhotoTitle-" + n;
String title = getAppProperty(nthTitle);
if (title == null || title.length() == 0)
title = image;
imageNames.addElement(image);
imageList.append(title, null);
}
imageNames.addElement("testchart:");
imageList.append("Test Chart", null);
| protected void | startApp()Start up the Hello MIDlet by setting the PhotoFrame
and loading the initial images.
if (imageList.size() > 0) {
display.setCurrent(imageList);
openOptions();
restoreOptions();
} else {
alert.setString("No images configured.");
display.setCurrent(alert, imageList);
}
|
|