前提
AndroidアプリではIntent
を使用してアプリ間で情報をやりとりできる。
今回の記事では「URLを開く方法をユーザーに選ばせたい」というシナリオを前提とする。
このIntent
だが、データ形式に対してデフォルトの共有方法をユーザーが設定することもできる。
たとえば、「URLはデフォルトでChromeで開くようにする」「“https://b.hatena.ne.jp/entry/“から始まるURL(以下「ブコメページのURL」と呼称する)の場合はデフォルトで
Satena
で」という風に設定すると、毎回いちいち「どのアプリでこのURL開きますか?」を選択する必要はなくなるわけだ。
この記事に書いたこと
以下二点を同時に満たす実装例。
IntentChooserを強制的に表示する
(複数の選択肢がある場合、デフォルト設定の有無に関わらずchooserを表示する)IntentChooserに表示するアプリ項目を呼び出し元から操作する
何故そんなことしたいのか
Satena
ではURLに対して「ページを外部ブラウザで開く」というコマンドを用意しているのだが、これには当然ながらIntent
を利用している。
そして同時に「ブコメページのURLを
Satena
で開く」ことができるようにしてある。(後述の
intent-filter例
参照)
さて、では
Satena
アプリ内でブコメページのURLを「外部ブラウザで開く」した場合、どうなるだろう。
「ブコメページのURLをSatenaで開く」がデフォルト動作として設定されている場合、Chromeなどの外部ブラウザが開かれることはなく、Satenaのブコメページ用Activityが新たに開かれてしまう。なんだか書いてあるのと挙動が違わね? というようなことになる。
Satenaのintent-filter例
AndroidManifest.xml
<intent-filter android:label="Satenaで見る">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="https" android:host="b.hatena.ne.jp" android:pathPrefix="/entry"/>
</intent-filter>
コード
「ページを外部ブラウザで開く」の実装内容抜粋
val packageManager = context.packageManager
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
if (url.startsWith("https://b.hatena.ne.jp/entry/")) {
// 強制的にchooserを開くための処理
// ブコメページ以外のURLを使用することで「Satenaで開く」以外のURLを開く純粋な方法を収集する
val dummyIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://dummy"))
val intentActivities = packageManager.queryIntentActivities(dummyIntent, PackageManager.MATCH_ALL)
val bookmarksActivities = packageManager.queryIntentActivities(intent, PackageManager.MATCH_ALL)
// 生成するintentリストを弄ればchooserの項目を操作できる
val intents = bookmarksActivities.plus(intentActivities)
.distinctBy { it.activityInfo.name }
.map { Intent(intent).apply { setPackage(it.activityInfo.packageName) } }
check(intents.isNotEmpty()) { "cannot resolve intent for browsing the website: $url" }
val chooser = Intent.createChooser(Intent(), "Choose a browser").apply {
putExtra(Intent.EXTRA_INITIAL_INTENTS, intents.toTypedArray())
}
context.startActivity(chooser)
}
else {
// それ以外のURLの場合はデフォルトで開く
checkNotNull(intent.resolveActivity(packageManager)) { "cannot resolve intent for browsing the website: $url" }
context.startActivity(intent)
}