Helper class to send emails with attachments on Android I use this EmailTools helper class inside an Android app to send data as an email attachment, by calling send(context, deviceName, recordedContent), where the parameters are:


context: an instance of Context, in order to:


create Intent.ACTION_SEND for launching an existing email app to send emails
get elements from string.xml to format the subject
get the app's version to include in the message body

deviceName: a String, the name of the device where the data is coming from.
recordedContent: a String, the recorded data, to send as attachment.


Is there a better way? How would you improve this?

public abstract class EmailTools {

    private static final String TAG = EmailTools.class.getSimpleName();

    private static final String MESSAGE_TYPE = "message/rfc822";

    public static void send(Context context, String deviceName, String recordedContent) {
        String subject = String.format(context.getString(R.string.fmt_subject_recorded_data), deviceName);
        String message = String.format(context.getString(R.string.fmt_recorded_from), deviceName);
        message += getPackageInfo(context);

        Intent intent = new Intent(Intent.ACTION_SEND);
        intent.setType(MESSAGE_TYPE);
        intent.putExtra(Intent.EXTRA_SUBJECT, subject);
        intent.putExtra(Intent.EXTRA_TEXT, message);
        addAttachmentToIntent(context, deviceName, recordedContent, intent);
        launchEmailApp(context, intent);
    }

    private static String getPackageInfo(Context context) {
        String packageName = context.getPackageName();
        PackageManager manager = context.getPackageManager();
        try {
            PackageInfo info;
            if (manager != null) {
                info = manager.getPackageInfo(packageName, 0);
                if (info != null) {
                    return String.format("\n\n--\n[App: %s Version: %d/%s]",
                            packageName, info.versionCode, info.versionName);
                }
            }
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "Could not get package info", e);
        }
        return "";
    }

    private static void addAttachmentToIntent(Context context, String deviceName, String recordedContent, Intent intent) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("_yyyyMMdd_HHmm");
        String filename = deviceName + dateFormat.format(new Date()) + ".dat";
        try {
            FileOutputStream ostream = context.openFileOutput(filename, Context.MODE_WORLD_READABLE);
            ostream.write(recordedContent.getBytes());
            ostream.close();
            File attachment = context.getFileStreamPath(filename);
            intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(attachment));
        } catch (IOException e) {
            Log.e(TAG, "could not create temp file for attachment :(", e);
        }
    }

    private static void launchEmailApp(Context context, Intent intent) {
        try {
            context.startActivity(Intent.createChooser(intent, context.getString(R.string.email_client_chooser)));
        } catch (ActivityNotFoundException ex) {
            Toast.makeText(context, context.getString(R.string.no_email_client), Toast.LENGTH_SHORT).show();
        }
    }
}
I'd change a few things to make your code cleaner.

Your send method does three different things: it extract the message from the context, it creates the intent and it finally launches the email app. What about introducing a Message class, a Message decodeMessage(Context context, String deviceName), and an Intent createIntent(Message message) methods?

getPackageInfo is definitely to arrow-shaped. I'd refactor it to separate the logic of creation of the package info string String formatPackageInfo(PackageInfo packageInfo) and the code to retrieve the PackageInfo instance.

private PackageInfo getPackageInfo(){
    String packageName = context.getPackageName();
    PackageManager manager = context.getPackageManager();
    if (manager == null)
        return null;
    try
    {
        return manager.getPackageInfo(packageName, 0);
    }
    catch (PackageManager.NameNotFoundException e)
    {
        Log.e(TAG, "Could not get package info", e);
    }
    return null;
}

private static String formatPackageInfo(PackageInfo packageInfo) {
        if(packageInfo == null)
            return "";
        return String.format("\n\n--\n[App: %s Version: %d/%s]",
                        packageName, info.versionCode, info.versionName);
}


In addAttachmentToIntent I'd probably move the declaration of dateFormat and of filename in the try block. You're not going to use them outside that scope so it could be a good idea to give them the smallest scope possible.