AndroidAppLinksでアプリを開く

## 概要
業務でAndroidAppLinks（以下AAL）を使ったのでまとめてみます。
主な内容は以下の通り
- DeepLinkとの違い
- assetlinks.json の置き場所
- 署名毎の対応
- 反応して欲しいURLの正規表現の書き方とその限界について
- 他のアプリも同じURLで反応する場合の挙動

## DeepLinkとの違い
https://developer.android.com/training/app-links?hl=ja
DeepLinkで複数のアプリが反応した場合、どのアプリで開くか尋ねられる。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204184/53f15e4f-f974-a4b7-85ad-8dfd323aef2a.png" width="300px"/>

AALではどのアプリで開くか尋ねられることなくアプリを直接起動させることができる。

## assetlinks.json の置き場所
自分の持つドメインの直下に `.well-known/assetlinks.json` を設置する
https://test.com が自分のドメインであれば https://test.com/.well-known/assetlinks.json に json ファイルを設置する。

弊社ではテスト環境用の `stg.test.com`　`dev.test.com` と本番環境用の `test.com` が存在したため、それぞれのホストの直下に assetlinks.json を置く必要があった。

## 署名毎の対応
弊社では本番環境に加え、stg,dev環境の計三つの環境（ドメイン）が存在した。
また、それぞれAndroidのプロジェクト内でフレーバー、署名が分かれていたため
ドメイン毎に別のassetlinks.jsonを用意する必要があった。

### assetlinks.json の注意点
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204184/95b04b06-69cb-2207-19c4-a7b6a4d0ddc1.png" width="800px"/>

注意点
①フレーバーがあっているか
②署名は正しいか（debugのままになっていないか）

### フレーバー毎に反応するURLを変える

URLのホストが3つあるが、本番ビルドでは `stg.test.com`,`dev.test.com`に反応して欲しくない。
そこでURL Mappingで以下のように設定した。


hostName の箇所を変数にして、`build.gradle` で代入する方法を取った。

``` AndroidManifest.xml
<intent-filter>
    <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="${hostName}"
        android:path="/android_app_links" />
</intent-filter>
```

``` build.gradle
productFlavors {
    dev {
        manifestPlaceholders = [hostName:"dev.test.com"]
    }
    stg {
        manifestPlaceholders = [hostName:"stg.test.com"]
    }
    pro {
        manifestPlaceholders = [hostName:"test.com"]
    }
}
```

https://developer.android.com/studio/build/manifest-build-variables?hl=ja

こうすることで、Devのフレーバーで起動したときには `dev.test.com` のホストのIntentFilterにしか反応しなくなるのでAALの動作もそれに従います。

## 反応して欲しいURLの正規表現の書き方とその限界について
弊社の仕様の要望は以下の通りだった

反応して欲しいURL

```
https://test.com/10文字の英数字
https://test.com/10文字の英数字_桁数不明の数字

- 例
https://test.com/Ki9s7kPoiu
https://test.com/Ki9s7kPoiu_299281
```

反応してほしくないURL

```
https://test.com/10文字の英数字/*
https://test.com/10文字の英数字_桁数不明の数字/*

- 例
https://test.com/Ki9s7kPoiu/image
https://test.com/Ki9s7kPoiu_299281/login
```

普通に正規表現が使えれば Path に対し以下のような条件が付けられる

```
/[a-zA-z0-9]{10}$
/[a-zA-z0-9]{10}_[0-9]+$
```

早速この正規表現をIntentFilterのPathPatternに記入してみるが動作しない。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204184/08d70216-b387-33d8-5358-adc341d9f19e.png" width="800px"/>

なんならURL MappingのPathPatternのプレースホルダーにある `/[a-z]+` すら動作しない始末
いろいろ調べたが、そもそもこのPathPatternは正式な正規表現ではない。
そのため限られた条件しか付けられないことがわかった。

IntentFilterで使えるPathPatternの文字は `.` と `*` だけだった。
`.` は任意の一文字、`*` は直前の文字が0個以上続くという意味。
つまり私がやりたかった `/10文字の英数字` という条件は書けなかったので、妥協して`10文字である`という条件でのみ絞ることにした。
しかし繰り返しを表す `{10}` もつかえないのでPathPatternは以下のようになった。

```
/..........
（.が10個）
```

また、`/10文字の英数字_桁数不明の数字`は

```
/.........._.*
```
となった。
もし `.` `*` これ以外に使える文字があれば教えて欲しい。

## 他のアプリも同じURLで反応する場合の挙動
弊社にはアプリが2つあり(以下AとBとする)、それぞれのアプリが同じURLに対してAALで反応する。
もしユーザーの端末にAとBどちらのアプリもインストールされている場合はAを優先して起動したいという要望があった。

一瞬話が逸れるが、iOSではAALと同じような仕組みであるUniversal Links（以下UL）というのがある。
https://qiita.com/mono0926/items/2bf651246714f20df626
これはサーバー側に置くjsonファイル `apple-app-site-association` にどのようなURLにアプリが反応するかを記述する。
その際に、先に反応して欲しいアプリを `apple-app-site-association` の先頭に記述することで優先度付けを行うことができた（公式なやり方ではないが）

iOSでできるのであればAndroidでもできるだろうと思い調べるが、優先度についてどこにも書かれていない。
また、 `assetlinks.json` の記述順を変えても優先度をつけることはできなかった。
あとIntentFilterのorderも試したが、当然意味がなかった。
結果としてはA、Bどちらのアプリを優先的に起動するかを設定することはできなかった。

ちなみにA,Bどちらのアプリもインストールした状態でAALが動作すると毎回ランダムに起動するわけではなく、後にインストールした方のアプリが優先的に起動する。

## 最後に
いろいろ妥協する場面があったが、反応したいURLの正規表現なんかはURLをAALありきで設計すれば回避できるし、ほとんどの使い方で問題になることはなさそう。
署名やフレーバーが細かく分かれていると、どこに何を置くのかが分からなくなるくらいで、それに気をつければつまずくこともないかと思います。
同じURLで起動するアプリの優先度を設定できない問題は回避する策が今のところ私は知らないので、もし知っている人がいたらコメントください。