For implementing the UI, the MIDlet contains the following classes, each of which creates a separate screen:
The CameraScreen
class creates the form for capturing an image. The actual capture
operation runs in its own thread. Once the image has been captured,
the MIDlet calls the imageCaptured
method of the
main class.
To implement the CameraScreen
class:
Create the CameraScreen.java
class file.
Import the required classes.
import java.io.IOException; import javax.microedition.lcdui.Canvas; import javax.microedition.lcdui.Command; import javax.microedition.lcdui.CommandListener; import javax.microedition.lcdui.Displayable; import javax.microedition.lcdui.Graphics; import javax.microedition.media.Manager; import javax.microedition.media.MediaException; import javax.microedition.media.Player; import javax.microedition.media.control.VideoControl;
For the Series 40 full touch version of the MIDlet, the required classes are as follows:
import java.io.IOException; import javax.microedition.lcdui.Canvas; import javax.microedition.lcdui.Command; import javax.microedition.lcdui.CommandListener; import javax.microedition.lcdui.Displayable; import javax.microedition.lcdui.Graphics; import javax.microedition.media.Manager; import javax.microedition.media.MediaException; import javax.microedition.media.Player; import javax.microedition.media.control.VideoControl; import com.nokia.mid.ui.VirtualKeyboard; import com.nokia.mid.ui.gestures.GestureEvent; import com.nokia.mid.ui.gestures.GestureInteractiveZone; import com.nokia.mid.ui.gestures.GestureListener; import com.nokia.mid.ui.gestures.GestureRegistrationManager; import com.nokia.mid.ui.orientation.Orientation;
Set CameraScreen
to extend Canvas
and implement CommandListener
.
class CameraScreen extends Canvas implements CommandListener {
The Series 40 full touch version of the MIDlet also implements GestureListener
.
class CameraScreen extends Canvas implements CommandListener, GestureListener {
Create the required
variables, and create the CameraScreen
class constructor.
private final MMSMIDlet midlet; private final Command exitCommand; private Player player = null; private Command captureCommand = null; private VideoControl videoControl = null; CameraScreen(MMSMIDlet midlet) { this.midlet = midlet; // Builds the user interface exitCommand = new Command("Exit", Command.EXIT, 1); addCommand(exitCommand); captureCommand = new Command("Capture", Command.SCREEN, 1); addCommand(captureCommand); setCommandListener(this); }
For the Series 40 full touch version of the MIDlet,
create the CameraScreen
class constructor as follows:
CameraScreen(MMSMIDlet midlet) { this.midlet = midlet; /** * To determine whether a Series 40 device is a full touch device */ if (System.getProperty("com.nokia.keyboard.type").equals("None")) { /** * hide the open keypad command from the Options menu */ VirtualKeyboard.hideOpenKeypadCommand(true); } // Builds the user interface exitCommand = new Command("Exit", Command.EXIT, 1); addCommand(exitCommand); captureCommand = new Command("Capture", Command.SCREEN, 1); addCommand(captureCommand); setCommandListener(this); Orientation.setAppOrientation(Orientation.ORIENTATION_PORTRAIT); initializeGesture(); }
Implement the paint
method for drawing on the canvas. The method paints
the canvas background black.
public void paint(Graphics g) { // black background g.setColor(0x0000000); g.fillRect(0, 0, getWidth(), getHeight()); }
Implement the commandAction
method for detecting command invocations.
public void commandAction(Command c, Displayable d) { if (c == exitCommand) { midlet.exitApplication(); } else if (c == captureCommand) { captureImage(); } }
Create a method for detecting key presses.
public void keyPressed(int keyCode) { if (getGameAction(keyCode) == FIRE) { captureImage(); } }
For the Series 40 full touch version of the MIDlet, create also a method for checking the encoding support.
private boolean checkEncodingSupport() { String encodings = System.getProperty("video.snapshot.encodings"); return (encodings != null) && (encodings.indexOf("png") != -1) && (encodings.indexOf("image/bmp") != -1); }
Create a method for building and starting the video player.
synchronized void start() { try { // Newer phones need to be initialized to image mode try { player = Manager.createPlayer("capture://image"); // Older phones don't support this, so we start them // in video mode. } catch (Exception ce) { player = Manager.createPlayer("capture://video"); } player.realize(); // Get VideoControl for the viewfinder videoControl = (VideoControl) player.getControl("VideoControl"); if (videoControl == null) { discardPlayer(false); midlet.showError("Cannot get the video control.\n" + "Capture may not be supported."); player = null; } else { // Set up the viewfinder on the screen. videoControl.initDisplayMode(VideoControl.USE_DIRECT_VIDEO, this); int canvasWidth = getWidth(); int canvasHeight = getHeight(); int displayWidth = videoControl.getDisplayWidth(); int displayHeight = videoControl.getDisplayHeight(); int x = (canvasWidth - displayWidth) / 2; int y = (canvasHeight - displayHeight) / 2; videoControl.setDisplayLocation(x, y); player.start(); videoControl.setVisible(true); } } catch (IOException ioe) { discardPlayer(false); midlet.showError("IOException: " + ioe.getMessage()); } catch (MediaException me) { midlet.showError("MediaException: " + me.getMessage()); } catch (SecurityException se) { midlet.showError("SecurityException: " + se.getMessage()); } }
For the Series 40 full touch version of the MIDlet, create the method for building and starting the video player as follows:
synchronized void start() { try { if (!checkEncodingSupport()) { player = Manager.createPlayer("capture://image"); } else { player = Manager.createPlayer("capture://video"); } player.realize(); // Get VideoControl for the viewfinder videoControl = (VideoControl) player.getControl("VideoControl"); if (videoControl == null) { discardPlayer(false); midlet.showError("Cannot get the video control.\n" + "Capture may not be supported."); player = null; } else { // Set up the viewfinder on the screen. videoControl.initDisplayMode(VideoControl.USE_DIRECT_VIDEO, this); int canvasWidth = getWidth(); int canvasHeight = getHeight(); videoControl.setDisplaySize(canvasWidth, canvasHeight); int displayWidth = videoControl.getDisplayWidth(); int displayHeight = videoControl.getDisplayHeight(); int x = (canvasWidth - displayWidth) / 2; int y = (canvasHeight - displayHeight) / 2; videoControl.setDisplayLocation(x, y); player.start(); videoControl.setVisible(true); } } catch (IOException ioe) { discardPlayer(false); midlet.showError("IOException: " + ioe.getMessage()); } catch (MediaException me) { midlet.showError("MediaException: " + me.getMessage()); } catch (SecurityException se) { midlet.showError("SecurityException: " + se.getMessage()); } }
Create methods for stopping and discarding the video player.
// Stops the video player synchronized void stop() { if (player != null) { try { videoControl.setVisible(false); player.stop(); } catch (MediaException me) { midlet.showError("MediaException: " + me.getMessage()); } } } // this method will discard the video player private void discardPlayer(boolean restart) { if (player != null) { player.deallocate(); player.close(); player = null; } videoControl = null; if (restart) { start(); } }
Create a method for capturing the image in a new thread.
private void captureImage() { if (player != null) { // Capture image in a new thread. new Thread() { public void run() { try { //For Devices that support jpg image encoding, mostly Symbian byte[] jpgImage = videoControl.getSnapshot("encoding=image/jpg&width=270&height=360"); midlet.imageCaptured(jpgImage); discardPlayer(false); } catch (MediaException me) { try { //For Devices that do not support jpg image encoding, mostly Series 40 byte[] jpgImage = videoControl.getSnapshot(null); midlet.imageCaptured(jpgImage); discardPlayer(false); } catch (Exception e) { midlet.showError("Exception: " + e.getMessage()); discardPlayer(true); } } catch (SecurityException se) { midlet.showError("SecurityException: " + se.getMessage()); } } }.start(); } }
For the Series
40 full touch version of the MIDlet, implement gesture event handling.
The initializeGesture
method sets up gesture event
handling for the MIDlet. The gestureAction
method
implements the actual event handling. The MIDlet only registers tap
gestures, which it uses to capture an image.
Note: The GESTURE_TAP
event is supported from Java Runtime 2.0.0 for
Series 40 onwards.
public void initializeGesture() { GestureRegistrationManager.setListener(this, this); // Create an interactive zone and set it to receive taps GestureInteractiveZone myGestureZone = new GestureInteractiveZone(GestureInteractiveZone.GESTURE_TAP); // Set the location (relative to the container) and size of the interactive zone: myGestureZone.setRectangle(0, 0, getWidth(), getHeight()); // Register the interactive zone for GestureCanvas (this) GestureRegistrationManager.register(this, myGestureZone); } public void gestureAction(Object c, GestureInteractiveZone giz, GestureEvent gestureEvent) { switch (gestureEvent.getType()) { case GestureInteractiveZone.GESTURE_TAP: captureImage(); break; } } }
The InfoScreen
class
creates a form for displaying information and error messages.
To implement the InfoScreen
class:
Create the InfoScreen.java
class file.
Import the required classes.
import javax.microedition.lcdui.Alert; import javax.microedition.lcdui.AlertType; import javax.microedition.lcdui.Display;
Set InfoScreen
to extend Alert
, and create
the class constructor.
class InfoScreen extends Alert { InfoScreen() { super("InfoScreen"); }
Create methods for showing the INFO and ERROR alert types.
void showInfo(String message, Display display) { setTitle("Info"); setType(AlertType.INFO); setTimeout(3000); setString(message); display.setCurrent(this); } void showError(String message, Display display) { setTitle("Error"); setType(AlertType.ERROR); setTimeout(5000); setString(message); display.setCurrent(this); } }
The ReceiveScreen
class creates a form for displaying a received message. The screen
is activated when a new message arrives. The MIDlet main class reads
the incoming MMS message and passes it to the ReceiveScreen
class for parsing.
Note: On current Series 40 and Symbian devices, MMS messages are received in the inbox of the device's default messaging client. The MMSMIDlet cannot therefore display received messages.
To implement the ReceiveScreen
class:
Create the ReceiveScreen.java
class file.
Import the required classes.
import javax.microedition.lcdui.Command; import javax.microedition.lcdui.CommandListener; import javax.microedition.lcdui.Displayable; import javax.microedition.lcdui.Form; import javax.microedition.lcdui.Image; import javax.microedition.lcdui.ImageItem; import javax.microedition.lcdui.Item; import javax.microedition.lcdui.StringItem; import javax.wireless.messaging.Message; import javax.wireless.messaging.MessagePart; import javax.wireless.messaging.MultipartMessage;
Set ReceiveScreen
to extend Form
and implement CommandListener
.
class ReceiveScreen extends Form implements CommandListener {
Create the required
variables, and create the ReceiveScreen
class constructor.
private final MMSMIDlet midlet; private final Command commandClose = new Command("Close", Command.ITEM, 1); ReceiveScreen(MMSMIDlet midlet) { super(null); this.midlet = midlet; }
Create a method
for initializing the Message
object that is to be
displayed on the ReceiveScreen
. The method checks
that the Message
object is not null
and, if it is not, passes it to the createForm
method, which builds the form
used to display the message.
public void setMessage(Message mmsMessage) { if (mmsMessage != null) { createForm(mmsMessage); } }
Implement the commandAction
method for detecting command invocations.
public void commandAction(Command c, Displayable d) { if (c == commandClose) { midlet.resumeDisplay(); } }
Create the method for building the form used to display the message.
The getAddress
method returns the address associated
with the message, and the getTimestamp
method returns the timestamp
indicating when the message has been sent.
private void createForm(Message mmsMessage) { deleteAll(); setTitle("Received MMS Message"); if (mmsMessage instanceof MultipartMessage) { MultipartMessage multipartMessage = (MultipartMessage) mmsMessage; // Display message header. StringItem messageHeader = new StringItem(null, "From: " + mmsMessage.getAddress() + "\n" + "Sent: " + multipartMessage.getTimestamp().toString()); messageHeader.setLayout(Item.LAYOUT_NEWLINE_AFTER); append(messageHeader);
The getMessageParts
method returns an array of all MessageParts
of
the message.
MessagePart[] messageParts = multipartMessage.getMessageParts();
To display the message, the getMIMEType
method
returns the mime type and the getContentLocation
method
the content location of the MessagePart
, whereas
the getContent
method returns the content of the MessagePart
as an array of bytes.
// Display all parts of the message. for (int i = 0; i < messageParts.length; i++) { MessagePart messagePart = messageParts[i]; String mimeType = messagePart.getMIMEType(); String contentLocation = messagePart.getContentLocation(); byte[] part = messagePart.getContent(); if (mimeType.equals("image/jpeg")) { // Message contains an image. Image image = Image.createImage(part, 0, part.length); ImageItem imageItem = new ImageItem(null, image, Item.LAYOUT_CENTER + Item.LAYOUT_NEWLINE_AFTER, contentLocation); append(imageItem); } if (mimeType.equals("text/plain")) { // Message contains text. StringItem stringItem = new StringItem(null, new String( part)); stringItem.setLayout(Item.LAYOUT_CENTER); append(stringItem); } // Unknown MIME-types are ignored } } addCommand(commandClose); setCommandListener(this); } }
The SendScreen
class
creates a form for composing the message to be sent. When the user
selects Send, the class creates the image and text parts of
the message and delegates the sending to the MIDlet main class.
To implement the SendScreen
class:
Create the SendScreen.java
class file.
Import the required classes.
import java.io.UnsupportedEncodingException; import javax.microedition.lcdui.Command; import javax.microedition.lcdui.CommandListener; import javax.microedition.lcdui.Displayable; import javax.microedition.lcdui.Form; import javax.microedition.lcdui.Image; import javax.microedition.lcdui.ImageItem; import javax.microedition.lcdui.Item; import javax.microedition.lcdui.TextField; import javax.wireless.messaging.MessagePart; import javax.wireless.messaging.SizeExceededException;
For the Series 40 full touch version of the MIDlet, import also
the IconCommand
class.
import com.nokia.mid.ui.IconCommand;
Set SendScreen
to extend Form
and implement CommandListener
.
class SendScreen extends Form implements CommandListener {
Create the required
variables, and create the SendScreen
class constructor.
private final MMSMIDlet midlet; private final Command commandSend = new Command("Send", Command.ITEM, 1); private final Command commandBack = new Command("Back", Command.BACK, 0); private TextField messageText; private TextField recipientPhone; private byte[] messageImage; SendScreen(MMSMIDlet midlet) { super("MMS Message"); this.midlet = midlet; addCommand(commandSend); addCommand(commandBack); setCommandListener(this); }
For the Series 40 full touch version of the MIDlet,
replace the commandSend
variable with an IconCommand
and map it to action button 1 with the predefined
icon IconCommand.ICON_OK
.
private final IconCommand commandSend = new IconCommand("Send", Command.ITEM, 1, IconCommand.ICON_OK);
Create a method for initializing the message composing area.
void initializeComposeCanvas(byte[] pngImage) { messageText = new TextField("Text: ", null, 256, TextField.ANY); recipientPhone = new TextField("To: ", null, 256, TextField.PHONENUMBER); messageImage = pngImage; Image tempImage = Image.createImage(pngImage, 0, pngImage.length); ImageItem imageItem = new ImageItem(null, tempImage, Item.LAYOUT_CENTER, null); deleteAll(); append(messageText); append(recipientPhone); append(imageItem); midlet.showSendScreen(); }
Implement the commandAction
method for detecting command invocations.
public void commandAction(Command c, Displayable d) { if (c == commandBack) { midlet.showCameraScreen(); } if (c == commandSend) { sendMMS(); } }
Create a method for defining the MMS content and sending the MMS to the recipient.
private void sendMMS() { String recipientAddress = "mms://" + recipientPhone.getString() + ":" + midlet.getApplicationID(); try { // get the byte content of the message and // the captured image byte[] textBytes = messageText.getString().getBytes("UTF-8"); byte[] imageBytes = messageImage; // Create the message part containting the image // and give the appropriate MIME-type and id MessagePart imagePart = new MessagePart(imageBytes, 0, imageBytes.length, "image/jpeg", "id0", "snapshot image", null); // Create the message part containting the text // and give the appropriate MIME-type and id MessagePart textPart = new MessagePart(textBytes, 0, textBytes.length, "text/plain", "id1", "message text", "UTF-8"); midlet.showInfo("Sending message..."); midlet.sendMessage(recipientAddress, imagePart, textPart); } catch (UnsupportedEncodingException uee) { midlet.showError("Encoding not supported. " + uee.getMessage()); } catch (SizeExceededException see) { midlet.showError("Message part is too big. " + see.getMessage()); } } }
The UnsupportedEncodingException
indicates that
the selected character encoding is not supported, while the SizeExceededException
indicates that an operation
is not executable due to insufficient system resources.