数年前までは、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
|
【2017 Android Studioの最新記事】
- 別に納品するわけでは無いので、Android の ListView のカスタマイズなんてこれで十分でしょ / TestArrayAdapter バージョン2
- Java : Class 構造より、update 文を作成する
- ViewSwitcher を使用した2画面アプリ (5) : SQLiteデータを更新する
- ViewSwitcher を使用した2画面アプリ (4) : SQLiteデータをインポートしてリストビューに表示する
- Android Studio にインポートして使用する SQLite データベースを MDB より作成する VBScript
- ViewSwitcher を使用した2画面アプリ (3) : 画面部分の作成と画面切り替えテスト『画面をコントロールする Helper クラスの作成』
- ViewSwitcher を使用した2画面アプリ (2) : 画面部分の作成と画面切り替えテスト『画面の作成』
- ViewSwitcher を使用した2画面アプリ (1) : 画面部分の作成と画面切り替えテスト『メニューの作成』
- OkHttp を使用した HttpAccess クラスで Web 上の 画像をダウンロードして表示するテンプレート
- OkHttp を使用した HttpAccess クラスで Web 上の PHP アプリに対してファイルをアップロードするテンプレート
- OkHttp を使用した HttpAccess クラスで Web 上の掲示板に投稿(POST)するテンプレート
- OkHttp を使用した HttpAccess クラスで Web 上のデータを取得(GET)して ListView を表示するテンプレート
- Okhttp を使用した、GET、POST、ファイルアップロードを楽に実装できる HttpAccess クラス
- Android の assets フォルダーに保存した 400x320 の画像ファイルの扱い
- Android の drawable フォルダーに保存した 400x320 の画像ファイルの6種類の扱いと Density
- Android での保存用テキストデータの扱いを okio で簡素化する
- Android 6.0 : テストの為の Runtime Permission の対応を自動化するテンプレート
- Android Studio : Runtime Permission 等の裏方作業を MainActivity にさせて、本来の処理は継承したサブクラスで行う( カメラを呼び出して画像を保存させ、I..
- Android 6.0 エミュレータで 複数の Runtime Permission の対応を簡潔に吸収するクラス( CheckMyPermission )
- Android 6.0 の Runtime Permission に対応する前に、AndroidManifest.xml に権限の記述の必要無いプライベートな書き込みで情報を収集する