Send emails from Android app using Firebase callable functions

Danielle H
4 min readDec 7, 2019
As easy as that!

Here we will see how to use Firebase cloud functions without needing an HTTP request, Volley, Retrofit or authentication tokens. Instead, we will use Firebase Callable functions.

We have a number of apps that need to send status emails to our account without the user having to press ‘send’. At the beginning, I used this solution, which worked excellently. However, the password needs to be hardcoded in the app, and in addition to security concerns this is not a practical solution because every time you change your password you need to update the app.

So the solution is to have the app send a request to a third party, which sends the email for you.

Which third party? I checked out Firebase Cloud Functions. I started with Udacity’s course Firebase in a weekend and Edigleysson Silva’s excellent post on sending emails via cloud functions, and checked out various ways to secure the function from unauthenticated users. I was all set up to use Firebase Authentication on an HTTP function when I discovered this gem: Calling functions directly from your Firebase app. No need for Volley or Retrofit, no need to get and manage authentication tokens, just…call the function. And It Just Works.

So let’s get started:

Creating the cloud function

Create the cloud function using Edigleysson Silva’s excellent post. Note that you need to have Node.js and npm before following the directions in the post, which you can install from the Node.js site (installing Node.js installs npm as well). To check if you have Node and npm (or if it installed correctly) you can type in the Windows Command Prompt (cmd):

$ node --version
v12.13.1
$npm --version
6.12.1

There is one important change to the sendMail function defined by Edigleysson Silva: instead of

exports.sendMail = functions.https.onRequest((req, res) =>

we want

exports.sendMail = functions.https.onCall((data, context) =>

This defines that our function is called directly from a Firebase app, and not as an explicit HTTP request. data holds the input arguments from the function, such as current status or the email body as a String. context holds the authorization (if any) and user data (if any).

So my function looks like this:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const nodemailer = require('nodemailer');
/**
* Here we're using Gmail to send
*/
let transporter = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 587,
secure: false,
auth: {
user: 'myuser',
pass: 'mypassword'
}
});
/**
* function to send email using given data. Data is formatted as two
* objects:data.event and data.action
*/
exports.sendMail = functions.https.onCall((data, context) => {
//confirm user authentication. comment out if not using Firebase Auth.
if (!context.auth) {
throw new functions.https.HttpsError('failed-precondition', 'The function must be called ' +
'while authenticated.');
}
const event = data.event;
const action = data.action;
const mailOptions = {
from: 'myuser', // Something like: Jane Doe <janedoe@gmail.com>
to: 'destination email',
subject: `App Status: ${action}`, // email subject
text: event // email content in text
//html:<p>put html code here if you prefer<p>
};
// returning result
return transporter.sendMail(mailOptions, (erro, info) => {
if(erro){
return res.send(erro.toString());
}
return res.send('email sent successfully');
});
});

Calling the function from Android

Add Firebase to your app

First, you need to define your Firebase app if you have not done so already. The versions that worked for me without androidX were:

In the project level build.gradle:

buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.0'
classpath 'com.google.gms:google-services:4.1.0'
}
}
allprojects {
repositories {
google()
jcenter()
}
}

In the app level build.gradle:

dependencies {
implementation 'com.google.firebase:firebase-auth:17.0.0'
implementation 'com.firebaseui:firebase-ui-auth:4.3.1'
implementation 'com.google.firebase:firebase-core:16.0.9'
implementation 'com.google.firebase:firebase-functions:17.0.0'
}
apply plugin: 'com.google.gms.google-services'

Call the function from your app

In the activity that needs to send the email (or in a Utility file), define the following function:

private Task<String> sendMail(String eventText, String action) {FirebaseFunctions functions = FirebaseFunctions.getInstance();
// Create the arguments to the callable function.
Map<String, Object> data = new HashMap<>();
data.put("event", eventText);
data.put("action", action);
data.put("push", true);
return functions
.getHttpsCallable("sendMail")
.call(data)
.continueWith(new Continuation<HttpsCallableResult, String>() {
@Override
public String then(@NonNull Task<HttpsCallableResult> task) throws Exception {
String result = (String) task.getResult().getData();
return result;
}
});
}

Then call the function where you need to in your app by simply calling

sendMail(myEmailText,myEmailAction);

You can get more information on calling cloud functions in the official documentation.

In my case I am also using Firebase authentication so the user is signed in before calling this email. You can comment out the code that verifies authentication in the cloud function if not using this.

Verify Gmail account (optional)

You might need to verify the account the first time as Google (rightly) won’t always allow using your email to send emails from apps (unless the email you are sending from is also your Firebase account email).

First, you must enable ‘Less Secure Access’ in your sender email account: sign into the sending account using a web browser at https://mail.google.com, then go to Settings > Accounts and Import > Other Google Account settings. Under Security, scroll down and enable access for less secure apps.

You might also need to clear captcha: when signed in to your sender account, go to: https://accounts.google.com/DisplayUnlockCaptcha, give it a few minutes and try again.

And… that’s it!

--

--

Danielle H

I started programming in LabVIEW and Matlab, and quickly expanded to include Android, Swift, Flutter, Web(PHP, HTML, Javascript), Arduino and Processing.