メモ2ブログ

メモtoウェブログ。旧ブログはこちら。 http://sakebook.blogspot.jp/

暗黙的Intentを絞り込んで明示的Intentのように連携する

よく使われている便利なアプリと連携がしたいときがあります。 LINETwitterなどのように、スキーマが定義されているものならば、特定できるのですが、そうではないアプリだと、暗黙的Intentを発行し、大量のアプリが候補で表示されます。

したい処理自体は暗黙的Intentの内容で良いのですが、特定のアプリに処理させたいときがあります。 暗黙的Intentは、対応できるアプリが複数あると選択画面がでてくるので、一つに絞り込めればシームレスに連携できます。

発行した暗黙的Intentに対応しているアプリ一覧取得

暗黙的Intentで起動するアプリは、発行する暗黙的IntentをPackageManager.queryIntentActivities(Intent intent int flags) に投げることで取得できます。 第二引数のflagsPackageManager.MATCH_DEFAULT_ONLY を指定します。0にすると、android.intent.category.DEFAULTを指定していない、 本来は呼ばれないものまで含まるので注意が必要です。

PackageManager pm = context.getPackageManager();
List<ResolveInfo> resInfo =
        pm.queryIntentActivities(targetIntent, PackageManager.MATCH_DEFAULT_ONLY);

resInfoには、targetIntentによって応答するアプリが入っています。一つのアプリで、複数応答するものはその数だけ入っています。

絞り込む

ResolveInfoから、絞り込みたいアプリのPackageNameと応答するActivityの名前を取得し、絞り込みたいIntentに、setComponentで指定し、特定させます。

intent.setComponent(new ComponentName(info.activityInfo.packageName, info.activityInfo.name));

次のようなクラスを作りました。コピペで使えます。

public class ImplicitIntentConverter {

    public static Intent convertExplicitIntentOfPackage(Context context, Intent targetIntent, String targetPackageName) {
        PackageManager pm = context.getPackageManager();
        List<ResolveInfo> resInfo =
                pm.queryIntentActivities(targetIntent, PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo info: resInfo) {
            String packageName = info.activityInfo.packageName;

            if (targetPackageName.equals(packageName)) {
                Intent intent = (Intent) targetIntent.clone();
                intent.setComponent(new ComponentName(packageName, info.activityInfo.name));
                return intent;
            }
        }
        return null;
    }
}

特定したいアプリが端末になかった場合に、nullを返すか引数に渡したtargetIntentをそのまま返すかは、用途に応じて変えるといいと思います。

始めから明示的Intentで呼ぶ場合との違い

スキーマが定義されていなくても、呼びたいアプリのPackageNameと応答するAcitvityの名前が分かっていれば、直接呼ぶことができます。

しかし、Acitvityの名前をコードに埋め込んでしまうと、可能性は低いですが、 そのActivityがなくなった場合には呼べません。 また、応答するアプリの作りにもよりますが、外部のアプリから直接呼ばれることを想定していない場合は、 予期せぬ動作を引き起こす恐れがあります。ActionやCategory、Dataを正しく指定しないと、想定した動作にはならないでしょう。

今回の方法だと、Activityの名前が変更されても、それは応答するアプリ側で解決してくれるため、対応できます。また、暗黙的Intentとして発行しているので、通常の呼ばれ方と同様なので不具合を引き起こす心配も少ないです。しかし、for文などの処理を挟んでいるので、 端末によっては少し遅くなる場合もあり得ます。

この枠組みを作っておくと、渡すPackageNameを動的に変えることで挙動を制御することができます。 ABテストなどに使えるかもしれません。

以上。

参考

Android 特定の Intent (Action) を処理できる Activity (アプリ)の一覧を取得 / Y.A.M の 雑記帳

Android:IntentFilterにDEFAULT_CATEGORYが必要な理由 / Yukiの枝折