root/trunk/LogicMail/src/org/logicprobe/LogicMail/ui/CompositionScreen.java

Revision 704, 38.6 KB (checked in by octorian, 27 hours ago)

Cleanup of inappropriate getAccountConfig() calls

Line 
1/*-
2 * Copyright (c) 2008, Derek Konigsberg
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the project nor the names of its
15 *    contributors may be used to endorse or promote products derived
16 *    from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29 * OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31package org.logicprobe.LogicMail.ui;
32
33import java.io.DataInputStream;
34import java.io.IOException;
35import java.util.Calendar;
36import java.util.Vector;
37
38import javax.microedition.io.Connector;
39import javax.microedition.io.file.FileConnection;
40
41import net.rim.device.api.io.MIMETypeAssociations;
42import net.rim.device.api.system.Application;
43import net.rim.device.api.system.EventLogger;
44import net.rim.device.api.ui.Field;
45import net.rim.device.api.ui.Font;
46import net.rim.device.api.ui.Keypad;
47import net.rim.device.api.ui.Manager;
48import net.rim.device.api.ui.MenuItem;
49import net.rim.device.api.ui.Screen;
50import net.rim.device.api.ui.component.AutoTextEditField;
51import net.rim.device.api.ui.component.Dialog;
52import net.rim.device.api.ui.component.Menu;
53import net.rim.device.api.util.Arrays;
54import net.rim.device.api.util.DataBuffer;
55
56import org.logicprobe.LogicMail.AppInfo;
57import org.logicprobe.LogicMail.LogicMailResource;
58import org.logicprobe.LogicMail.conf.IdentityConfig;
59import org.logicprobe.LogicMail.conf.MailSettings;
60import org.logicprobe.LogicMail.message.AbstractMimeMessagePartVisitor;
61import org.logicprobe.LogicMail.message.ApplicationPart;
62import org.logicprobe.LogicMail.message.AudioPart;
63import org.logicprobe.LogicMail.message.ContentPart;
64import org.logicprobe.LogicMail.message.ImagePart;
65import org.logicprobe.LogicMail.message.Message;
66import org.logicprobe.LogicMail.message.MessagePart;
67import org.logicprobe.LogicMail.message.MimeMessageContent;
68import org.logicprobe.LogicMail.message.MimeMessageContentFactory;
69import org.logicprobe.LogicMail.message.MessageEnvelope;
70import org.logicprobe.LogicMail.message.MessageFlags;
71import org.logicprobe.LogicMail.message.MimeMessagePart;
72import org.logicprobe.LogicMail.message.MimeMessagePartFactory;
73import org.logicprobe.LogicMail.message.MultiPart;
74import org.logicprobe.LogicMail.message.TextContent;
75import org.logicprobe.LogicMail.message.TextPart;
76import org.logicprobe.LogicMail.message.UnsupportedContentException;
77import org.logicprobe.LogicMail.message.VideoPart;
78import org.logicprobe.LogicMail.model.Address;
79import org.logicprobe.LogicMail.model.MailboxNode;
80import org.logicprobe.LogicMail.model.MessageNode;
81import org.logicprobe.LogicMail.model.MessageNodeEvent;
82import org.logicprobe.LogicMail.model.MessageNodeListener;
83import org.logicprobe.LogicMail.model.NetworkAccountNode;
84import org.logicprobe.LogicMail.util.EventObjectRunnable;
85import org.logicprobe.LogicMail.util.UnicodeNormalizer;
86
87
88/**
89 * This is the message composition screen.
90 */
91public class CompositionScreen extends AbstractScreenProvider {
92    public final static int COMPOSE_NORMAL = 0;
93    public final static int COMPOSE_REPLY = 1;
94    public final static int COMPOSE_REPLY_ALL = 2;
95    public final static int COMPOSE_FORWARD = 3;
96   
97    private int composeType = -1;
98    private String initialRecipient;
99    private MessageNode sourceMessageNode;
100    private NetworkAccountNode accountNode;
101    private UnicodeNormalizer unicodeNormalizer;
102   
103    private BorderedFieldManager recipientsFieldManager;
104        private BorderedFieldManager subjectFieldManager;
105        private BorderedFieldManager messageFieldManager;
106    private AutoTextEditField subjectEditField;
107    private AutoTextEditField messageEditField;
108    private BorderedFieldManager attachmentsFieldManager;
109   
110    private String inReplyTo;
111    private boolean messageSent;
112    private IdentityConfig identityConfig;
113    private MessageNode replyToMessageNode;
114
115    private Object generateLock = new Object();
116    private Message pendingMessage;
117    private Vector pendingLocalAttachments = new Vector();
118    private Vector pendingRemoteAttachments = new Vector();
119    private Runnable pendingMessageRunnable;
120   
121    private MenuItem sendMenuItem = new MenuItem(resources.getString(LogicMailResource.MENUITEM_SEND), 300100, 10) {
122        public void run() {
123            sendMessage();
124        }
125    };
126    private MenuItem saveDraftMenuItem = new MenuItem(resources.getString(LogicMailResource.MENUITEM_SAVE_DRAFT), 300200, 20) {
127        public void run() {
128            saveAsDraft();
129            screen.close();
130        }
131    };
132    private MenuItem addToMenuItem = new MenuItem(resources.getString(LogicMailResource.MENUITEM_ADD_TO), 400100, 1010) {
133        public void run() {
134            insertRecipientField(EmailAddressBookEditField.ADDRESS_TO);
135        }
136    };
137    private MenuItem addCcMenuItem = new MenuItem(resources.getString(LogicMailResource.MENUITEM_ADD_CC), 400200, 1020) {
138        public void run() {
139            insertRecipientField(EmailAddressBookEditField.ADDRESS_CC);
140        }
141    };
142    private MenuItem addBccMenuItem = new MenuItem(resources.getString(LogicMailResource.MENUITEM_ADD_BCC), 400300, 1030) {
143        public void run() {
144            insertRecipientField(EmailAddressBookEditField.ADDRESS_BCC);
145        }
146    };
147    private MenuItem attachFileMenuItem = new MenuItem(resources.getString(LogicMailResource.MENUITEM_ATTACH_FILE), 400400, 1040) {
148        public void run() {
149            attachFile();
150        }
151    };
152    private MenuItem deleteFieldMenuItem = new MenuItem(resources.getString(LogicMailResource.MENUITEM_DELETE_FIELD), 400900, 1090) {
153        public void run() {
154            deleteField(false);
155        }
156    };
157   
158    private MessageNodeListener messageNodeListener = new MessageNodeListener() {
159        public void messageStatusChanged(MessageNodeEvent e) {
160            messageNodeListener_MessageStatusChanged(e);
161        }
162    };
163
164    /**
165     * Creates a new instance of CompositionScreen.
166     *
167     * @param accountNode Account node
168     */
169    public CompositionScreen(NetworkAccountNode accountNode) {
170        this.accountNode = accountNode;
171        this.identityConfig = accountNode.getIdentityConfig();
172        if(MailSettings.getInstance().getGlobalConfig().getUnicodeNormalization()) {
173            unicodeNormalizer = UnicodeNormalizer.getInstance();
174        }
175    }
176
177    /**
178     * Creates a new instance of CompositionScreen.
179     *
180     * @param accountNode Account node
181     * @param recipient Message recipient address to pre-populate the "To" field with
182     */
183    public CompositionScreen(NetworkAccountNode accountNode, String recipient) {
184        this(accountNode);
185        this.initialRecipient = recipient;
186    }
187
188    /**
189     * Creates a new instance of CompositionScreen.
190     * Used for working with an already created message,
191     * such as a draft, reply, or forward.
192     *
193     * @param accountNode Account node
194     * @param messageNode Message we are composing
195     * @param composeType Type of message we are creating
196     */
197    public CompositionScreen(
198            NetworkAccountNode accountNode,
199                MessageNode messageNode,
200                int composeType) {
201        this.accountNode = accountNode;
202        this.identityConfig = accountNode.getIdentityConfig();
203        if(MailSettings.getInstance().getGlobalConfig().getUnicodeNormalization()) {
204            unicodeNormalizer = UnicodeNormalizer.getInstance();
205        }
206
207        this.composeType = composeType;
208        this.sourceMessageNode = messageNode;
209    }
210
211    private void messageNodeListener_MessageStatusChanged(MessageNodeEvent e) {
212        int type = e.getType();
213        if(type == MessageNodeEvent.TYPE_STRUCTURE_LOADED
214                || type == MessageNodeEvent.TYPE_CONTENT_LOADED) {
215            // Immediately remove the listener to avoid redundant calls
216            MessageNode messageNode = (MessageNode)e.getSource();
217            messageNode.removeMessageNodeListener(messageNodeListener);
218
219            // Schedule the UI update
220            invokeLater(new EventObjectRunnable(e) {
221                public void run() {
222                    MessageNode messageNode = (MessageNode)getEvent().getSource();
223                    populateFromMessage(messageNode);
224                    messageEditField.setEditable(true);
225                }
226            });
227        }
228    }
229
230    private void populateFromMessage(MessageNode message) {
231        int i;
232        MimeMessagePart body = message.getMessageStructure();
233
234        PopulatePartsVisitor visitor = new PopulatePartsVisitor();
235        body.accept(visitor);
236       
237        TextPart bodyPart = visitor.getFirstTextPart();
238        if(bodyPart != null) {
239            MimeMessageContent content = message.getMessageContent(bodyPart);
240            if(content instanceof TextContent) {
241                messageEditField.insert(normalize(((TextContent)content).getText()));
242                messageEditField.setCursorPosition(0);
243            }
244        }
245
246        Vector attachmentParts = visitor.getAttachmentParts();
247        if(attachmentParts.size() > 0) {
248            if(attachmentsFieldManager != null) {
249                attachmentsFieldManager.deleteAll();
250            }
251            else {
252                attachmentsFieldManager = FieldFactory.getInstance().getBorderedFieldManager(
253                        BorderedFieldManager.BOTTOM_BORDER_NORMAL
254                        | BorderedFieldManager.OUTER_FILL_NONE);
255                messageFieldManager.add(attachmentsFieldManager);
256            }
257           
258            int size = attachmentParts.size();
259            for(i = 0; i < size; i++) {
260                ContentPart attachmentPart = (ContentPart)attachmentParts.elementAt(i);
261                attachmentsFieldManager.add(new AttachmentField(message, attachmentPart));
262            }
263        }
264
265        // Set the subject
266        subjectEditField.setText(normalize(message.getSubject()));
267
268        // Set the recipients
269        Address[] recipients = message.getTo();
270        if (recipients != null) {
271            for (i = 0; i < recipients.length; i++) {
272                insertRecipientField(EmailAddressBookEditField.ADDRESS_TO).setText(normalize(recipients[i].toString()));
273            }
274        }
275
276        recipients = message.getCc();
277        if (recipients != null) {
278            for (i = 0; i < recipients.length; i++) {
279                insertRecipientField(EmailAddressBookEditField.ADDRESS_CC).setText(normalize(recipients[i].toString()));
280            }
281        }
282
283        recipients = message.getBcc();
284        if (recipients != null) {
285            for (i = 0; i < recipients.length; i++) {
286                insertRecipientField(EmailAddressBookEditField.ADDRESS_BCC).setText(normalize(recipients[i].toString()));
287            }
288        }
289
290        inReplyTo = message.getInReplyTo();
291    }
292   
293    private static class PopulatePartsVisitor extends AbstractMimeMessagePartVisitor {
294        private TextPart firstTextPart;
295        private Vector attachmentParts = new Vector();;
296       
297        public TextPart getFirstTextPart() {
298            return firstTextPart;
299        }
300       
301        public Vector getAttachmentParts() {
302            return attachmentParts;
303        }
304       
305        public void visitTextPart(TextPart part) {
306            if(firstTextPart == null) {
307                firstTextPart = part;
308            }
309            else {
310                attachmentParts.addElement(part);
311            }
312        }
313       
314        public void visitApplicationPart(ApplicationPart part) {
315            attachmentParts.addElement(part);
316        }
317        public void visitAudioPart(AudioPart part) {
318            attachmentParts.addElement(part);
319        }
320        public void visitImagePart(ImagePart part) {
321            attachmentParts.addElement(part);
322        }
323        public void visitMessagePart(MessagePart part) {
324            attachmentParts.addElement(part);
325        }
326        public void visitVideoPart(VideoPart part) {
327            attachmentParts.addElement(part);
328        }
329    };
330   
331    private void appendSignature() {
332        // Add the signature if available
333        if (identityConfig != null) {
334            String sig = identityConfig.getMsgSignature();
335
336            if ((sig != null) && (sig.length() > 0)) {
337                messageEditField.insert("\r\n-- \r\n" + sig);
338                messageEditField.setCursorPosition(0);
339            }
340        }
341    }
342   
343    public void initFields(Screen screen) {
344        super.initFields(screen);
345
346        FieldFactory fieldFactory = FieldFactory.getInstance();
347        recipientsFieldManager = fieldFactory.getBorderedFieldManager(
348                        Manager.NO_HORIZONTAL_SCROLL
349                        | Manager.NO_VERTICAL_SCROLL
350                        | BorderedFieldManager.BOTTOM_BORDER_NONE);
351        recipientsFieldManager.add(new EmailAddressBookEditField(
352                EmailAddressBookEditField.ADDRESS_TO, ""));
353        recipientsFieldManager.add(new EmailAddressBookEditField(
354                EmailAddressBookEditField.ADDRESS_CC, ""));
355
356        subjectFieldManager = fieldFactory.getBorderedFieldManager(
357                        Manager.NO_HORIZONTAL_SCROLL
358                        | Manager.NO_VERTICAL_SCROLL
359                        | BorderedFieldManager.BOTTOM_BORDER_LINE);
360        subjectEditField = new AutoTextEditField(resources.getString(LogicMailResource.MESSAGEPROPERTIES_SUBJECT) + ' ', "");
361        subjectEditField.setFont(subjectEditField.getFont().derive(Font.BOLD));
362        subjectFieldManager.add(subjectEditField);
363       
364        messageFieldManager = new BorderedFieldManager(
365                BorderedFieldManager.BOTTOM_BORDER_NORMAL
366                | BorderedFieldManager.FILL_NONE
367                | Field.USE_ALL_HEIGHT);
368        messageEditField = new AutoTextEditField();
369                messageEditField.setEditable(false);
370        messageFieldManager.add(messageEditField);
371       
372        screen.add(recipientsFieldManager);
373        screen.add(subjectFieldManager);
374        screen.add(messageFieldManager);
375       
376        if(sourceMessageNode == null) {
377            appendSignature();
378                messageEditField.setEditable(true);
379
380                if(initialRecipient != null) {
381                    EmailAddressBookEditField toAddressField =
382                        (EmailAddressBookEditField)recipientsFieldManager.getField(0);
383                    toAddressField.setText(initialRecipient);
384                }
385        }
386        else if(composeType == COMPOSE_NORMAL) {
387                if(sourceMessageNode.getMessageStructure() != null && sourceMessageNode.hasMessageContent()) {
388                        populateFromMessage(sourceMessageNode);
389                        messageEditField.setEditable(true);
390                }
391                else {
392                        sourceMessageNode.addMessageNodeListener(messageNodeListener);
393                        sourceMessageNode.refreshMessage();
394                }
395        }
396        else
397        {
398                this.replyToMessageNode = sourceMessageNode;
399       
400                MessageNode populateMessage;
401       
402                switch (composeType) {
403                case COMPOSE_REPLY:
404                        populateMessage = sourceMessageNode.toReplyMessage();
405                    break;
406                case COMPOSE_REPLY_ALL:
407                        populateMessage = sourceMessageNode.toReplyAllMessage(identityConfig.getEmailAddress());
408                    break;
409                case COMPOSE_FORWARD:
410                    //TODO: Consider bringing along attachments when forwarding
411                        populateMessage = sourceMessageNode.toForwardMessage();
412                    break;
413            default:
414                populateMessage = sourceMessageNode;
415                break;
416                }
417                populateFromMessage(populateMessage);
418                appendSignature();
419                messageEditField.setEditable(true);
420        }
421    }
422
423    public boolean onClose() {
424        if (!messageSent &&
425                ((subjectEditField.getText().length() > 0) ||
426                (messageEditField.getText().length() > 0))) {
427
428                boolean shouldClose = false;
429                if(accountNode.getDraftMailbox() != null) {
430                        int choice = Dialog.ask(
431                                        resources.getString(LogicMailResource.COMPOSITION_PROMPT_SAVE_OR_DISCARD),
432                                        new Object[] {
433                                                        resources.getString(LogicMailResource.MENUITEM_SAVE_AS_DRAFT),
434                                                        resources.getString(LogicMailResource.MENUITEM_DISCARD),
435                                                        resources.getString(LogicMailResource.MENUITEM_CANCEL) }, 0);
436                        if(choice == 0) {
437                                // Save as draft, then close
438                                saveAsDraft();
439                                shouldClose = true;
440                        }
441                        else if(choice == 1) {
442                                shouldClose = true;
443                        }
444                }
445                else {
446                        int choice =
447                                Dialog.ask(
448                                                resources.getString(LogicMailResource.COMPOSITION_PROMPT_DISCARD_UNSENT),
449                                                new Object[] {
450                                                                resources.getString(LogicMailResource.MENUITEM_DISCARD),
451                                                                resources.getString(LogicMailResource.MENUITEM_CANCEL)}, 0);
452                        if(choice == 0) { shouldClose = true; }
453                }
454               
455            if (shouldClose) {
456                screen.close();
457                return true;
458            } else {
459                return false;
460            }
461        } else {
462                screen.close();
463            return true;
464        }
465    }
466
467    public void makeMenu(Menu menu, int instance) {
468        if (((EmailAddressBookEditField) recipientsFieldManager.getField(0))
469                .getText().length() > 0) {
470            menu.add(sendMenuItem);
471        }
472        MailboxNode draftMailbox = accountNode.getDraftMailbox();
473        if(draftMailbox != null
474                && ((subjectEditField.getText().length() > 0)
475                || (messageEditField.getText().length() > 0))) {
476            menu.add(saveDraftMenuItem);
477        }
478
479        menu.add(addToMenuItem);
480        menu.add(addCcMenuItem);
481        menu.add(addBccMenuItem);
482        menu.add(attachFileMenuItem);
483       
484        // "Delete field" is shown if the focus is on a recipient field that is
485        // not the only recipient field, or if the focus is on an attachment
486        // field.
487        if((recipientsFieldManager.getFieldWithFocus() != null
488                && recipientsFieldManager.getFieldWithFocusIndex() > 0)
489                || (attachmentsFieldManager != null
490                        && attachmentsFieldManager.getFieldWithFocus() != null
491                        && attachmentsFieldManager.getFieldCount() > 0)) {
492            menu.add(deleteFieldMenuItem);
493        }
494    }
495   
496    public boolean keyChar(char key, int status, int time) {
497        switch (key) {
498        case Keypad.KEY_BACKSPACE:
499            if(deleteField(true)) {
500                return true;
501            }
502            else {
503                return super.keyChar(key, status, time);
504            }
505        default:
506            return super.keyChar(key, status, time);
507        }
508    }
509   
510    /**
511     * Delete the current recipient or attachment field.
512     *
513     * @param onlyEmpty only delete a recipient field if its contents are empty
514     * @return true, if successful
515     */
516    private boolean deleteField(boolean onlyEmpty) {
517        if(recipientsFieldManager.getFieldWithFocus() != null
518                && recipientsFieldManager.getFieldWithFocusIndex() > 0) {
519            EmailAddressBookEditField currentField =
520                (EmailAddressBookEditField) recipientsFieldManager.getFieldWithFocus();
521           
522            if(onlyEmpty && currentField.getText().length() > 0) {
523                return false;
524            }
525           
526            int index = currentField.getIndex();
527            recipientsFieldManager.delete(currentField);
528            recipientsFieldManager.getField(index - 1).setFocus();
529            return true;
530        }
531        else if(attachmentsFieldManager != null
532                && attachmentsFieldManager.getFieldWithFocus() != null
533                && attachmentsFieldManager.getFieldCount() > 0) {
534            attachmentsFieldManager.delete(attachmentsFieldManager.getFieldWithFocus());
535           
536            if(attachmentsFieldManager.getFieldCount() == 0) {
537                messageFieldManager.delete(attachmentsFieldManager);
538                attachmentsFieldManager = null;
539            }
540           
541            return true;
542        }
543        else {
544            return false;
545        }
546    }
547   
548    private void saveAsDraft() {
549        final MailboxNode draftMailbox = accountNode.getDraftMailbox();
550        final MessageEnvelope envelope = generateEnvelope();
551        generateMessage(new Runnable() {
552            public void run() {
553                envelope.date = Calendar.getInstance().getTime();
554                MessageFlags messageFlags = new MessageFlags(
555                        false,  // seen
556                        false,  // answered
557                        false,  // flagged
558                        false,  // deleted
559                        true,   // draft
560                        true,   // recent
561                        false,  // forwarded
562                        false); // junk
563                draftMailbox.appendMessage(envelope, pendingMessage, messageFlags);
564                // TODO: Save reply-to information with the draft message
565            }
566        });
567    }
568
569    private MessageEnvelope generateEnvelope() {
570        // Simplest possible implementation for now,
571        // which turns the content of the screen into
572        // a message containing a single text/plain section
573        MessageEnvelope env = new MessageEnvelope();
574
575        env.inReplyTo = inReplyTo;
576
577        // Build the recipients list
578        EmailAddressBookEditField currentField;
579        int size = recipientsFieldManager.getFieldCount();
580
581        for (int i = 0; i < size; i++) {
582            currentField = (EmailAddressBookEditField) recipientsFieldManager.getField(i);
583
584            if ((currentField.getAddressType() == EmailAddressBookEditField.ADDRESS_TO) &&
585                    (currentField.getText().length() > 0)) {
586                if (env.to == null) {
587                    env.to = new String[1];
588                    env.to[0] = currentField.getText();
589                } else {
590                    Arrays.add(env.to, currentField.getText());
591                }
592            } else if ((currentField.getAddressType() == EmailAddressBookEditField.ADDRESS_CC) &&
593                    (currentField.getText().length() > 0)) {
594                if (env.cc == null) {
595                    env.cc = new String[1];
596                    env.cc[0] = currentField.getText();
597                } else {
598                    Arrays.add(env.cc, currentField.getText());
599                }
600            } else if ((currentField.getAddressType() == EmailAddressBookEditField.ADDRESS_BCC) &&
601                    (currentField.getText().length() > 0)) {
602                if (env.bcc == null) {
603                    env.bcc = new String[1];
604                    env.bcc[0] = currentField.getText();
605                } else {
606                    Arrays.add(env.bcc, currentField.getText());
607                }
608            }
609        }
610
611        // Set the sender and reply-to addresses
612        // (this comes from identity settings)
613        env.from = new String[1];
614
615        String fullName = identityConfig.getFullName();
616
617        if ((fullName != null) && (fullName.length() > 0)) {
618            env.from[0] = "\"" + fullName + "\"" + " <" +
619            identityConfig.getEmailAddress() + ">";
620        } else {
621            env.from[0] = identityConfig.getEmailAddress();
622        }
623
624        String replyToAddress = identityConfig.getReplyToAddress();
625
626        if ((replyToAddress != null) && (replyToAddress.length() > 0)) {
627            env.replyTo = new String[1];
628            env.replyTo[0] = replyToAddress;
629        }
630
631        // Set the subject
632        env.subject = subjectEditField.getText();
633
634        // Set the date
635        env.date = Calendar.getInstance().getTime();
636       
637        return env;
638    }
639   
640    private void generateMessage(Runnable generatedRunnable) {
641        synchronized(generateLock) {
642                String contentText = messageEditField.getText();
643            MimeMessagePart bodyPart = MimeMessagePartFactory.createMimeMessagePart(
644                        "text", "plain", null, "7bit", "us-ascii", "", "", contentText.length());
645            MimeMessageContent bodyContent;
646            try {
647                        bodyContent = MimeMessageContentFactory.createContentEncoded(
648                                        bodyPart, contentText.getBytes());
649                } catch (UnsupportedContentException e) {
650                        bodyContent = null;
651                }
652           
653                if(attachmentsFieldManager != null) {
654                    MultiPart multiPart = new MultiPart("mixed");
655                    pendingMessage = new Message(multiPart);
656                    multiPart.addPart(bodyPart);
657                   
658                    int count = attachmentsFieldManager.getFieldCount();
659                    for(int i=0; i<count; i++) {
660                        AttachmentField attachmentField =
661                            (AttachmentField)attachmentsFieldManager.getField(i);
662                    ContentPart attachmentPart =
663                        attachmentField.getMessagePart();
664                    MimeMessageContent attachmentContent = null;
665                   
666                        MessageNode messageNode = attachmentField.getMessageNode();
667                        if(messageNode != null && messageNode == sourceMessageNode) {
668                            attachmentContent = messageNode.getMessageContent(attachmentPart);
669                            if(attachmentContent == null) {
670                                pendingRemoteAttachments.addElement(attachmentPart);
671                            }
672                        }
673                        else {
674                                String fileUrl = attachmentPart.getTag();
675                                if(fileUrl != null && fileUrl.startsWith("file:///")) {
676                                    pendingLocalAttachments.addElement(attachmentPart);
677                                }
678                        }
679                       
680                        if(attachmentContent != null) {
681                        multiPart.addPart(attachmentPart);
682                        pendingMessage.putContent(attachmentPart, attachmentContent);
683                        }
684                    }
685                }
686                else {
687                    pendingMessage = new Message(bodyPart);
688                }
689               
690                pendingMessage.putContent(bodyPart, bodyContent);
691           
692                boolean hasAllData = true;
693                if(pendingLocalAttachments.size() > 0) {
694                    hasAllData = false;
695                    (new Thread() { public void run() {
696                        handlePendingLocalAttachments();
697                    } }).start();
698                }
699                if(pendingRemoteAttachments.size() > 0) {
700                    hasAllData = false;
701                    sourceMessageNode.addMessageNodeListener(pendingAttachmentListener);
702                    int size = pendingRemoteAttachments.size();
703                    for(int i = 0; i < size; i++) {
704                        sourceMessageNode.requestContentPart((ContentPart)pendingRemoteAttachments.elementAt(i));
705                    }
706                }
707                if(hasAllData) {
708                    generatedRunnable.run();
709                pendingMessageRunnable = null;
710                pendingMessage = null;
711                }
712                else {
713                    pendingMessageRunnable = generatedRunnable;
714                }
715        }
716    }
717   
718    private void handlePendingLocalAttachments() {
719        synchronized(generateLock) {
720            if(pendingLocalAttachments.size() == 0) {
721                return;
722            }
723           
724            int size = pendingLocalAttachments.size();
725            for(int i=0; i<size; i++) {
726                ContentPart attachmentPart = (ContentPart)pendingLocalAttachments.elementAt(i);
727                String fileUrl = attachmentPart.getTag();
728                byte[] data;
729                try {
730                    FileConnection fileConnection = (FileConnection)Connector.open(fileUrl);
731                    DataInputStream input = fileConnection.openDataInputStream();
732   
733                    DataBuffer buf = new DataBuffer(1024, true);
734                    byte[] rawBuf = new byte[1024];
735                    int n;
736                    while((n = input.read(rawBuf, 0, 1024)) != -1) {
737                        buf.write(rawBuf, 0, n);
738                    }
739                    data = buf.toArray();
740                   
741                    input.close();
742                    fileConnection.close();
743                } catch (IOException e) {
744                    EventLogger.logEvent(AppInfo.GUID,
745                            ("Error: " + e.getMessage()).getBytes(),
746                            EventLogger.ERROR);
747                    data = null;
748                }
749               
750                if(data != null && data.length > 0) {
751                    try {
752                        MimeMessageContent attachmentContent =
753                            MimeMessageContentFactory.createContentRaw(attachmentPart, data);
754                        MultiPart multiPart = (MultiPart)pendingMessage.getStructure();
755                        multiPart.addPart(attachmentPart);
756                        pendingMessage.putContent(attachmentPart, attachmentContent);
757                    } catch (UnsupportedContentException e) { }
758                }
759            }
760            pendingLocalAttachments.removeAllElements();
761           
762            if(pendingLocalAttachments.size() == 0 && pendingRemoteAttachments.size() == 0) {
763                pendingMessageRunnable.run();
764                pendingMessageRunnable = null;
765                pendingMessage = null;
766            }
767        }
768    }
769   
770    private MessageNodeListener pendingAttachmentListener = new MessageNodeListener() {
771        public void messageStatusChanged(MessageNodeEvent e) {
772            if(e.getType() == MessageNodeEvent.TYPE_CONTENT_LOADED) {
773                synchronized(generateLock) {
774                    Vector loadedAttachments = new Vector();
775                    int size = pendingRemoteAttachments.size();
776                    for(int i=0; i<size; i++) {
777                        ContentPart attachmentPart = (ContentPart)pendingRemoteAttachments.elementAt(i);
778                        MimeMessageContent attachmentContent =
779                            sourceMessageNode.getMessageContent(attachmentPart);
780                        if(attachmentContent != null) {
781                            MultiPart multiPart = (MultiPart)pendingMessage.getStructure();
782                            multiPart.addPart(attachmentPart);
783                            pendingMessage.putContent(attachmentPart, attachmentContent);
784                            loadedAttachments.addElement(attachmentPart);
785                        }
786                    }
787                    size = loadedAttachments.size();
788                    for(int i=0; i<size; i++) {
789                        pendingRemoteAttachments.removeElement(loadedAttachments.elementAt(i));
790                    }
791                   
792                    if(pendingRemoteAttachments.size() == 0) {
793                        sourceMessageNode.removeMessageNodeListener(pendingAttachmentListener);
794                    }
795                   
796                    if(pendingLocalAttachments.size() == 0 && pendingRemoteAttachments.size() == 0) {
797                        pendingMessageRunnable.run();
798                        pendingMessageRunnable = null;
799                        pendingMessage = null;
800                    }
801                }
802            }
803        }
804    };
805   
806    private void sendMessage() {
807        final MessageEnvelope envelope = generateEnvelope();
808        generateMessage(new Runnable() {
809            public void run() {
810                if (replyToMessageNode != null) {
811                    if(composeType == COMPOSE_FORWARD) {
812                        accountNode.sendMessageForwarded(envelope, pendingMessage, replyToMessageNode);
813                    }
814                    else {
815                        accountNode.sendMessageReply(envelope, pendingMessage, replyToMessageNode);
816                    }
817                } else {
818                    accountNode.sendMessage(envelope, pendingMessage);
819                }
820               
821                messageSent = true;
822                synchronized(Application.getEventLock()) {
823                    screen.setDirty(false);
824                    screen.close();
825                }
826            }});
827    }
828
829    /**
830     * Insert a new recipient field.
831     * @param addressType The type of address this field will hold
832     * @return The newly added field
833     */
834    private EmailAddressBookEditField insertRecipientField(int addressType) {
835        int size = recipientsFieldManager.getFieldCount();
836        EmailAddressBookEditField currentField;
837        int i;
838
839        // If a field of this type already exists, and is empty, move
840        // focus there instead of adding a new field
841        for (i = 0; i < size; i++) {
842            currentField = (EmailAddressBookEditField) recipientsFieldManager.getField(i);
843
844            if ((currentField.getAddressType() == addressType) &&
845                    (currentField.getText().length() == 0)) {
846                currentField.setFocus();
847
848                return currentField;
849            }
850        }
851
852        // Otherwise, find the appropriate insertion point,
853        // and add a new field, and give it focus
854        if (addressType == EmailAddressBookEditField.ADDRESS_TO) {
855            for (i = 0; i < size; i++) {
856                currentField = (EmailAddressBookEditField) recipientsFieldManager.getField(i);
857
858                if (currentField.getAddressType() != EmailAddressBookEditField.ADDRESS_TO) {
859                    currentField = new EmailAddressBookEditField(EmailAddressBookEditField.ADDRESS_TO,
860                            "");
861                    recipientsFieldManager.insert(currentField, i);
862                    currentField.setFocus();
863
864                    return currentField;
865                }
866            }
867        } else if (addressType == EmailAddressBookEditField.ADDRESS_CC) {
868            i = 0;
869
870            while (i < size) {
871                currentField = (EmailAddressBookEditField) recipientsFieldManager.getField(i);
872
873                if ((currentField.getAddressType() == EmailAddressBookEditField.ADDRESS_TO) ||
874                        (currentField.getAddressType() == EmailAddressBookEditField.ADDRESS_CC)) {
875                    i++;
876                } else {
877                    currentField = new EmailAddressBookEditField(EmailAddressBookEditField.ADDRESS_CC,
878                            "");
879                    recipientsFieldManager.insert(currentField, i);
880                    currentField.setFocus();
881
882                    return currentField;
883                }
884            }
885        }
886
887        currentField = new EmailAddressBookEditField(addressType, "");
888        recipientsFieldManager.add(currentField);
889        currentField.setFocus();
890
891        return currentField;
892    }
893   
894    /**
895     * Attach a file to the current message.
896     */
897    private void attachFile() {
898        String fileUrl = ScreenFactory.getInstance().showFilePicker();
899        if(fileUrl != null) {
900            ContentPart attachmentPart = handleSelectedFile(fileUrl);
901            if(attachmentPart != null) {
902                if(attachmentsFieldManager == null) {
903                    attachmentsFieldManager = FieldFactory.getInstance().getBorderedFieldManager(
904                            BorderedFieldManager.BOTTOM_BORDER_NORMAL
905                            | BorderedFieldManager.OUTER_FILL_NONE);
906                    messageFieldManager.add(attachmentsFieldManager);
907                }
908                attachmentsFieldManager.add(new AttachmentField(null, attachmentPart));
909            }
910        }
911    }
912   
913    private ContentPart handleSelectedFile(String fileUrl) {
914        ContentPart mimeContentPart = null;
915        try {
916            FileConnection fileConnection = (FileConnection)Connector.open(fileUrl);
917            if(fileConnection.canRead()) {
918                String mimeType = MIMETypeAssociations.getMIMEType(fileUrl);
919                if(mimeType == null) {
920                    mimeType = "application/octet-stream";
921                }
922               
923                int p = mimeType.indexOf('/');
924                String mimeSubtype = mimeType.substring(p + 1);
925                mimeType = mimeType.substring(0, p);
926               
927                p = fileUrl.lastIndexOf('/');
928                String fileName = fileUrl.substring(p + 1);
929               
930                MimeMessagePart part =
931                    MimeMessagePartFactory.createMimeMessagePart(
932                            mimeType,
933                            mimeSubtype,
934                            fileName,
935                            null,         // encoding
936                            null,         // param
937                            "attachment", // disposition
938                            null,         // content ID
939                            (int)fileConnection.fileSize(),
940                            fileUrl);
941               
942                if(part instanceof ContentPart) {
943                    mimeContentPart = (ContentPart)part;
944                }
945            }
946            fileConnection.close();
947        } catch (IOException e) {
948            mimeContentPart = null;
949        }
950        return mimeContentPart;
951    }
952   
953    /**
954     * Run the Unicode normalizer on the provide string,
955     * only if normalization is enabled in the configuration.
956     * If normalization is disabled, this method returns
957     * the input unmodified.
958     *
959     * @param input Input string
960     * @return Normalized string
961     */
962    private String normalize(String input) {
963        if(unicodeNormalizer == null) {
964            return input;
965        }
966        else {
967            return unicodeNormalizer.normalize(input);
968        }
969    }
970}
Note: See TracBrowser for help on using the browser.