読者です 読者をやめる 読者になる 読者になる

urlを開く時にAndroid標準ブラウザを自動指定する試行錯誤

はじめに

Androidで複数のブラウザアプリをいれていると、アプリ選択画面が出てしまいますが

でも一部の広告とかは、端末の標準ブラウザを指定せずとも自動的に開く みたいな事をしている物があります

そこら辺の実装が気になったのでちょっと調べてみました

Chrome Custom Tabs つかえばええやん。。。

と思われる方も多いと思うのですが、*1

あれの実体は、「アプリの皮をかぶったChromeなので数点欠点があります

  1. target-15以上から
    1. 2/3系OSには使えない
  2. Intent連携の可能の余地が少ない
    1. Closeアイコン等もIntent指定モドキで指定できるが以下の制限があり
    2. https://material.io/icons/Google準拠のやつじゃないと基本弾かれる
  3. popupWindowが新しいTabのような形で開けない
    1. Android Chromeでも一部のリンクをChromeTabにしましたが遷移がおかしくなることも
    2. あれ、Chromeのタブの自動history機能がなかった場合、開いてたタブが戻せなくて閉じてしまうケースが多発してそう
  4. 一部のJSがエラーになる
    1. これに関してはChromeでエラーとなるパターンが有り、その場合はあえて 端末の標準ブラウザで開きたい みたいな形になるのかなと*2

3)4)に関しては 基本的にブラウザアプリでしか対処できない状態だったりします

参考

qiita.com

端末内の標準ブラウザというと

どうも端末ごとにメーカー独自でパッケージがバラバラみたいで中々厳しい状況だったりします

でも詳しくない一般ユーザ*3は、自分からChromeとかFirefoxとか入れないだろう というお話も

端末標準ブラウザ に限定できればテストの工数が削減されるみたいな話だったりも

実装の試行錯誤

2系端末で標準ブラウザで開くイメージ

  • 昔のGoogleのサイトに乗っていたやつ。
  • Emuratorとかであればこれで済みそうなんですけどね・・・
public boolean checkPackageName(String packageName) {
    PackageManager pm = getPackageManager();
    try {
        pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
        return true;
    } catch (NameNotFoundException e) {
        return false;
    }
}


private void showBrowser(String url) {
    if(TextUtils.isEmpty(url)) {
            return;
    }

    Uri uri = Uri.parse(url);

    try {
        //2系用標準ブラウザを明示的に指定する
        if(checkPackageName("com.android.browser")){
            Intent intent = new Intent(Intent.ACTION_VIEW,uri);
            intent.setPackage("com.android.browser");
            startActivity(intent);
            return;
        }
    } catch (Exception e) {
    }
}

TextUtilsに関しては、id:sakura_bird1 さんのブログを参照

規程のアプリが指定されている時

private void showBrowser(String url) {
    //〜略〜

    //デフォルトパッケージ名を指定する
    //  => 規定で開く設定 をしている場合に実行される
    try {
        Intent browser = new Intent(Intent.ACTION_VIEW, uri);
        ResolveInfo defaultResInfo =
                getPackageManager().resolveActivity(browser, PackageManager.MATCH_DEFAULT_ONLY);
        if (defaultResInfo != null) {
            browser.setPackage(defaultResInfo.activityInfo.packageName);
            startActivity(browser);
            return;
        }
    } catch (Exception e) {
    }

参考

qiita.com

規程のアプリが指定されていない時

private void showBrowser(String url) {
    //〜略〜

    List<ResolveInfo> resInfos = null;
    try {
        int flag;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            flag = PackageManager.MATCH_ALL;
        } else {
            flag = PackageManager.MATCH_DEFAULT_ONLY;
        }
        
        Intent intent = new Intent(Intent.ACTION_VIEW,uri);
        resInfos = getPackageManager().queryIntentActivities(intent, flag);

        //わからないときはOSまかせにする
        if(resInfos == null || resInfos.size() <= 1){
            startActivity(intent);
            return;
        }
    } catch (Exception e) {
    }

参考

手動で著名なブラウザアプリを弾く

すごく泥臭い手法・・・

private void showBrowser(String url) {
    //〜略〜

    //chorme,firefox,opera等を除く
    String[] excludeList ={
        "chrome",
        "firefox",
        "opera",
        "yahoo",
        "dolphin"
    };

    HashSet<ResolveInfo> runlist = new HashSet<>();
    for (ResolveInfo resInfo : resInfos) {
        boolean found_f = false;
        for (String exclude : excludeList) {
            if(resInfo.activityInfo.packageName != null && resInfo.activityInfo.packageName.indexOf(exclude) != -1){
                found_f = true;
                break;
            }
        }
        if(!found_f)runlist.add(resInfo);
    }

    //1つに絞り込めた
    if(runlist.size() == 1){
        try {
            Intent intent = new Intent(Intent.ACTION_VIEW,uri);
            ResolveInfo resInfo = (ResolveInfo)(runlist.toArray()[0]);
            String packageName = resInfo.activityInfo.packageName;
            intent.setPackage(packageName);
            startActivity(intent);
            return;
        } catch (Exception e) {
        }
    }

これでも通過してしまう時は。。。

private void showBrowser(String url) {
    //〜略〜

    //ブラウザアプリが複数入れられている場合は選ばせる
    Intent intent = new Intent(Intent.ACTION_VIEW,uri);
    startActivity(chooser);
}

or

ココまで来たらあえてユーザーに恣意的に選ばせるとか・・

private void showBrowser(String url) {
    //〜略〜

    //ブラウザアプリが複数入れられている場合は選ばせる
    Intent intent = new Intent(Intent.ACTION_VIEW,uri);
    final Intent chooser = Intent.createChooser(intent, "Browser");
    startActivity(chooser);
}

でもまあ、なんか微妙ですね・・・

*1:特にセキュリティ界隈では 神ツールやw みたいに拝まれていますが・・

*2:WebViewも同じような理由でJSエラーとかで敬遠される。。

*3:自分の親の世代とか・・今ガラケーの機種変ができないらしくスマホ機種変を強制されるらしい