AndroidのWebViewではtel
やmailto
などのリンクを踏んでも電話アプリやメールアプリが起動しないためそれに対応するための実装が必要です。
また暗黙的Intentに対応する場合も、そのための実装が必要です。
これらの実装方法を紹介します。
URL判定処理
URLからIntentを作成します。
class WebViewRequestValidation {
fun isTransitionToOtherApp(url: Uri): TransitionToOtherApp {
return when (url.scheme) {
"tel" -> TransitionToOtherApp.Yes(Intent(Intent.ACTION_DIAL, url))
"mailto" -> TransitionToOtherApp.Yes(Intent(Intent.ACTION_SENDTO, url))
"intent" -> TransitionToOtherApp.Yes(Intent.parseUri(url.toString(), Intent.URI_INTENT_SCHEME))
else -> TransitionToOtherApp.No()
}
}
sealed class TransitionToOtherApp {
class Yes(val intent: Intent):TransitionToOtherApp()
class No():TransitionToOtherApp()
}
}
Intent起動処理
上記で作成したIntentを起動します。処理の流れは以下の通りです。
- 対象Activityが存在すれば、startActivityを実行する。
- 存在しないActivityをstartActivityしてしまうと、アプリがクラッシュします。
- intentから
browser_fallback_url
が取得できれば、そのURLを表示する。- こちらの処理について、詳しくはChromeのドキュメントを参照ください。
- GooglePlayStoreアプリがアクティブであれば(存在すれば)、GooglePlayStoreアプリを起動する。
- 上記全てがヒットしない場合、
https://play.google.com/store/apps/details?id=$applicationId
を表示する。
最後のhttps://play.google.com/store/apps/details?id=$applicationId
は、表示してもアプリをダウンロードできないので、なにかユーザにメッセージを表示するほうが適切かもしれません。
class ImplicitIntentStarter(private val wContext: WeakReference<Context>) {
fun startActivity(intent: Intent) {
val context = wContext.getNullable() ?: return
val packageManager = context.packageManager
if (intent.resolveActivity(packageManager) != null) {
context.startActivity(intent)
return
}
val fallbackUrl = intent.getStringExtra("browser_fallback_url")
if (fallbackUrl != null) {
// ブラウザ起動など
return
}
startGooglePlayStoreActivity(intent.`package`)
}
fun startGooglePlayStoreActivity(applicationId: String) {
val context = wContext.getNullable() ?: return
val packageManager = context.packageManager
val marketIntent = Intent(Intent.ACTION_VIEW).setData(Uri.parse("market://details?id=$applicationId"))
if (marketIntent.resolveActivity(packageManager) != null) {
context.startActivity(marketIntent)
return
}
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=$applicationId")))
}
}
// WeakReferenceから取得する値を null safe に扱うための拡張です。
fun <T> WeakReference<T>.getNullable(): T? = get()
WebViewのリクエストをインターセプト
最後に、WebViewのリクエストをインターセプトし、上記の処理を実装します。
private val validation = WebViewRequestValidation()
private val implicitIntentStarter = ImplicitIntentStarter(wContext)
val webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
// WebViewの画面遷移をさせたくない場合は true を返す。
val allowedRequest = false
val notAllowedRequest = true
val urlString = url ?: return notAllowedRequest
val url = Uri.parse(urlString)
validation.isTransitionToOtherApp(url).let {
when (it) {
is WebViewRequestValidation.TransitionToOtherApp.Yes -> {
implicitIntentStarter.startActivity(it.intent)
return notAllowedRequest
}
is WebViewRequestValidation.TransitionToOtherApp.No -> {}
}
}
return allowedRequest
}
}
webView.setWebViewClient(webViewClient)