LoginSignup
4
5

More than 3 years have passed since last update.

Android WebView から別アプリを開く

Posted at

AndroidのWebViewではtelmailtoなどのリンクを踏んでも電話アプリやメールアプリが起動しないためそれに対応するための実装が必要です。

また暗黙的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を表示する。
  • 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)
4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5