0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Androidで外部ブラウザを起動する際に考慮したこと

Posted at

Androidで外部ブラウザを起動させる実装をしました。
単純な実装ではあるのですが、少しはまるポイントがあったのでメモしておきます。

ブラウザ開くだけ

外部ブラウザを起動する際には、Intentを使用します。
単純に開くだけなら以下のような形になります。
※今回のコードではActivityの拡張関数を作成する形で進めます。

Activity+Extension.kt
fun Activity.openBrowser(url: String) {
    val intent = Intent(Intent.ACTION_VIEW, url.toUri())
    startActivity(intent)
}

これだけで十分ではあるのですが、開けるブラウザアプリがない場合もあります。
Android端末ならChromeなどがデフォルトでインストールされていると思いますが、設定で無効にすることができてしまうためです。
そのためその場合の考慮をします。

ブラウザアプリが存在するかどうかを確認する

渡されたURIを処理するためのアクティビティを知るためには、queryIntentActivitiesまたはresolveActivityを使用します。

Activity+Extension.kt
fun Activity.openBrowser(url: String) {
    val intent = Intent(Intent.ACTION_VIEW, url.toUri())
    
    val resolveActivity = intent.resolveActivity(packageManager)
    if (resolveActivity != null) {
        startActivity(intent)
    } else {
        Toast.makeText(
            this,
            "有効なブラウザが見つかりません。",
            Toast.LENGTH_SHORT
        ).show()
    }

    // 以下でもよい
    val activities = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
    if (activities.isNotEmpty())
        // 省略
}

使用するのはどちらでも良いですが、queryIntentActivitiesの場合は条件を満たすアクティビティをすべて探してきてリストで返し(空ならemptyList)、resolveActivityは条件を満たす最適なアクティビティを返します(なければnull)。

ブラウザアプリを開くような場合の動作はほぼ同じで、デフォルトで設定されているブラウザアプリを開きます。なのでどちらでも良いです。

1点注意として、Android11以降ではほかのアプリの情報を知るPackageManagerの使用が厳格になったため、AndroidManifest.xmlに以下のようにスキーム情報を記載しておくことが必要になります。

AndroidManifest.xml
<manifest ... >
    <queries>
        <intent>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="http" />
        </intent>
        <intent>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="https" />
        </intent>
    </queries>
    ...
</manifest>

これがないと、queryIntentActivitiesresolveActivityが空やnullで返ってきてしまいます。

はまったポイント

YoutubeやX、Amazonのようなブラウザからディープリンクで専用ネイティブアプリにリダイレクトするようなURLが渡された場合です。

そのアプリがインストールされていなければ問題なくブラウザで開くのですが、インストールされているとうまく開くことができません。
そのため以下のように指定します。

Activity+Extension.kt
    // MATCH_ALLに変更
    val activities = packageManager.queryIntentActivities(intent, PackageManager.MATCH_ALL)
    if (activities.isNotEmpty()) {
        startActivity(intent)
    } else {
        Toast.makeText(
            this,
            "有効なブラウザが見つかりません。",
            Toast.LENGTH_SHORT
        ).show()
    }

MATCH_DEFAULT_ONLYは通常使われるアプリだけ探すので基本的に推奨されるのですが、URIからほかのアプリを開く可能性がある場合はMATCH_ALLを使用したほうがよさそうです。

ちなみにこの場合はresolveActivityで同じことをするのは難しそうでした。
(他の解決方法があるかもしれません)

これによって、専用アプリがある場合はそちらで、ない場合はブラウザで開くことが可能になりました。

おまけ:URLが有効な形式かどうか判定

1 . Patterns.WEB_URL

Android標準のutilを使用して、URL形式「っぽい」か判別します。

Activity+Extension.kt
import android.util.Patterns

fun isValidUrl(url: String) = Patterns.WEB_URL.matcher(url).matches()

注意したいのが、このutilは"https://example.com"のようなものはもちろんのこと、"example.com"のようなものも有効なurlだと判定してしまうことです。そこで2です。

2 . スキームでチェックする

httpかhttps形式を持っているのかをチェックすることで、より厳格になります。

Activity+Extension.kt
import android.net.Uri

fun isValidHttpUrl(url: String): Boolean {
    return try {
        val uri = Uri.parse(url)
        val scheme = uri.scheme
        (scheme == "http" || scheme == "https") && Patterns.WEB_URL.matcher(url).matches()
    } catch (e: Exception) {
        false
    }
}

3 . URLクラスでパースできるかを見る

2よりもさらに厳格にみる場合、URLクラスでURLの構文として正しいかをチェックし、ダメそうならMalformedURLExceptionを返すようにするのも良いです。

Activity+Extension.kt
import java.net.MalformedURLException
import java.net.URL

fun isStrictValidUrl(url: String): Boolean {
    return try {
        val parsedUrl = URL(url)
        parsedUrl.protocol == "http" || parsedUrl.protocol == "https"
    } catch (e: MalformedURLException) {
        false
    }
}

以上です。

参考

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?