数年前までは、Google Code の 2009 年ライブラリを使用して Android 用のメール送信を行っていたのですが、
参照 ➡ javamail-android + AsyncTask でメール送信を行う為のテンプレート
今回改めて調べてみると、オリジナルの javamail 側で Android の対応がなされていました。gradle に記述するだけで使用可能です。
▼ 今回使用したもの
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "23.0.3"
defaultConfig {
applicationId "january17.lightbox.mailapplication"
minSdkVersion 19
targetSdkVersion 22
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
packagingOptions {
pickFirst 'META-INF/LICENSE.txt' // picks the JavaMail license file
}
}
repositories {
jcenter()
maven {
url "https://maven.java.net/content/groups/public/"
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:22.2.1'
compile 'com.sun.mail:android-mail:1.5.5'
compile 'com.sun.mail:android-activation:1.5.5'
testCompile 'junit:junit:4.12'
}
さらに、今回は添付ファイルを付加する為に調べてみると、MimeBodyPart クラスの attachFile メソッドに、ファイルのパスを渡すだけで全てライブラリが実行してくれる事が解りました。さっそくオリジナルのソースコードを読んでみると、古くからあるサンプルの FileDataSource を使った内容(Google code バージョンでも同等の処理をしていました)であり、さらに Google code バージョンには無い contentType と encoding を指定できる attachFile メソッドも存在していました。
添付ファイル用 AndroidSendmail クラス
public class AndroidSendmail {
private String server;
private String port;
private String userid;
private String password;
private String username;
// ************************************
// コンストラクタ
// ************************************
public AndroidSendmail(
String server,
String port,
String userid,
String password,
String username) {
this.server = server;
this.port = port;
this.userid = userid;
this.password = password;
this.username = username;
}
// ************************************
// AsyncTask の onPostExecute から外部イベントとして
// 呼び出す為のインターフェイス
// ************************************
public interface SendMailed {
public void onMailResult(String result);
}
// ************************************
// メール送信
// ************************************
public void SendMail(String to, String from, String subject, String body,String file, final SendMailed sm) {
new AsyncTask<String, Void, String>() {
// ************************************
// 非同期処理
// ************************************
@Override
protected String doInBackground(String... params) {
Log.i("lightbox","開始");
String result_string = "";
try {
// ************************************
// プロパティオブジェクトを作成
// プロパティオブジェクトは、extends Hashtable(連想配列)
// ************************************
Properties props = new Properties();
// ************************************
// * 連想配列に送信用サーバのアドレスをセット
// ************************************
// props.put("mail.smtp.host","smtp.mail.yahoo.co.jp"); // Yahoo
// props.put("mail.smtp.host","smtp.live.com"); // Microsoft
props.put("mail.smtp.host", server);
props.put("mail.smtp.port", port);
props.put("mail.smtp.auth", "true" ); // SMTP 認証を行う
// ************************************
// SSL関連設定
// ************************************
if ( port.equals("465") ) {
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.socketFactory.fallback", "false");
props.put("mail.smtp.socketFactory.port", port);
}
// ************************************
// 暗号化
// ************************************
if ( port.equals("587") ) {
props.put("mail.smtp.starttls.enable", "true");
}
// ************************************
// メール用のセッションを作成
// ************************************
SimpleAuthenticator sa =
new SimpleAuthenticator(userid, password);
Session MailSession =
Session.getInstance( props, sa );
// ************************************
// メール用のメッセージオブジェクトを作成
// ************************************
MimeMessage msg = new MimeMessage(MailSession);
// ************************************
// 宛先
// ( 第一引数では、CC や BCC を指定できます。)
// ************************************
msg.setRecipients(
Message.RecipientType.TO,
new Address[] { new InternetAddress( params[0], "日本語相手先", "ISO-2022-JP" ) }
);
// ************************************
// 送信者
// ************************************
msg.setFrom(
new InternetAddress( params[1], username, "ISO-2022-JP" )
);
// ************************************
// 件名
// ************************************
msg.setSubject( params[2], "ISO-2022-JP" );
// ************************************
// 本文(テキスト)
// ************************************
MimeBodyPart mbp1 = new MimeBodyPart();
mbp1.setText(params[3], "ISO-2022-JP");
// ************************************
// 添付ファイル
// ************************************
MimeBodyPart mbp2 = new MimeBodyPart();
mbp2.attachFile(params[4]);
// ************************************
// マルチパート作成
// ************************************
Multipart mp = new MimeMultipart();
mp.addBodyPart(mbp1);
mp.addBodyPart(mbp2);
msg.setContent(mp);
// ************************************
// 送信
// ************************************
Transport.send( msg );
result_string = "メールの送信を完了しました";
}
catch( Exception e ) {
e.printStackTrace();
result_string = "メールの送信に失敗しました";
}
Log.i("lightbox","終了");
return result_string;
}
// ************************************
// 非同期処理終了後の処理( 画面へのアクセスが可能 )
// ************************************
@Override
protected void onPostExecute(String result) {
// 引数のインターフェイス内のメソッドを呼び出す
sm.onMailResult(result);
}
}.execute(to, from, subject, body,file);
}
// ************************************
// 認証用のプライベートクラス
// ************************************
private class SimpleAuthenticator extends Authenticator {
private String user_string = null;
private String pass_string = null;
public SimpleAuthenticator( String user_s, String pass_s ) {
super();
user_string = user_s;
pass_string = pass_s;
}
protected PasswordAuthentication getPasswordAuthentication(){
return new PasswordAuthentication( this.user_string, this.pass_string );
}
}
}
添付するファイルは、ギャラリーから取得する画像を使ってテストしました。昨今、Android では、ギャラリーから取得する為の intent のパラメータが過去バージョンから変わっており、取得する uri も 直接のパスでは無い為、作法にのっとってデーターベースより必要な情報を収集しています。
MainActivity
※ ここでは、MIME と 画像の表示名を DB から取得していますが、使用していません
※ Gmail を使用する場合は、安全性の低いアプリの許可を『有効』にする 必要があります。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) MainActivity.this.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// ギャラリーの呼び出し
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent, 10); // 10 は任意
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 10 && resultCode == Activity.RESULT_OK) {
if (data != null) {
Uri uri = data.getData();
String id = DocumentsContract.getDocumentId(uri);
Log.i("lightbox", "id文字列:" + id);
// カラムリストの指定
String[] proj = {
MediaStore.Images.Media.DATA,
MediaStore.Images.Media.MIME_TYPE,
MediaStore.Images.Media.DISPLAY_NAME
};
String selection = "_id=?";
String[] args = new String[]{id.split(":")[1]};
// 目的データのカーソルを取得
ContentResolver contentResolver = getContentResolver();
Cursor cursor = contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, proj, selection, args, null);
cursor.moveToFirst(); // 読み出し
// データを取得
String result = cursor.getString(0);
String mime = cursor.getString(1);
String name = cursor.getString(2);
Log.i("lightbox", "Path:" + result);
cursor.close();
AndroidSendmail as = new AndroidSendmail(
"smtp.gmail.com", // サーバ( 例 : "smtp.mail.yahoo.co.jp" or "smtp.live.com" )
"465", // 465 または 587
"アカウント", // アカウント
"パスワード", // パスワード
"日本語ユーザ名"
);
as.SendMail(
"宛先メールアドレス",
"自分のメールアドレス",
"件名",
"本文一行目\n本文二行目",
result, // ファイルへのパス
new AndroidSendmail.SendMailed() {
@Override
public void onMailResult(String result) {
Log.i("lightbox", result);
}
}
);
}
}
}
}
上のコードでは、Android API 19 以上で利用できる、getDocumentId を使用していますが、それより古いバージョンでは 呼び出し時に ACTION_GET_CONTENT を使用して、取得できる uri の パスの一番最後の数値を _id に使用して取得できました。
※ API 18(4.3.1) と API 15(4.0.3) を エミュレータで確認しました。
Uri uri = data.getData();
Log.i("lightbox", "id文字列:" + uri.toString());
// カラムリストの指定
String[] proj = {
MediaStore.Images.Media.DATA,
MediaStore.Images.Media.MIME_TYPE,
MediaStore.Images.Media.DISPLAY_NAME
};
String selection = "_id=?";
String[] path = (uri.toString()).split("/");
String[] args = new String[]{path[path.length-1]};
Manifest.xml
※ INTERNET と READ_EXTERNAL_STORAGE が必要です
<?xml version="1.0" encoding="utf-8"?>
<manifest package="january17.lightbox.mailapplication"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
最後に
マルチパートの内容を WEB上の Outlook から送って比べてみました。Microsoft は、異常に多くの情報を持っていましたが、一般的なデータは javamail の setText(本文, "ISO-2022-JP") と attachFile(ファイルのパス) で問題無いようでした。(ヘッダ部分は通常の WEB メールで確認できます)
▼ Microsoft()
--_002_PS1PR01MB0667CE2FF5CECC981B23A8CF81710PS1PR01MB0667apcp_
Content-Type: text/plain; charset="iso-2022-jp"
Content-Transfer-Encoding: quoted-printable
=1B$BK\J80l9TL\=1B(B
=1B$BK\J8Fs9TL\=1B(B
--_002_PS1PR01MB0667CE2FF5CECC981B23A8CF81710PS1PR01MB0667apcp_
Content-Type: image/jpeg; name="2013-01-26T01-26-00_0.jpg"
Content-Description: 2013-01-26T01-26-00_0.jpg
Content-Disposition: attachment; filename="2013-01-26T01-26-00_0.jpg";
size=431072; creation-date="Fri, 20 Jan 2017 16:29:33 GMT";
modification-date="Fri, 20 Jan 2017 16:29:33 GMT"
Content-Transfer-Encoding: base64
/9j/4AAQSkZJRgABAQAAAQABAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQA
AAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAGYKADAAQAAAABAAAEyAAAAAD/2wBD
AAIBAQIBAQICAQICAgICAwUDAwMDAwYEBAMFBwYHBwcGBgYHCAsJBwgKCAYGCQ0JCgsLDAwMBwkN
▼ JavaMail for Android
------=_Part_0_1107074864.1484932123775
Content-Type: text/plain; charset=ISO-2022-JP
Content-Transfer-Encoding: quoted-printable
=1B$BK\J80l9TL\=1B(B
=1B$BK\J8Fs9TL\=1B(B
------=_Part_0_1107074864.1484932123775
Content-Type: image/jpeg; name=KIMG0178.JPG
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=KIMG0178.JPG
/9j/4TjERXhpZgAATU0AKgAAAAgACgEPAAIAAAAIAAAAhgEQAAIAAAAGAAAAjgESAAMAAAABAAMA
AAEaAAUAAAABAAAAlAEbAAUAAAABAAAAnAEoAAMAAAABAAIAAAExAAIAAAALAAAApAEyAAIAAAAU
AAAAsAITAAMAAAABAAEAAIdpAAQAAAABAAAAxAAABEpLWU9DRVJBADQwNEtDAAAAAEgAAAABAAAA