简体   繁体   中英

Best way to display an email (JavaMail Message) in android

I am building an android email client app. I managed to display the emails headers/senders in a RecylerView. Next, I want to start a new activity that will display the email content/attachments when the user chooses a specific mail.

I have a hard time doing this through intents. I am able to get the contents of the email (text, inline images, attachments) but I can't figure out a way to display them as close to the original format as possible. I thought of putting the text in a StringBuilder and sending it through an intent in order to display the text, but this way I can't display the inline images in the right place and there are also formatting problems.

Any kind of guidance towards the way I should approach this is much appreciated.

The class that displays the list of the available mails and gets the content of the specific mail to send it another activity for displaying it. I know the code is a little hazardous, I tried many approaches and it is far from the final form.

public class CheckMail extends Activity {


static List<Message> messages = new ArrayList<>();
String[] sender;
String[] date;
String[] subject;
boolean[] seen;
Context context = null;
ListView listView;
Intent intent;
Store store;
StringBuilder content = new StringBuilder();

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_check_mail);
    if (android.os.Build.VERSION.SDK_INT > 9)
    {
        StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
        StrictMode.setThreadPolicy(policy);
    }

    context = this;


    ReadEmails task = new ReadEmails();
    task.execute();

}

public void writePart(Part p) throws Exception {
    if (p instanceof Message)
        this.writeEnvelope((Message) p);



    //check if the content is plain text
    if (p.isMimeType("text/plain")) {

      content.append(p.getContent().toString());


    }

    //check if the content has attachment
    else if (p.isMimeType("multipart/*")) {
        System.out.println("This is a Multipart");
        System.out.println("---------------------------");
        Multipart mp = (Multipart) p.getContent();
        int count = mp.getCount();
        for (int i = 0; i < count; i++)
            writePart(mp.getBodyPart(i));
    }

     //check if the content is a nested message
    else if (p.isMimeType("message/rfc822")) {
        System.out.println("This is a Nested Message");
        System.out.println("---------------------------");
        writePart((Part) p.getContent());
    }


    /*
    //check if the content is an inline image
    else if (p.isMimeType("image/jpeg")) {
        System.out.println("--------> image/jpeg");
        Object o = p.getContent();

        InputStream x = (InputStream) o;
        // Construct the required byte array
        System.out.println("x.length = " + x.available());
        while ((i = (int) ((InputStream) x).available()) > 0) {
            int result = (int) (((InputStream) x).read(bArray));
            if (result == -1)
                int i = 0;
            byte[] bArray = new byte[x.available()];

            break;
        }
        FileOutputStream f2 = new FileOutputStream("/tmp/image.jpg");
        f2.write(bArray);
    }
    else if (p.getContentType().contains("image/")) {
        System.out.println("content type" + p.getContentType());
        File f = new File("image" + new Date().getTime() + ".jpg");
        DataOutputStream output = new DataOutputStream(
                new BufferedOutputStream(new FileOutputStream(f)));
        com.sun.mail.util.BASE64DecoderStream test =
                (com.sun.mail.util.BASE64DecoderStream) p
                        .getContent();
        byte[] buffer = new byte[1024];
        int bytesRead;
        while ((bytesRead = test.read(buffer)) != -1) {
            output.write(buffer, 0, bytesRead);
        }
    }
    else {
        Object o = p.getContent();
        if (o instanceof String) {
            System.out.println("This is a string");
            System.out.println("---------------------------");
            System.out.println((String) o);
        }
        else if (o instanceof InputStream) {
            System.out.println("This is just an input stream");
            System.out.println("---------------------------");
            InputStream is = (InputStream) o;
            is = (InputStream) o;
            int c;
            while ((c = is.read()) != -1)
                System.out.write(c);
        }
        else {
            System.out.println("This is an unknown type");
            System.out.println("---------------------------");
            System.out.println(o.toString());
        }
    }

*/

}

public void writeEnvelope(Message m) throws Exception {
    System.out.println("This is the message envelope");
    System.out.println("---------------------------");
    Address[] a;
    StringBuilder sender = new StringBuilder();
    StringBuilder recipients = new StringBuilder();
    String subject = "";

    // FROM
    if ((a = m.getFrom()) != null) {
        for (int j = 0; j < a.length; j++)
            sender.append(a[j].toString());
    }

    // TO
    if ((a = m.getRecipients(Message.RecipientType.TO)) != null) {
        for (int j = 0; j < a.length; j++)
            recipients.append(a[j].toString());
    }

    // SUBJECT
    if (m.getSubject() != null)
        subject = m.getSubject();



    intent.putExtra("Sender", sender.toString());
    intent.putExtra("Recipients", recipients.toString());
    intent.putExtra("Message", subject);
    intent.putExtra("Date", m.getReceivedDate().toString());


}



class ReadEmails extends AsyncTask<String, Void, String> {


    @Override
    protected String doInBackground(String... params) {


        // Create all the needed properties - empty!
        Properties connectionProperties = new Properties();
        // Create the session
        Session session = Session.getDefaultInstance(connectionProperties, null);

        try {
            System.out.print("Connecting to the IMAP server...");
            // Connecting to the server
            // Set the store depending on the parameter flag value
            store = session.getStore("imaps");

            // Set the server depending on the parameter flag value
            String server = "imap.gmail.com";
            store.connect(server, "....@gmail.com", "password");

            System.out.println("done!");

            // Get the Inbox folder
            Folder inbox = store.getFolder("Inbox");

            // Set the mode to the read-only mode
            inbox.open(Folder.READ_ONLY);

            // Get messages
            CheckMail.messages = Arrays.asList(inbox.getMessages());

            System.out.println("Reading messages...");

             sender = new String[messages.size()];
             date = new String[messages.size()];
             subject = new String[messages.size()];
             seen = new boolean[messages.size()];

            for (int i = 0; i < messages.size(); i++) {
                try {
                    Address[] froms = messages.get(i).getFrom();
                    String email = froms == null ? null : ((InternetAddress) froms[0]).getAddress();

                    sender[i] = email;
                    date[i] = messages.get(i).getReceivedDate().toString();
                    subject[i] = messages.get(i).getSubject();


                    Flags flags = messages.get(i).getFlags();
                    Flags.Flag[] sf = flags.getSystemFlags();
                    for (int j = 0; j < sf.length; j++) {
                        if (sf[j] == Flags.Flag.SEEN)
                            seen[i] = true;
                    else
                    seen[i] = false;
                    }


                } catch (MessagingException e) {
                    e.printStackTrace();
                }
            }

            System.out.println("Done reading...");


        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    @Override
    protected void onPostExecute(String result) {

        CustomListAdapter whatever = new CustomListAdapter((Activity) context, sender, date, subject, seen);
        listView = (ListView) findViewById(R.id.listviewID);
        listView.setAdapter(whatever);


        listView.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position,
                                    long id) {

                try {

                    content.delete(0, content.length());
                    intent = new Intent(context, OpenMail.class);
                    writePart(messages.get(position));
                    intent.putExtra("Content", content.toString());
                    startActivity(intent);

                }
                catch (Exception e)
                {e.printStackTrace();}
            }
        });

    }





}
}

Since no one answered...I display emails via Javamail in some of my apps.

I think you're on the right track, having separate activities for the list and viewer is a good approach. [Or separate fragments because on a tablet you may want to display the list and the body on the same screen, side by side]

Couple issues that might come up:

  1. I would be cautious about putting the email content in an extra to start the activity and/or committing it to saved instance state in the viewer activity because there are size limits [For example, 1MB for saved instance state on Android 7+]

  2. Downloading the email in a ASyncTask in the activity might not be the best approach. I don't know the full purpose of the app, but I assume that is something that should succeed whether the user waits or not? The ASyncTask will continue to run if they task away, but it will hold on to the activity context causing a so called 'temporary memory leak'. It is probably best to put it in a service and download it in a separate thread. However, doing it in the activity as a proof of concept is perfectly reasonable...

  3. I don't think that walking the message structure in the email list activity is the best approach. In my apps, I download email in a background service and commit the data to an SQL-DB via a ContentProvider. On the message viewer screen the email body is retrieved from the ContentProvider/SQL-DB using a component called the CursorLoader. It handles all the loading in the background so that the UI remains responsive whilst loading large mails. But in any event, I avoid passing the message body between activities.

  4. A lot of emails have HTML parts (multipart/alternative: text/plain & text/html) so the viewer was implemented as a WebView. The WebView produced good looking emails with minimal effort.

  5. Couple miscellaneous gotchas, when retrieving the mail, take care to call setPeek(true). It will stop the the READ flag being set. It is not an issue for GMail, but some IMAP servers will set this flag. Users will complain if any app other than their primary email app changes the READ flag. Also don't assume that any of the headers are present, SPAM emails are notorious for leaving out the message ID, subject and other fields. Finally, it might be worth considering implementing authentication via OAuth2 which will enable your app to connect to a user's GMail account via Javamail without needing their password.

I'm not sure that any of that really helps because it is a pretty big job, but one step at a time...Cheers!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM