Implementing the UI elements

For implementing the UI, the MIDlet contains the following classes, each of which creates a separate screen:

Implementing the CameraScreen class

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:

  1. Create the CameraScreen.java class file.

  2. 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;
  3. 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 {
  4. 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();
    
        }
  5. 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());
        }
  6. 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();
            }
        }
  7. Create a method for detecting key presses.

        public void keyPressed(int keyCode) {
            if (getGameAction(keyCode) == FIRE) {
                captureImage();
            }
        }
  8. 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);
        }
  9. 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());
            }
        }
  10. 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();
            }
        }
  11. 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
                            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();
            }
        }
  12. 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;
            }
        }
    }

Implementing the InfoScreen class

The InfoScreen class creates a form for displaying information and error messages.

To implement the InfoScreen class:

  1. Create the InfoScreen.java class file.

  2. Import the required classes.

    import javax.microedition.lcdui.Alert;
    import javax.microedition.lcdui.AlertType;
    import javax.microedition.lcdui.Display;
  3. Set InfoScreen to extend Alert, and create the class constructor.

    class InfoScreen extends Alert {
    
        InfoScreen() {
            super("InfoScreen");
        }
  4. 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);
        }
    }

Implementing the ReceiveScreen class

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 Nokia Asha and Series 40 software platform 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:

  1. Create the ReceiveScreen.java class file.

  2. 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;
  3. Set ReceiveScreen to extend Form and implement CommandListener.

    class ReceiveScreen extends Form implements CommandListener {
  4. 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;
        }
  5. 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);
            }
        }
  6. Implement the commandAction method for detecting command invocations.

        public void commandAction(Command c, Displayable d) {
            if (c == commandClose) {
                midlet.resumeDisplay();
            }
        }
  7. 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);
        }
    }

Implementing the SendScreen class

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:

  1. Create the SendScreen.java class file.

  2. 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;
  3. Set SendScreen to extend Form and implement CommandListener.

    class SendScreen extends Form implements CommandListener {
  4. 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);
  5. 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();
        }
  6. Implement the commandAction method for detecting command invocations.

        public void commandAction(Command c, Displayable d) {
            if (c == commandBack) {
                midlet.showCameraScreen();
            }
    
            if (c == commandSend) {
                sendMMS();
            }
        }
  7. 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.