Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 58ca9fe7 authored by cketti's avatar cketti
Browse files

Changed external intent (e.g. Intent.ACTION_SEND_TO) handling code in MessageCompose

- extracted handling of external intents to a new method
- only allow mailto URI for ACTION_VIEW and ACTION_SENDTO (as documented by AOSP)
- allow additional recipients via "to" parameter in mailto URI
- removed check for allowed MIME type when adding attachments since we're accepting all MIME types anyway
- show CC and BCC text fields when one of those recipient types was set by the intent
- use MIME type specified in intent for attachments (if present and doesn't contain a "*")
parent 914a462a
Loading
Loading
Loading
Loading
+0 −12
Original line number Diff line number Diff line
@@ -176,18 +176,6 @@ public class K9 extends Application
     */
    private static final Method mGetBlockNetworkLoads = getMethod(WebSettings.class, "setBlockNetworkLoads");


    /**
     * The MIME type(s) of attachments we're willing to send. At the moment it is not possible
     * to open a chooser with a list of filter types, so the chooser is only opened with the first
     * item in the list. The entire list will be used to filter down attachments that are added
     * with Intent.ACTION_SEND.
     */
    public static final String[] ACCEPTABLE_ATTACHMENT_SEND_TYPES = new String[]
    {
        "*/*"
    };

    /**
     * The MIME type(s) of attachments we're willing to view.
     */
+195 −227
Original line number Diff line number Diff line
@@ -3,9 +3,8 @@ package com.fsck.k9.activity;

import java.io.File;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.StringTokenizer;
@@ -456,145 +455,8 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
        }


        String action = intent.getAction();

        if (Intent.ACTION_VIEW.equals(action))
        {
            /*
             * Someone has clicked a mailto: link. The address is in the URI.
             */
            if (intent.getData() != null)
            {
                Uri uri = intent.getData();
                if ("mailto".equals(uri.getScheme()))
                {
                    initializeFromMailTo(uri.toString());
                }
                else
                {
                    String toText = uri.getSchemeSpecificPart();
                    if (toText != null)
                    {
                        mToView.setText(toText);
                    }
                }
            }
        }
        else if (Intent.ACTION_SENDTO.equals(action))
        {
            final Uri uri = intent.getData();

            // look for recipient address
            if (uri != null && "mailto".equals(uri.getScheme()))
            {
                initializeFromMailTo(uri.toString());
            }
        }
        //TODO: Use constant Intent.ACTION_SEND_MULTIPLE once we drop Android 1.5 support
        else if (Intent.ACTION_SEND.equals(action)
                 || "android.intent.action.SEND_MULTIPLE".equals(action))
        {
            /*
             * Someone is trying to compose an email with an attachment, probably Pictures.
             * The Intent should contain an EXTRA_STREAM with the data to attach.
             */

            String text = intent.getStringExtra(Intent.EXTRA_TEXT);
            if (text != null)
            {
                mMessageContentView.setText(text);
            }
            String subject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
            if (subject != null)
            {
                mSubjectView.setText(subject);
            }

            String type = intent.getType();
            //TODO: Use constant Intent.ACTION_SEND_MULTIPLE once we drop Android 1.5 support
            if ("android.intent.action.SEND_MULTIPLE".equals(action))
            {
                ArrayList<Parcelable> list = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
                if (list != null)
                {
                    for (Parcelable parcelable : list)
                    {
                        Uri stream = (Uri) parcelable;
                        if (stream != null && type != null)
                        {
                            if (MimeUtility.mimeTypeMatches(type, K9.ACCEPTABLE_ATTACHMENT_SEND_TYPES))
                            {
                                addAttachment(stream);
                            }
                        }
                    }
                }
            }
            else
            {
                Uri stream = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
                if (stream != null && type != null)
                {
                    if (MimeUtility.mimeTypeMatches(type, K9.ACCEPTABLE_ATTACHMENT_SEND_TYPES))
                    {
                        addAttachment(stream);
                    }
                }
            }

            /*
             * There might be an EXTRA_SUBJECT, EXTRA_TEXT, EXTRA_EMAIL, EXTRA_BCC or EXTRA_CC
             */

            String extraSubject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
            String extraText = intent.getStringExtra(Intent.EXTRA_TEXT);

            mSubjectView.setText(extraSubject);
            mMessageContentView.setText(extraText);

            String[] extraEmail = intent.getStringArrayExtra(Intent.EXTRA_EMAIL);
            String[] extraCc = intent.getStringArrayExtra(Intent.EXTRA_CC);
            String[] extraBcc = intent.getStringArrayExtra(Intent.EXTRA_BCC);

            String addressList;
            // Cache array size, as per Google's recommendations.
            int arraySize;
            int i;

            addressList = "";
            if (extraEmail != null)
            {
                arraySize = extraEmail.length;
                for (i=0; i < arraySize; i++)
                {
                    addressList += extraEmail[i]+", ";
                }
            }
            mToView.setText(addressList);

            addressList = "";
            if (extraCc != null)
            {
                arraySize = extraCc.length;
                for (i=0; i < arraySize; i++)
                {
                    addressList += extraCc[i]+", ";
                }
            }
            mCcView.setText(addressList);

            addressList = "";
            if (extraBcc != null)
            {
                arraySize = extraBcc.length;
                for (i=0; i < arraySize; i++)
                {
                    addressList += extraBcc[i]+", ";
                }
            }
            mBccView.setText(addressList);

        }
        final String action = intent.getAction();
        initFromIntent(intent);

        if (mIdentity == null)
        {
@@ -740,6 +602,130 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
        mDraftNeedsSaving = false;
    }

    /**
     * Handle external intents that trigger the message compose activity.
     *
     * @param intent The (external) intent that started the activity.
     */
    private void initFromIntent(final Intent intent)
    {
        final String action = intent.getAction();

        if (Intent.ACTION_VIEW.equals(action) || Intent.ACTION_SENDTO.equals(action))
        {
            /*
             * Someone has clicked a mailto: link. The address is in the URI.
             */
            if (intent.getData() != null)
            {
                Uri uri = intent.getData();
                if ("mailto".equals(uri.getScheme()))
                {
                    initializeFromMailto(uri);
                }
            }

            /*
             * Note: According to the documenation ACTION_VIEW and ACTION_SENDTO
             * don't accept EXTRA_* parameters. Contrary to the AOSP Email application
             * we don't accept those EXTRAs.
             * Dear developer, if your application is using those EXTRAs you're doing
             * it wrong! So go fix your program or get AOSP to change the documentation.
             */
        }
        //TODO: Use constant Intent.ACTION_SEND_MULTIPLE once we drop Android 1.5 support
        else if (Intent.ACTION_SEND.equals(action) ||
                 "android.intent.action.SEND_MULTIPLE".equals(action))
        {
            /*
             * Note: Here we allow a slight deviation from the documentated behavior.
             * EXTRA_TEXT is used as message body (if available) regardless of the MIME
             * type of the intent. In addition one or multiple attachments can be added
             * using EXTRA_STREAM.
             */
            CharSequence text = intent.getCharSequenceExtra(Intent.EXTRA_TEXT);
            if (text != null)
            {
                mMessageContentView.setText(text);
            }

            String type = intent.getType();
            if (Intent.ACTION_SEND.equals(action))
            {
                Uri stream = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
                if (stream != null)
                {
                    addAttachment(stream, type);
                }
            }
            else
            {
                ArrayList<Parcelable> list = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
                if (list != null)
                {
                    for (Parcelable parcelable : list)
                    {
                        Uri stream = (Uri) parcelable;
                        if (stream != null)
                        {
                            addAttachment(stream, type);
                        }
                    }
                }
            }

            String subject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
            if (subject != null)
            {
                mSubjectView.setText(subject);
            }

            String[] extraEmail = intent.getStringArrayExtra(Intent.EXTRA_EMAIL);
            String[] extraCc = intent.getStringArrayExtra(Intent.EXTRA_CC);
            String[] extraBcc = intent.getStringArrayExtra(Intent.EXTRA_BCC);

            if (extraEmail != null)
            {
                setRecipients(mToView, Arrays.asList(extraEmail));
            }

            boolean ccOrBcc = false;
            if (extraCc != null)
            {
                ccOrBcc |= setRecipients(mCcView, Arrays.asList(extraCc));
            }

            if (extraBcc != null)
            {
                ccOrBcc |= setRecipients(mBccView, Arrays.asList(extraBcc));
            }

            if (ccOrBcc)
            {
                // Display CC and BCC text fields if CC or BCC recipients were set by the intent.
                onAddCcBcc();
            }
        }
    }

    private boolean setRecipients(TextView view, List<String> recipients)
    {
        boolean recipientAdded = false;
        if (recipients != null)
        {
            StringBuffer addressList = new StringBuffer();
            for (String recipient : recipients)
            {
                addressList.append(recipient);
                addressList.append(", ");
                recipientAdded = true;
            }
            view.setText(addressList);
        }

        return recipientAdded;
    }

    private void initializeCrypto()
    {
        if (mPgpData != null)
@@ -1228,7 +1214,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
            }
        }

        onAddAttachment2(K9.ACCEPTABLE_ATTACHMENT_SEND_TYPES[0]);
        onAddAttachment2("*/*");
    }

    /**
@@ -1248,36 +1234,31 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc

    private void addAttachment(Uri uri)
    {
        addAttachment(uri, -1, null);
        addAttachment(uri, null);
    }

    private void addAttachment(Uri uri, int size, String name)
    private void addAttachment(Uri uri, String contentType)
    {
        long size = -1;
        String name = null;

        ContentResolver contentResolver = getContentResolver();

        Attachment attachment = new Attachment();
        attachment.name = name;
        attachment.size = size;
        attachment.uri = uri;
        Cursor metadataCursor = contentResolver.query(
                                    uri,
                                    new String[] { OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE },
                                    null,
                                    null,
                                    null);

        if (attachment.size == -1 || attachment.name == null)
        {
            Cursor metadataCursor = contentResolver.query(uri, new String[] { OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE }, null, null, null);
        if (metadataCursor != null)
        {
            try
            {
                if (metadataCursor.moveToFirst())
                {
                        if (attachment.name == null)
                        {
                            attachment.name = metadataCursor.getString(0);
                        }
                        if (attachment.size == -1)
                        {
                            attachment.size = metadataCursor.getInt(1);
                            Log.v(K9.LOG_TAG, "size: " + attachment.size);
                        }
                    name = metadataCursor.getString(0);
                    size = metadataCursor.getInt(1);
                }
            }
            finally
@@ -1285,30 +1266,29 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
                metadataCursor.close();
            }
        }
        }

        if (attachment.name == null)
        if (name == null)
        {
            attachment.name = uri.getLastPathSegment();
            name = uri.getLastPathSegment();
        }

        String contentType = contentResolver.getType(uri);

        if ((contentType == null) || (contentType.indexOf('*') != -1))
        {
            contentType = contentResolver.getType(uri);
        }
        if (contentType == null)
        {
            contentType = MimeUtility.getMimeTypeByExtension(attachment.name);
            contentType = MimeUtility.getMimeTypeByExtension(name);
        }

        attachment.contentType = contentType;

        if (attachment.size<=0)
        if (size <= 0)
        {
            String uriString = uri.toString();
            if (uriString.startsWith("file://"))
            {
                Log.v(K9.LOG_TAG, uriString.substring("file://".length()));
                File f = new File(uriString.substring("file://".length()));
                attachment.size = f.length();
                size = f.length();
            }
            else
            {
@@ -1317,15 +1297,16 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
        }
        else
        {
            Log.v(K9.LOG_TAG, "old attachment.size: " + attachment.size);
            Log.v(K9.LOG_TAG, "old attachment.size: " + size);
        }
        Log.v(K9.LOG_TAG, "new attachment.size: " + attachment.size);
        addAttachmentView(attachment);
        Log.v(K9.LOG_TAG, "new attachment.size: " + size);

    }
        Attachment attachment = new Attachment();
        attachment.uri = uri;
        attachment.contentType = contentType;
        attachment.name = name;
        attachment.size = size;

    private void addAttachmentView(Attachment attachment)
    {
        View view = getLayoutInflater().inflate(R.layout.message_compose_attachment, mAttachments, false);
        TextView nameView = (TextView)view.findViewById(R.id.attachment_name);
        ImageButton delete = (ImageButton)view.findViewById(R.id.attachment_delete);
@@ -2166,72 +2147,59 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
        }
    }

    private String decode(String s)
    throws UnsupportedEncodingException
    {
        return URLDecoder.decode(s, "UTF-8");
    }

    /**
     * When we are launched with an intent that includes a mailto: URI, we can actually
     * gather quite a few of our message fields from it.
     *
     * @mailToString the href (which must start with "mailto:").
     */
    private void initializeFromMailTo(String mailToString)
    private void initializeFromMailto(Uri mailtoUri)
    {

        // Chop up everything between mailto: and ? to find recipients
        int index = mailToString.indexOf("?");
        int length = "mailto".length() + 1;
        String to;
        try
        String schemaSpecific = mailtoUri.getSchemeSpecificPart();
        int end = schemaSpecific.indexOf('?');
        if (end == -1)
        {
            // Extract the recipient after mailto:
            if (index == -1)
            {
                to = decode(mailToString.substring(length));
            }
            else
            {
                to = decode(mailToString.substring(length, index));
            }
            mToView.setText(to);
        }
        catch (UnsupportedEncodingException e)
        {
            Log.e(K9.LOG_TAG, e.getMessage() + " while decoding '" + mailToString + "'");
            end = schemaSpecific.length();
        }

        // Extract the other parameters
        // Extract the recipient's email address from the mailto URI if there's one.
        String recipient = Uri.decode(schemaSpecific.substring(0, end));

        // We need to disguise this string as a URI in order to parse it
        Uri uri = Uri.parse("foo://" + mailToString);

        String addressList;
        /*
         * mailto URIs are not hierarchical. So calling getQueryParameters()
         * will throw an UnsupportedOperationException. We avoid this by
         * creating a new hierarchical dummy Uri object with the query
         * parameters of the original URI.
         */
        Uri uri = Uri.parse("foo://bar?" + mailtoUri.getEncodedQuery());

        addressList = "";
        List<String> cc = uri.getQueryParameters("cc");
        for (String address : cc)
        // Read additional recipients from the "to" parameter.
        List<String> to = uri.getQueryParameters("to");
        if (recipient.length() != 0)
        {
            addressList += address + ",";
            to = new ArrayList<String>(to);
            to.add(0, recipient);
        }
        mCcView.setText(addressList);
        setRecipients(mToView, to);

        addressList = "";
        List<String> bcc = uri.getQueryParameters("bcc");
        for (String address : bcc)
        // Read carbon copy recipients from the "cc" parameter.
        boolean ccOrBcc = setRecipients(mCcView, uri.getQueryParameters("cc"));

        // Read blind carbon copy recipients from the "bcc" parameter.
        ccOrBcc |= setRecipients(mBccView, uri.getQueryParameters("bcc"));

        if (ccOrBcc)
        {
            addressList += address + ",";
            // Display CC and BCC text fields if CC or BCC recipients were set by the intent.
            onAddCcBcc();
        }
        mBccView.setText(addressList);

        // Read subject from the "subject" parameter.
        List<String> subject = uri.getQueryParameters("subject");
        if (subject.size() > 0)
        {
            mSubjectView.setText(subject.get(0));
        }

        // Read message body from the "body" parameter.
        List<String> body = uri.getQueryParameters("body");
        if (body.size() > 0)
        {