ともちゃんのAndroid開発日記

組込みC言語プログラマだったともちゃんが、四苦八苦しながら、AndroidのJAVAとKotlinを習得して行きます。ともちゃんの備忘録も兼ねています。

OAuth2認証を使用して、添付ファイル付きGmailを自動送信する

OAuth2認証を使用して、添付ファイル付きGmailを自動送信しようとしたとき、かなりつまづいたので、記事にします。

1.Javamail-androidのダウンロード

activation.jar、additionnal.jar、mail.jarをダウンロードし、libsフォルダに入れます。

2.uses-permissions

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />

3.AuthTokenの取得

private void authenticate() {
final AccountManager manager = AccountManager.get(AuthenticationActivity.this);
final SharedPreferences prefs3 = getSharedPreferences("setting3", Context.MODE_PRIVATE);
final SharedPreferences.Editor editor3 = prefs3.edit();

String previousAuthToken = prefs3.getString("AuthToken", null);
if (previousAuthToken != null) {
manager.invalidateAuthToken("com.google", previousAuthToken);
}
manager.getAuthToken(new Account(mailAddressFrom, "com.google"),
"oauth2:https://mail.google.com/",
null,
false,
new AccountManagerCallback<Bundle>() {
@Override
public void run(AccountManagerFuture<Bundle> future) {
try {
Bundle bundle = future.getResult();
Intent intent = (Intent) bundle.get(AccountManager.KEY_INTENT);
// 認証されていない場合は認証許可ダイアログを表示
if (intent != null) {
startActivityForResult(intent, REQUEST_AUTH_DIALOG);
return;
}
String authToken =
bundle.getString(AccountManager.KEY_AUTHTOKEN);
editor3.putString("AuthToken", authToken);
editor3.apply();
finish();
} catch (OperationCanceledException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (AuthenticatorException e) {
e.printStackTrace();
}
}
},
null);

}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
switch (requestCode) {
case REQUEST_AUTH_DIALOG:
authenticate();
break;
}

}
}

4.Gmailの送信

public class GmailSendHelper {
private static final int RESPONSE_CODE_AUTH_SUCCESS = 235;
private static final int SUBMISSION_PORT = 587;
private static final String GMAIL_SMTP_SERVER = "smtp.gmail.com";
private final String mailAddressFrom;
private final String authToken;
private Session session;

public GmailSendHelper(String mailAddressFrom, String authToken) {
this.mailAddressFrom = mailAddressFrom;
this.authToken = authToken;
}

private SMTPTransport connectToSmtp(String host, int port)
throws MessagingException {
Properties props = new Properties();
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.starttls.required", "true");
props.put("mail.smtp.sasl.enable", "false");
// props.put("mail.smtp.ssl.enable", "true");
session = Session.getInstance(props);

SMTPTransport transport = new SMTPTransport(session, /* URL Name */ null);
transport.connect(host, port, mailAddressFrom,
/* Password, OAuth認証なのでNull */ null);

byte[] response = String.format("user=%s\1auth=Bearer %s\1\1", mailAddressFrom,
authToken).getBytes();
response = BASE64EncoderStream.encode(response);

transport.issueCommand(
"AUTH XOAUTH2 " + new String(response), RESPONSE_CODE_AUTH_SUCCESS);
return transport;
}

public void sendEmail(
InternetAddress[] internetAddresses,
ReportFragment.ReportMessageAndFile resultOfExport
)
throws MessagingException {
SMTPTransport smtpTransport
= connectToSmtp(GMAIL_SMTP_SERVER, SUBMISSION_PORT);

MimeMessage mimeMsg = new MimeMessage(session);

try {

mimeMsg.setSubject(resultOfExport.mailSubject, "utf-8");
mimeMsg.setFrom(new InternetAddress(mailAddressFrom));
mimeMsg.setRecipients(javax.mail.Message.RecipientType.TO,
internetAddresses);

// メール本文
final MimeBodyPart txtPart = new MimeBodyPart();
txtPart.setText(resultOfExport.mailText, "utf-8");

// 添付ファイル
final MimeBodyPart filePart = new MimeBodyPart();
File file = new File(resultOfExport.filePath);
FileDataSource fds = new FileDataSource(file);
DataHandler data = new DataHandler(fds);
filePart.setDataHandler(data);
filePart.setFileName(MimeUtility.encodeWord(resultOfExport.fileName));

final Multipart mp = new MimeMultipart();
mp.addBodyPart(txtPart);
mp.addBodyPart(filePart);
mimeMsg.setContent(mp);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}

smtpTransport.sendMessage(mimeMsg, mimeMsg.getAllRecipients());
}
 

5.API ConsoleでクライアントIDを登録する。

https://console.developers.google.com/apis/credentials

で認証情報を獲得します。「認証情報を作成」→「ウィザードで選択」を選択します。

f:id:uchida001tmhr:20180630072638p:plain

f:id:uchida001tmhr:20180630072829p:plain

名前:適当な名前を付けます。

名証明書フィンガープリント(デバッグ時:Windows):

keytool -exportcert -alias androiddebugkey -keystore "%USERPROFILE%\.android\debug.keystore" -list -v

名証明書フィンガープリント(リリース時:Windows):

keytool -exportcert -keystore [jksファイルのフルパス] -list -v

 上記を実行:

f:id:uchida001tmhr:20180630073631p:plain

パスワード(デバッグ時):android

パスワード(リリース時):jksファイルに設定したパスワード

 

SHA1のフィンガープリントをコピーして、上記「OAuthクライアントIDの作成」に貼り付ける。

(2018/10/21追記) Google Play アプリ署名を有効にして、Android App Bundleとしてリリースする場合、名証明書のフィンガープリントには、Google Developer Consoleの「アプリのリリース」→「アプリの署名」の「アプリへの署名証明書」のSHA1フィンガープリントを持ってくる必要があります。keytoolで出力したフィンガープリントではうまくいきませんので、ご注意を!!

 

これで、コンパイルデバッグすれば、自動でOAuth2認証を行い、添付メール付きメールを自動送信してくれます。