Skip to content

Instantly share code, notes, and snippets.

@demixdn
Created May 8, 2017 09:55
Show Gist options
  • Save demixdn/198ee0455d4a12de247f22ae44b0b73f to your computer and use it in GitHub Desktop.
Save demixdn/198ee0455d4a12de247f22ae44b0b73f to your computer and use it in GitHub Desktop.
Send email
dependencies {
compile 'javax.mail:mail:1.4.7'
compile 'javax.activation:activation:1.1.1'
}
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.io.UnsupportedEncodingException;
import java.util.Properties;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import static android.text.TextUtils.isEmpty;
import static android.util.Patterns.EMAIL_ADDRESS;
public final class EmailClient implements EmailSubmit {
private static final int PROCESSORS = Runtime.getRuntime().availableProcessors();
private static final int SCALE_POOL_SIZE = 4;
private static final int INITIAL_POOL_SIZE = PROCESSORS > 1 ? PROCESSORS : 2;
private static final int MAX_POOL_SIZE = INITIAL_POOL_SIZE * SCALE_POOL_SIZE;
private static final int KEEP_ALIVE_TIME = 15;
private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
private String addressFrom;
private String personalName;
private String addressTo;
private String smtpHost;
private int smtpPort;
private boolean authEnabled;
private String authLogin;
private String authPassword;
private Callback callback;
private String subject;
private String message;
private ThreadPoolExecutor threadPoolExecutor;
private EmailClient() {
//private
}
@Override
public boolean submit(@NonNull String subject, @NonNull String message) {
Properties properties = getProperties();
// Get the default Session object.
Session session = Session.getDefaultInstance(properties);
try {
MimeMessage mimeMessage = createMessage(subject, message, session);
Transport transport = session.getTransport("smtps");
transport.connect(null, properties.getProperty("mail.password"));
transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());
transport.close();
return true;
} catch (MessagingException | UnsupportedEncodingException e) {
e.printStackTrace();
return false;
}
}
@Override
public void submit(@NonNull String subject, @NonNull String message, @Nullable Callback callback) {
this.callback = callback;
checkNotEmpty(subject);
this.subject = subject;
checkNotEmpty(message);
this.message = message;
runOnExecutor();
}
private void runOnExecutor() {
if (this.threadPoolExecutor == null) {
createExecutor();
}
this.threadPoolExecutor.execute(new SendEmailTask());
}
private void createExecutor() {
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();
ThreadFactory threadFactory = new JobThreadFactory();
this.threadPoolExecutor = new ThreadPoolExecutor(INITIAL_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, workQueue, threadFactory);
}
@NonNull
private MimeMessage createMessage(@NonNull String subject, @NonNull String content, Session session) throws MessagingException, UnsupportedEncodingException {
MimeMessage mimeMessage = new MimeMessage(session);
mimeMessage.addHeader("Content-type", "text/plain; charset=UTF-8");
String emailEncoding = "UTF-8";
// Set From: header field of the header.
InternetAddress address = isEmpty(personalName) ? new InternetAddress(addressFrom) : new InternetAddress(addressFrom, personalName);
mimeMessage.setFrom(address);
// Set To: header field of the header.
mimeMessage.addRecipient(Message.RecipientType.TO, new InternetAddress(addressTo));
mimeMessage.setSubject(subject, emailEncoding);
mimeMessage.setText(content, emailEncoding);
mimeMessage.saveChanges();
return mimeMessage;
}
@NonNull
private Properties getProperties() {
// Get system properties
Properties properties = System.getProperties();
// Setup mail server
properties.setProperty("mail.smtps.host", smtpHost);
properties.setProperty("mail.smtps.port", String.valueOf(smtpPort));
properties.setProperty("mail.smtps.auth", String.valueOf(authEnabled));
if (authEnabled) {
properties.setProperty("mail.user", authLogin);
properties.setProperty("mail.password", authPassword);
}
return properties;
}
private static void checkArgument(boolean b) {
if (!b) {
throw new IllegalArgumentException();
}
}
private static CharSequence checkNotEmpty(CharSequence string) {
if (isEmpty(string)) {
throw new IllegalArgumentException();
}
return string;
}
private static CharSequence checkNotEmpty(CharSequence string, @Nullable String errorMessage) {
if (isEmpty(string)) {
throw new IllegalArgumentException(errorMessage);
}
return string;
}
static class Builder {
private static final String ERROR_AUTH_MESSAGE = "If authentication is enabled, then you must specify a username and password";
private static final String ERROR_EMPTY_MESSAGE = "This argument is required";
@Nullable
private String addressFrom;
@Nullable
private String personalName;
@Nullable
private String addressTo;
@Nullable
private String smtpHost;
private int smtpPort;
private boolean authEnabled;
@Nullable
private String authLogin;
@Nullable
private String authPassword;
Builder() {
}
Builder from(@NonNull String addressFrom) {
checkNotEmpty(addressFrom);
checkArgument(isEmail(addressFrom));
this.addressFrom = addressFrom;
return this;
}
Builder to(@NonNull String addressTo) {
checkNotEmpty(addressTo);
checkArgument(isEmail(addressTo));
this.addressTo = addressTo;
return this;
}
Builder name(@NonNull String personalName) {
checkNotEmpty(personalName);
this.personalName = personalName;
return this;
}
Builder smtpHost(@NonNull String smtpHost) {
checkNotEmpty(smtpHost);
this.smtpHost = smtpHost;
return this;
}
Builder smtpPort(int smtpPort) {
checkArgument(smtpPort > 0);
this.smtpPort = smtpPort;
return this;
}
Builder authEnabled(boolean authEnabled) {
this.authEnabled = authEnabled;
return this;
}
Builder authLogin(@NonNull String authLogin) {
checkNotEmpty(authLogin);
this.authLogin = authLogin;
return this;
}
Builder authPassword(@NonNull String authPassword) {
checkNotEmpty(authPassword);
this.authPassword = authPassword;
return this;
}
EmailClient build() {
EmailClient ec = new EmailClient();
checkNotEmpty(addressFrom, ERROR_EMPTY_MESSAGE);
ec.addressFrom = addressFrom;
checkNotEmpty(addressTo, ERROR_EMPTY_MESSAGE);
ec.addressTo = addressTo;
checkNotEmpty(smtpHost, ERROR_EMPTY_MESSAGE);
ec.smtpHost = smtpHost;
checkArgument(smtpPort > 0);
ec.smtpPort = smtpPort;
if (personalName != null) {
ec.personalName = personalName;
}
ec.authEnabled = authEnabled;
if (ec.authEnabled && (isEmpty(authLogin) || isEmpty(authPassword))) {
throw new IllegalArgumentException(ERROR_AUTH_MESSAGE);
}
if (ec.authEnabled) {
ec.authLogin = authLogin;
ec.authPassword = authPassword;
}
return ec;
}
private boolean isEmail(String email) {
return EMAIL_ADDRESS.matcher(email).matches();
}
}
private static class JobThreadFactory implements ThreadFactory {
private static final String THREAD_NAME = "emailclient-thread-";
private int counter = 0;
@Override
public Thread newThread(@NonNull Runnable runnable) {
return new Thread(runnable, THREAD_NAME + counter++);
}
}
public interface Callback {
void onSuccess();
void onFailure(Exception exception);
}
private class SendEmailTask implements Runnable {
@Override
public void run() {
Handler handler = new Handler(Looper.getMainLooper());
Properties properties = getProperties();
// Get the default Session object.
Session session = Session.getDefaultInstance(properties);
try {
MimeMessage mimeMessage = createMessage(subject, message, session);
Transport transport = session.getTransport("smtps");
transport.connect(null, properties.getProperty("mail.password"));
transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());
transport.close();
sendCallbackSuccess(handler);
} catch (MessagingException | UnsupportedEncodingException e) {
e.printStackTrace();
sendCallbackFailure(handler, e);
}
}
private void sendCallbackSuccess(Handler handler) {
handler.post(new Runnable() {
@Override
public void run() {
if (EmailClient.this.callback != null) {
EmailClient.this.callback.onSuccess();
}
}
});
}
private void sendCallbackFailure(Handler handler, final Exception e) {
handler.post(new Runnable() {
@Override
public void run() {
if (EmailClient.this.callback != null) {
EmailClient.this.callback.onFailure(e);
}
}
});
}
}
}
import android.accounts.NetworkErrorException;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import java.util.Random;
public final class EmailSender {
private static final String ADDRESS_FROM = "<email_from>";
private static final String ADDRESS_TO = "<email_to>";
private static final String SMTP_HOST = "<smtp_host>";
private static final int SMTP_PORT = 465;
private static final boolean AUTH_ENABLED = true;
private static final String AUTH_LOGIN = "<auth_login>";
private static final String AUTH_PASSW = "<auth_password>";
private static final int FAKE_DELAYED = 800;
private static final String SUBJECT = "your subject";
private final EmailSubmit emailSubmit;
public EmailSender() {
EmailClient.Builder builder = new EmailClient.Builder().from(ADDRESS_FROM)
.to(ADDRESS_TO)
.smtpHost(SMTP_HOST)
.smtpPort(SMTP_PORT)
.authEnabled(AUTH_ENABLED)
.authLogin(AUTH_LOGIN)
.authPassword(AUTH_PASSW);
emailSubmit = builder.build();
}
public final void sendCallback(@NonNull String message, @NonNull EmailClient.Callback callback){
emailSubmit.submit(SUBJECT, message, callback);
}
/**
* Use only for testing
* @param callback callback
* @deprecated only for test
*/
@SuppressWarnings("unused")
@Deprecated
private void fakeSend(final EmailClient.Callback callback) {
Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
@Override
public void run() {
Random random = new Random();
boolean nextBoolean = random.nextBoolean();
if (nextBoolean) {
callback.onSuccess();
} else {
callback.onFailure(new NetworkErrorException("wifi is enabled"));
}
}
}, FAKE_DELAYED);
}
}
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.annotation.WorkerThread;
public interface EmailSubmit {
@WorkerThread
boolean submit(@NonNull String subject, @NonNull String message);
@UiThread
void submit(@NonNull String subject, @NonNull String message, @Nullable EmailClient.Callback callback);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment