- Home /
The question is answered, right answer was accepted
How to attach a file to an email on android (using intent and C#)?
Hey there!
I've spent the last few days researching and trying some code to make my unity player send an email with a file attached to it. The player is integrated in an android app (which wasn't too easy to accomplish either, but finally managed to get it working) and needs to send an email when a gui button (within unity player) is pressed.
I've tried with the OpenURL option, but as far as I know (and tried), it doesn't allow attachments:
string escapedTo = WWW.EscapeURL("example@example-domain.com");
string escapedSubject = WWW.EscapeURL("Mail subject");
Application.OpenURL("mailto:" + escapedTo + "?subject=" + escapedSubject);
The code above opens the default email app, ready to send the mail, but it doesn't allow attachments as parameters.
I have also read about the posibility of using smtp, but that's not what I want. What I need is to call the activity to send the email, with the file I want to attach passed to the activity as a parameter (pretty much the same as with the openURL, which opens the android default mail activity).
I've read that I could use intent, but since my knowledge about android is way too limited, I have only gotten this far (mailTo, mailSubject and mailBody being parameters previously initialised):
AndroidJavaClass jc = new AndroidJavaClass("com.exampleCompany.exampleApp.exampleActivity");
AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject objIntent = new AndroidJavaObject("android.intent.action.SEND");
objIntent.Set<string>("EXTRA_EMAIL", mailTo);
objIntent.Set<string>("EXTRA_SUBJECT", mailSubject);
objIntent.Set<string>("EXTRA_TEXT", mailBody);
jo.Call("sendBroadcast", objIntent);
(Note: in my code I use real package names) I am obviously doing something wrong because I get nothing from that code. So, my question is: Is there a way to open the default android email activity passing a file to attach as a parameter?
Thanks in advance.
[Edit]: Typos
[Edit]: Improved the title of the question to make it more accurate.
Already changed it, so the main activity will use one function. This goes into my c# in unity:
AndroidJavaClass jc = new AndroidJavaClass("com.exampleCompany.exampleApp.UnityPlayerNativeActivity");
jc.Call("send$$anonymous$$ail");
And this goes in the UnityPlayerNativeActivity.java:
public void send$$anonymous$$ail() {
try {
final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("plain/text");
this.startActivity(Intent.createChooser(emailIntent, "Sending email..."));
} catch (Throwable t) {
Toast.makeText(this, "Request failed try again: " + t.toString(), Toast.LENGTH_LONG).show();
}
}
I just need to pass on the parameters (the path to the filie to attach being the most important).
Answer by Andres-Fernandez · Jun 13, 2014 at 07:34 AM
Ok, finally got it working. It took me this whole week (and a lot of research in Unity Answers and Stack Overflow), mainly because I had never programmed anything for android devices. There are a couple steps to do, and it can be a bit messy if you know nothing about android (as I did) but it is not as hard as it seems. The steps I followed are:
Copy the file in the StreamingAssets folder.
Get the reference to the android activity and call the method.
Copy the file to external storage.
Create the intent and start the activity.
I'll explain those steps right now and try to tell why they should be done. Just note that my unity player is integrated in the android app (thus all these steps, except the first one, are required).
Step 1: Copy the file in the StreamingAssets folder (skippable).
In my case, I had to attach a png file to the email, and although the docs doesn't give a long explanation of how it works, If you need to pass any file without converting it to an asset or a bundle, the StreamingAssets folder is the way to do it. Why? Because it creates a copy of the file in the assets folder of the android app once you export the unity player (just open Eclipse and your file will be there).
Note: I did this because I needed to pass an already created file. If you are generating the file by code you can skip this step.
Step 2: Get the reference to the activity and call the method.
When you build your unity player and check the Google Android Project option, Unity creates an activity called UnityPlayerNativeActivity, which will be the main activity of the app. To communicate with that activity, you need a reference to the AndroidJavaClass and AndroidJavaObject to call its methods (as you can check in many places, just google it). The code to do that goes inside the unity player (in any object) and is very easy (I use it in a function that gets called when I click on a "send mail" button created with NGUI):
AndroidJavaClass jc = new AndroidJavaClass("com.exampleCompany.exampleApp.UnityPlayerNativeActivity");
AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("context");
jo.Call("sendMail", new object[] { mailTo, mailSubject, mailBody, mailAttachmentPath });
That's all the code you need inside unity (the rest goes inside the UnityPlayerNativeActivity activity code). context is a variable that will hold a reference to the context of the activity. sendMail is the name of the function that will create the intent and mailTo, mailSubject, mailBody and mailAttachment are strings that hold the parameters of the function (mailAttachment is the name of the object "image.png").
Now we jump to Eclipse. Inside your UnityPlayerNativeActivity.java you will need all those things, so te class should include the context:
public class UnityPlayerNativeActivity extends Activity {
protected UnityPlayer mUnityPlayer; // don't change the name of this variable; referenced from native code
// Other variables
// You need the static reference to this object:
public static Context context;
// UnityPlayer.init() should be called before attaching the view to a layout - it will load the native code.
// UnityPlayer.quit() should be the last thing called - it will unload the native code.
protected void onCreate (Bundle savedInstanceState)
{
// Other stuff previous to onCreate
super.onCreate(savedInstanceState);
context = this; // Needs to go after super.OnCreate
// Rest of stuff
...
}
// Other functions
}
This way you have the context ready to be accesed by the androidJavaObject inside Unity. The function sendMail also goes inside this UnityPlayerNativeActivity class, and is explained in the following steps.
Steps 3 and 4. Copy the file to external storage, create the intent and start the activity:
Now we need to add the sendMail function. It will create the intent, copy the file to external storage and attach it to the intent. Finally, it will start the activity.
To attach everything to the email (address, subject and attachment) we need to use the intent.putExtra function. And it is important to use a MIME type that can handle the type of file we want to attach to the email (I used "image/*" since my file is a png).
Why do we have to copy the file to the external storage? Because android apps can't access files from other apps unless they are stored in external storage or shared by a content provider. Since my experiments with the content provider and file provider got me nowhere, I decided that external storage was the way to go.
To attach the file to the mail, you need the Uri that can be obtained from the external storage file through Uri.fromFile function.
The sendMail function (that goes inside UnityPlayerNativeActivity.java class) should look like this:
public void sendMail(String address, String subject, String message, String filePath) {
try {
// Create the intent
final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
// add the address, subject and body of the mail
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[] { address });
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message);
// set the MIME type and grant access to the uri (for the attached file, although I'm not sure if the grant access is required)
emailIntent.setType("image/*");
emailIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// Copy file to external storage
InputStream in = null;
OutputStream out = null;
try {
in = getAssets().open(filePath);
out = new FileOutputStream(new File(Environment.getExternalStorageDirectory(), filePath));
byte buf[] = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
in = null;
out.flush();
out.close();
out = null;
} catch (Exception e) {
Log.e("", e.getMessage());
}
// Get the Uri from the external file and add it to the intent
Uri uri = Uri.fromFile(new File(Environment.getExternalStorageDirectory(), filePath));
emailIntent.putExtra(Intent.EXTRA_STREAM, uri);
// finally start the activity
this.startActivity(Intent.createChooser(emailIntent, "Sending email..."));
} catch (Throwable t) {
Toast.makeText(this, "Request failed try again: " + t.toString(), Toast.LENGTH_LONG).show();
}
}
What does it do? It gathers the parameters from the call function inside the unity player and uses them to attach all that is required to the email intent. Then it streams the file to external storage and gets an uri from it (to add it to the intent). When it starts the activity, an activity chooser launches, showing apps that can handle images and email. If the user chooses an email app, the email will show with all its fields completed and the image file attached.
And that's pretty much it.
Note that
Environment.getExternalStorageDirectory()
works even in devices with no SD card.
Also forgot to mention that getAssets() is the function to access files in the assets folder in android.