Wednesday, August 24, 2011

Android action_send multiple images

Android OS makes it really easy to share an image programmatically, using the ACTION_SEND intent. I had a situation recently, while porting my iBridalGown for iOS app to Android, where I wanted to send an email with multiple image attachments. As with most Android programming tasks, there were first a few snafus to overcome ;) A couple of the problems I was facing were:
  1. ACTION_SEND actually can only handle one image attachment.
  2. The images were saved in the "private" app directory (the one you get when you call getFilesDir()) , which is problematic when attaching images to emails this way.
  3. There are not many clients that respond to ACTION_SEND which can accommodate text/html content and an attachment, let alone multiple attachments
So, what did I do? Three problems, three solutions:
  1. I used the ACTION_SEND_MULTIPLE intent, in order to send multiple attachments.
  2. I made temporary copies of the images in external storage before attaching them to the intent.
  3. I made sure only Gmail would be launched, since it is the only app I found which can reliably handle sending text/html and multiple image attachments.
More details...


1. Here's my code to create the Intent with a subject and a message:

        Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND_MULTIPLE); 
        emailIntent.setType("text/html"); 
        String subject = "email subject here";
        emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);     
        String body = "<html><body>Here's some <b>HTML</b>content";
        emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, Html.fromHtml(body));

2. Next, I add some code that loops through the filenames of the images I want to send, copies them to external storage, then attaches them to the intent:
        emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);       
        ArrayList<Uri> uris = new ArrayList<Uri>();
        for (String fileName : imageFileNames)
        {
                String tempFilename = copyPhoto(fileName); //get a temp filename from the copy method
                tempFileNames.add(tempFilename); //stored so i can delete the temp files after the send
                File myFile = new File(Environment.getExternalStorageDirectory(),tempFilename);
                Uri fileUri = Uri.fromFile(myFile);
                uris.add(fileUri);
        }
        emailIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);

3. Now, some code to limit the app list to Gmail only:

        final PackageManager pm = getPackageManager();
        final List<ResolveInfo> matches = pm.queryIntentActivities(emailIntent, 0);
        ResolveInfo best = null;
        for (final ResolveInfo info : matches)
          if (info.activityInfo.packageName.endsWith(".gm") ||
              info.activityInfo.name.toLowerCase().contains("gmail")) best = info;
        if (best != null)
          emailIntent.setClassName(best.activityInfo.packageName, best.activityInfo.name);
   
And finally, we start the activity:

       startActivityForResult(ACTION_SEND_WITH_IMAGES);

And there's that. I hope it helps :) Please join in with your comments...

2 comments:

  1. What did you do about deleting the temporary file copies afterwards? One problem you will run into is that you don't have any idea when the email actually is finished being sent (for example, internet access may not be available until an hour later). Therefore, you need to decide whether to:

    1) Just leave the files lying around wasting space.

    or

    2) Delete the files at some point, risking the chance that the email has not yet been sent.

    ReplyDelete
  2. Excellent question. I stored all the temp filenames (above in the tempFileNames array), and then called another method called resetTempFiles() to delete them when the app was terminating. I also called a method on app launch to cleanup any old temp files.

    In other words, you are absolutely right there is no callback to tell me whether the email was actually sent... i did a combination of your options #1 and #2, I held onto them for a little while wasting space, then deleted the files risking the chance that the email had not yet been sent, hopefully my timing covered the actual use cases ok ;)

    ReplyDelete