前人未踏の領域へ Androidアプリ開発編

Androidアプリ開発に関する調査メモ置き場。古い記事にはアプリ以外も含まれます。

Android:Email送信時にEXTRA_SUBJECT、EXTRA_TEXTが反映されない

課題

これまで以下のような感じでメールを送信していたが、いつ頃からか'EXTRA_SUBJECT', 'EXTRA_TEXT'がGmailにセットされなくなった。

/**
 * メール送信用Intentを生成する
 */
fun getMailIntent(mailTo: String, subject: String, body: String): Intent {
    val intent =
        Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:$mailTo"))
    intent.putExtra(Intent.EXTRA_SUBJECT, subject)
    intent.putExtra(Intent.EXTRA_TEXT, body)
    return intent
}

対応

Gmailのどこかのアップデートで EXTRA_EMAIL が必要になった模様。 ついでに公式サイトを参考にmailto:の記述もリファクタする。 applyはまだ使い慣れてないけどすっきり書けて良いね。

/**
 * メール送信用Intentを生成する
 */
fun getMailIntent(mailTo: String, subject: String, body: String): Intent {
    return Intent(Intent.ACTION_SENDTO)
        .apply {
            data = Uri.parse("mailto:")
            putExtra(Intent.EXTRA_EMAIL, arrayOf(mailTo))
            putExtra(Intent.EXTRA_SUBJECT, subject)
            putExtra(Intent.EXTRA_TEXT, body)
        }
}

なんとなくGmail以外のメーラーが起動しないような気がする...。

参考

developer.android.com

Android 10以降でアプリシェア時にEXTRA_INITIAL_INTENTSが機能しない

課題

コンテンツのシェア機能利用時に Intent.createChooser したIntentに対し、EXTRA_INITIAL_INTENTS で ターゲットアプリごとに特別にカスタマイズしたLabeledIntentを渡していたが、Android10端末でシェア対象のアプリが数件しか表示されなくなった。

原因

Android10のChooserActivityのコードを見ると以下のような記述がある

private static final int MAX_EXTRA_INITIAL_INTENTS = 2;

ここで2件に制限されているため、実際に渡した件数よりも少なく表示される。

        Parcelable[] pa = intent.getParcelableArrayExtra(Intent.EXTRA_INITIAL_INTENTS);
        Intent[] initialIntents = null;
        if (pa != null) {
            int count = Math.min(pa.length, MAX_EXTRA_INITIAL_INTENTS);
            initialIntents = new Intent[count];
            for (int i = 0; i < count; i++) {
                if (!(pa[i] instanceof Intent)) {
                    Log.w(TAG, "Initial intent #" + i + " not an Intent: " + pa[i]);
                    finish();
                    super.onCreate(null);
                    return;
                }
                final Intent in = (Intent) pa[i];
                modifyTargetIntent(in);
                initialIntents[i] = in;
            }
        }

実際には同じ場所に3, 4件表示されたりするのだが、これはおすすめアプリをGoogleが自動で選択しているような 気がする。

対応

Androidのフォーラムを見るとバグじゃないかという報告も出ているが、 明示的に制限が記述されている以上、EXTRA_INITIAL_INTENTSの利用はやめるか又は特に重要なアプリのみに制限し、 API Level 29以降は標準の Intent.ACTION_SENDだけを使うようにする。

when {
            Build.VERSION.SDK_INT <= Build.VERSION_CODES.P -> {
                   //これまでのシェア
            }
            else -> { 
                  //新しいシェア
                 val shareIntent: Intent = Intent(Intent.ACTION_SEND)
                 ... 略
                 val chooserIntent = Intent.createChooser(shareIntent, 'share')
                 activity.startActivity(chooserIntent)
         }      
}

Google標準の汎用シェア機能を使おうとすると、アプリごとに受け付けるパラメータが異なったりして うまく動作しないので EXTRA_INITIAL_INTENTS によりカスタマイズする方法が使われていたが、 Googleとしてはこれを制限することによって標準のシェア機能に対応するようアプリ側に促していきたいのかもしれない。 とはいえテキストと画像を同時に扱えるアプリとそうでないのがあったりパラメータの有無で表示されるアプリが変わってきちゃったりするので、 そう上手くは行かないんだよな。

参考

メアド、パスワードの入力候補を無効化したい

課題

Androidの8.0からAutofill機能が追加され、メアドとパスワードを記憶、自動セットしてくれるようになったが、 新規入力画面ではメールアドレス、パスワードの保管を無効化したい。どうすればよいか。

対応

importantForAutofillno にする

<TextView
    android:importantForAutofill="no" />

逆に yes にすればAndroidが普段AutoFillをしないような項目でも使えるようになる(はず)。

この対応がなぜ必要になったか

新規会員登録画面で別画面から取得したメールアドレス欄をisClickable,isFocusableがともにfalseな状態で初期表示させていたが、 パスワード入力時にAutoFill機能が発動し、メールアドレスが変更されてしまった。 その結果、別画面で実施していたメアドの存在チェックが意味をなさなくなってしまった。 EditTextやめればいいじゃんという考えもあると思うけど、 OAuthログイン時にはここでメールアドレスの入力が必要になるなどの分岐が存在した。

まあ新規登録時にAutoFillは普通いらないはず。