/* * ComposeScreen.java * * Copyright � 1998-2011 Research In Motion Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Note: For the sake of simplicity, this sample application may not leverage * resource bundles and resource strings. However, it is STRONGLY recommended * that application developers make use of the localization features available * within the BlackBerry development platform to ensure a seamless application * experience across a variety of languages and geographies. For more information * on localizing your application, please refer to the BlackBerry Java Development * Environment Development Guide associated with this release. */ package com.rim.samples.device.blackberrymaildemo; import java.util.Calendar; import java.util.Vector; import net.rim.blackberry.api.mail.Address; import net.rim.blackberry.api.mail.AddressException; import net.rim.blackberry.api.mail.Folder; import net.rim.blackberry.api.mail.Message; import net.rim.blackberry.api.mail.MessagingException; import net.rim.blackberry.api.mail.Multipart; import net.rim.blackberry.api.mail.Session; import net.rim.blackberry.api.mail.Store; import net.rim.blackberry.api.mail.TextBodyPart; import net.rim.blackberry.api.mail.Transport; import net.rim.device.api.command.Command; import net.rim.device.api.command.CommandHandler; import net.rim.device.api.command.ReadOnlyCommandMetadata; import net.rim.device.api.ui.Field; import net.rim.device.api.ui.MenuItem; import net.rim.device.api.ui.UiApplication; import net.rim.device.api.ui.component.BasicEditField; import net.rim.device.api.ui.component.Dialog; import net.rim.device.api.ui.component.EditField; import net.rim.device.api.ui.component.Menu; import net.rim.device.api.ui.component.SeparatorField; import net.rim.device.api.ui.component.TextField; import net.rim.device.api.util.StringProvider; /** * The ComposeScreen is a screen which displays either a new or saved message. * It adds the functionality of saving and sending messages to its parent class, * MessageScreen. */ public final class ComposeScreen extends MessageScreen { private static final int FIRST = 0; private static final int SEND_MENU_ITEM_INDEX = 0; private final Store _store; /** * Creates a new ComposeScreen object * * @param message * A message in the process of being composed, or null if a new * message is to be composed * @param store * The message store for this application */ public ComposeScreen(final Message message, final Store store) { super(message, true); _store = store; // If a new message is to be created, indicate this in the title if (message == null) { setTitle("New Message"); } // Create and add menu items specific to the Compose action (addTo, // addBcc, addCc, etc...). final AddHeaderFieldAction addToMenuItem = new AddHeaderFieldAction(Message.RecipientType.TO, "Add To: ", "To: "); final AddHeaderFieldAction addCcMenuItem = new AddHeaderFieldAction(Message.RecipientType.CC, "Add Cc: ", "Cc: "); final AddHeaderFieldAction addBccMenuItem = new AddHeaderFieldAction(Message.RecipientType.BCC, "Add Bcc: ", "Bcc: "); // MenuItem to save a message final MenuItem saveMenuItem = new MenuItem(new StringProvider("Save Message"), 0x230020, 1); saveMenuItem.setCommand(new Command(new CommandHandler() { /** * @see net.rim.device.api.command.CommandHandler#execute(ReadOnlyCommandMetadata, * Object) */ public void execute(final ReadOnlyCommandMetadata metadata, final Object context) { // If the save is completed, then // discard this screen if (onSave()) { close(); } else // If the message could not be saved, alert the user { UiApplication.getUiApplication().invokeLater( new Runnable() { public void run() { Dialog.alert("Message could not be saved"); } }); } } })); // MenuItem to send a message final MenuItem sendMenuItem = new MenuItem(new StringProvider("Send Message"), 0x230010, 0); sendMenuItem.setCommand(new Command(new CommandHandler() { /** * @see net.rim.device.api.command.CommandHandler#execute(ReadOnlyCommandMetadata, * Object) */ public void execute(final ReadOnlyCommandMetadata metadata, final Object context) { // If the save is completed, then // discard this screen try { _message = getMessage(); if (_message != null) { // Send the message Transport.send(_message); // Close the screen close(); } } catch (final MessagingException e) { BlackBerryMailDemo .errorDialog("Transport.send(Message) threw " + e.toString()); } } })); addMenuItem(sendMenuItem); addMenuItem(saveMenuItem); addMenuItem(addToMenuItem); addMenuItem(addCcMenuItem); addMenuItem(addBccMenuItem); } /** * Overrides MessageScreen.displayMessage(). The message's 'sent' properties * are not displayed since the message is still in the process of editing. */ void displayMessage() { // If the message does not exist then compose a new message if (_message == null) { // Add a To line final EditField toField = new EditField("To: ", "", 40, BasicEditField.FILTER_EMAIL); addTextFieldToTableAndScreen(toField, Message.RecipientType.TO); // Add a subject line final EditField subjectField = new EditField("Subject: ", ""); addTextFieldToTableAndScreen(subjectField, SUBJECT); // Add a separator between the body and the headers add(new SeparatorField()); // Add a body field final EditField bodyField = new EditField(); addTextFieldToTableAndScreen(bodyField, BODY); } else // The message exists so display it { displayHeader(); add(new SeparatorField()); displayMessageBody(); } } /** * Gets a message for sending or saving * * @return A new message */ Message getMessage() { // Find an outbox folder and use it to construct a new message final Folder outbox = _store.findFolder("Outbox")[FIRST]; final Message message = new Message(outbox); // Add all the current headers for (int keyNo = 0; keyNo < HEADER_KEYS.length; keyNo++) { final Vector fieldsByType = (Vector) _fieldTable.get(HEADER_KEYS[keyNo]); if (fieldsByType != null) { // Build a vector of all the addresses final Vector addressVector = new Vector(); final int size = fieldsByType.size(); for (int fieldNo = 0; fieldNo < size; fieldNo++) { final TextField addressField = (TextField) fieldsByType.elementAt(fieldNo); // Try to create a new address object wrapping the email // address and add it to the address vector. try { addressVector.addElement(new Address(addressField .getText(), "")); } catch (final AddressException e) // Invalid address { BlackBerryMailDemo .errorDialog("Address(String, String) threw " + e.toString()); } } // Dump the vector of addresses into an array to send the // message final Address[] addresses = new Address[addressVector.size()]; addressVector.copyInto(addresses); // Try to add the addresses to the message's list of recipients try { message.addRecipients(HEADER_KEYS[keyNo], addresses); } catch (final MessagingException e) { BlackBerryMailDemo .errorDialog("Message#addRecipients(int, Address[]) threw " + e.toString()); } } } // Add the subject final Vector subjectFields = (Vector) _fieldTable.get(SUBJECT); final TextField subjectField = (TextField) subjectFields.elementAt(FIRST); if (subjectFields != null && subjectFields.size() > 0) { message.setSubject(subjectField.getText()); } // Add the body by adding all the body fields into one multipart final Vector bodyFields = (Vector) _fieldTable.get(BODY); if (bodyFields != null) { final int size = bodyFields.size(); final Multipart content = new Multipart(); for (int fieldNo = 0; fieldNo < size; fieldNo++) { final TextField body = (TextField) bodyFields.elementAt(fieldNo); content.addBodyPart(new TextBodyPart(content, body.getText())); } try { message.setContent(content); } catch (final MessagingException e) { BlackBerryMailDemo .errorDialog("Message#setContent(Object) threw " + e.toString()); } } else { BlackBerryMailDemo.errorDialog("Error: no body field available"); return null; } // Set the date message.setSentDate(Calendar.getInstance().getTime()); return message; } /** * @see net.rim.device.api.ui.Screen#onSave() */ protected boolean onSave() { // Save the message to the outbox try { final Message newMessage = getMessage(); if (newMessage != null) { // Retrieve an outbox to save the message in final Store store = Session.waitForDefaultSession().getStore(); final Folder[] allOutboxFolders = store.list(Folder.OUTBOX); Folder outbox = null; for (int i = allOutboxFolders.length - 1; i >= 0; --i) { final Folder parent = allOutboxFolders[i].getParent(); if (parent != null && parent.getName().startsWith("Mailbox")) { outbox = allOutboxFolders[i]; break; } } if (outbox == null) { throw new MessagingException("no outbox folder found"); } // Save the new message and replace the old one if it exists outbox.appendMessage(newMessage); if (_message != null) { outbox.deleteMessage(_message, true); } _message = newMessage; // Set the status to composing and flag that it has been saved _message.setStatus(Message.Status.TX_COMPOSING, Message.Status.TX_ERROR); _message.setFlag(Message.Flag.SAVED, true); return true; } return false; } catch (final MessagingException e) { return false; } } /** * Make "Send" the default menu item. * * @see net.rim.device.api.ui.container.MainScreen#makeMenu(Menu,int) */ protected void makeMenu(final Menu menu, final int instance) { super.makeMenu(menu, instance); menu.setDefault(SEND_MENU_ITEM_INDEX); } /** * This class is responsible for adding the various header fields to the * compose screen (To, Bcc, CC). */ private final class AddHeaderFieldAction extends MenuItem { private final String _fieldLabelText; private final int _headerType; /** * Constructs a menu item which adds a header of a specified type to the * compose screen. * * @param headerType * One of the Message.RecipientType fields * @param menuItemText * String to use for the menu item * @param fieldLabelText * String to use for the label of this field */ AddHeaderFieldAction(final int headerType, final String menuItemText, final String fieldLabelText) { super(new StringProvider(menuItemText), 0x240010, 2); _fieldLabelText = fieldLabelText; _headerType = headerType; this.setCommand(new Command(new CommandHandler() { /** * Adds a new header field to the message. The field is placed * so that the header types are grouped together and the most * recently added one is closest to the bottom. * * @see net.rim.device.api.command.CommandHandler#execute(ReadOnlyCommandMetadata, * Object) */ public void execute(final ReadOnlyCommandMetadata metadata, final Object context) { final EditField newField = new EditField(_fieldLabelText, ""); // Find out where the last field of this type was added to // the // screen. Vector fieldsByType = (Vector) _fieldTable.get(_headerType); int lastInsertedIndex; if (fieldsByType == null) { // If a field of _headerType was not made yet, then // create the // vector which contains all of the fields of // _headerType. fieldsByType = new Vector(); _fieldTable.put(_headerType, fieldsByType); lastInsertedIndex = getIndexForNewFieldType(); } else { lastInsertedIndex = getIndexOfLastFieldOfType(_headerType); } // Add the new field to both the screen and the vector // keeping track // of all the fields of the same type. ComposeScreen.this.insert(newField, lastInsertedIndex + 1); fieldsByType.addElement(newField); newField.setFocus(); } })); } /** * Given the existing field type of this instance of the class, * determine where to place new header fields in the screen. * * @return The index at which to insert the new header field */ private int getIndexForNewFieldType() { // Note: we don't handle TO here since there ALWAYS must be one TO // field. switch (_headerType) { // Find the last TO field and use the next index as the // insertion point. case Message.RecipientType.CC: return getIndexOfLastFieldOfType(Message.RecipientType.TO); // Try to find the last CC field and use it as the next // insertion point. If no CC field exists then find the last // TO field and use its index as the insertion point. case Message.RecipientType.BCC: final int index = getIndexOfLastFieldOfType(Message.RecipientType.CC); if (index == -1) { return getIndexOfLastFieldOfType(Message.RecipientType.TO); } return index; default: throw new IllegalStateException( "Mail Demo: Unrecognized recipient type"); } } /** * Retrieves the index of the last field added of a specified type. * * @param type * The type of header field to retrieve the last index of * @return The index of the most recently added field of the specified * type, -1 if a field of the specified type has not been added * yet. */ private int getIndexOfLastFieldOfType(final int type) { final Vector fields = (Vector) _fieldTable.get(type); if (fields == null) { return -1; } final Field field = (Field) fields.lastElement(); return field.getIndex(); } } }