Firebase App Distribution のフィードバック機能を試してみる

ベータ版ですが使えるようになった
Firebase App Distribution のテスターからのフィードバック機能を試してみました。

![イメージ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/b92a6291-092c-cf29-3ec2-2c6b91bfc920.png)


https://firebase.google.com/docs/app-distribution/collect-feedback-from-testers

2023-03-06 のリリースノートに新機能として記載されています:

https://firebase.google.com/support/releases#appdistro-in-app-feedback-android-beta

:::note warn
**ベータ版なのでこの内容は変わるかもしれません。2023年5月現在の内容です。**
:::

## セットアップ

公式のガイドに沿ってセットアップしてみます。
まず、`app/build.gradle(.kts)` に依存を追加します。超簡単です:

```kotlin:app/build.gradle.kts
// API のみのライブラリ
implementation("com.google.firebase:firebase-appdistribution-api-ktx:16.0.0-beta08")
// SDK の完全な実装（オプション）
// これはフィードバックが必要なベータ版のビルドバリアントにのみ依存に含めるようにします。
betaImplementation("com.google.firebase:firebase-appdistribution:16.0.0-beta08")
```

:::note info
BoM には含まれていなさそうで `Could not find com.google.firebase:firebase-appdistribution-api-ktx:.` となるので直接バージョンを指定しています。
:::

さて、ここでポイントなのが、「API のみのライブラリ」のほうと「SDK の完全な実装」の 2 つがあり、
「SDK の完全な実装」はオプションっぽく書かれているので依存は要らないのかな？ と思いきや、
そんなことはなくて、これが依存に含まれてない場合はフィードバック機能が起動しません。

コード的にもこの依存がない場合はスタブの空実装が使われるようになっていて、
エラーにはならないけどなにも起こらないようになっています:

```java:FirebaseAppDistributionProxy.java
/**
 * This implementation of the Firebase App Distribution API proxies to the real implementation
 * ({@code FirebaseAppDistributionImpl}) provided by the {@code
 * com.google.firebase:firebase-appdistribution} artifact. If that artifact is not included in the
 * build, then the stubs will return failed {@link Task Tasks}/{@link UpdateTask UpdateTasks} with
 * {@link FirebaseAppDistributionException.Status#NOT_IMPLEMENTED}.
 */
```

```java:FirebaseAppDistributionProxy.java
  public FirebaseAppDistributionProxy(
      Provider<FirebaseAppDistribution> firebaseAppDistributionImplProvider) {
    FirebaseAppDistribution impl = firebaseAppDistributionImplProvider.get();
    delegate = impl != null ? impl : new FirebaseAppDistributionStub();
  }
```

https://github.com/firebase/firebase-android-sdk/blob/28e3a6821910a0ae452fbcc5b173fafe919ddbaf/firebase-appdistribution-api/src/main/java/com/google/firebase/appdistribution/internal/FirebaseAppDistributionProxy.java#L29-L43

したがって、「SDK の完全な実装」については、
ベータのフィードバックが必要なビルドバリアントでだけ含まれるようにしておけば、
「API」がすべてに含まれていて呼出されても動かなくて大丈夫という仕組みになっているようです。

(プロダクションのビルドに API だけとはいえ入ってるのは気持ちが悪いと感じもしますが、
 `MainActivity#onCreate` でフィードバック通知を立ち上げるユースケースが多いので、
 そのあたりを何も考えずにできるようにおもてなしがされているのでしょうか)


## 準備

なにより先に Firebase App Testers の API をダッシュボードから有効にしておきます:

https://console.cloud.google.com/apis/library/firebaseapptesters.googleapis.com

![Firebase App Testers API を有効にする](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/9402f9ba-931f-afa8-96c7-61c0e53dc4b9.png)

次に、App Distribution に配布前に
ローカルで動作確認するときはデバッグモードを有効にしておく必要があります:

```sh
adb shell setprop debug.firebase.appdistro.devmode true
```

実際にフィードバックを送るときはこれは無効じゃないと
送れなくなってしまうので `false` に戻しておきます。


## 実装

実際にお手軽なくみこみフィードバック通知を試してみます。
まず、`MainActivity#onCreate` でフィードバック通知を表示するようにします:

```kotlin:MainActivity.kt
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        Firebase.appDistribution.showFeedbackNotification(
            "みんなフィードバック送ってね！",
            InterruptionLevel.HIGH,
        )
        // ...
    }
}
```

あとは通知の権限が要るので `android.permission.POST_NOTIFICATIONS` の処理を追加します:

```xml:AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

    <application></application>
```

```kotlin:MainActivity.kt
    private val requestPermissionLauncher = registerForActivityResult(
        ActivityResultContracts.RequestPermission(),
    ) {
        if (it) {
            showFeedbackNotification()
        }
    }

    private fun checkPermissions() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return
        if (
            ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.POST_NOTIFICATIONS,
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
        }
    }
```

これでビルドして動作を確認してみましょう！
(実際にフィードバックを送るときは Firebase App Distribution にアップロードしましょう！)


## 動き

表示してみた感じはこうなります:

![フィードバック通知](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/02075236-52e6-44e4-3b9d-72ea0f7b6a2d.png)

`InterruptionLevel.HIGH` なのでヘッドアップ通知になっていますが、
`showFeedbackNotifcation` の引数で指定したレベルの通知で表示できます。

システムの言語によって文言は変化します:

![日本語のフィードバック通知](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/30cb34b3-3cbc-383f-db41-20b465e542e9.png)

それをタップすると、初回は Google アカウントへのログインが求められました。

![ログイン](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/62c1e6e9-6aa5-3387-8ae2-05d19468315d.png)

対象の App の Firebase App Distribution への権限のないアカウントでは続行できませんので、
事前にテスターとして追加しておくようにします:

![oops](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/dadde3fd-894e-2cf4-b275-3c2e6a789bcf.png)

![ログイン完了](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/2145aea9-d967-84de-6fe5-6a125c1ac877.png)

ログインして権限があることが認証されると、
フィードバックの `Activity` が起動してフィードバックを書けるようになります。

![フィードバックフォーム](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/4660aeb8-eff4-7885-3ef6-23815e914df7.png)

ルックスはまあまあ素朴すぎる感じもしますね。

上から、フィードバック入力欄がまずあります。
ここに肝腎要のフィードバックを書き込むことができます:

![入力中のフォーム](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/b797b921-79cd-fc6a-93d7-762ab96600a5.png)

最初どこが入力欄かわからなかったのですがここでした。

中間にはさっき表示していた画面のスクリーンショットが撮られています。
`GALLERY` ボタンを押すと、文字通りギャラリーアプリが起動して任意の画像を選択できます:

![スクリーンショット部分](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/4f77a01e-9aed-f635-b5db-1d6878932f87.png)

スクリーンショットのチェックを外すと、
スクリーンショットを含まない選択もできるようですね:

![スクショなし](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/224e0068-205f-3365-55a0-4033e998c388.png)

一番下にはさっきコードで指定したユーザー向けのメッセージがここに表示されています。
てっきり通知のメッセージだと思ったらここだったのですね、という感じです:

![notice](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/2a434d53-7f10-f0ac-b087-c460bbb9e4ad.png)

右上の送信ボタンを押すとフィードバックを送信できます:

![フィードバックの送信](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/14110daf-618f-6816-58db-beb970d700cc.gif)


送信されたフィードバックはコンソールから確認できます:

![コンソールでのフィードバック確認](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/ae53bb35-a62e-1f5f-de99-8c9501a1a446.png)

フィードバックはほかにメールでも受け取れるし、
Firebase Cloud Functions で任意の関数をトリガーして
別のサービスにフォワードすることも可能そうです。
この辺はやり方しだいで柔軟に使えそうですね。
(まだ試してないので推測です！ 試してみたい)


## 所感

まず、通知のフィードバックの実装はとても簡単でよさそうですよね。
そのほかにも `Firebase.appDistribution.startFeedback` を実行することで、
アプリケーション側の任意のタイミングでフィードバックを開始することもできます。
 
サンプルにはスクショを撮ったらフィードバックを起動するコードもあり、
工夫次第でアプリにあったかたちでフィードバックを送るカスタマイズもできそうです:

https://github.com/firebase/firebase-android-sdk/blob/master/firebase-appdistribution/test-app/src/main/kotlin/com/googletest/firebase/appdistribution/testapp/ScreenshotDetectionFeedbackTrigger.kt


しかしながら、フィードバックのプレースホルダというか、
テンプレートを指定して起動できないのはちょっと残念ですね……。

こういうイメージ↓
![テンプレートのイメージ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359439/1ffb3fbf-6d9b-34d6-0fad-99447de1f44c.png)


ここにチームにあった入力フォーマットのテンプレみたいな運用もできると
より便利になりそうだな〜と感じました。

たとえば、アカウントの ID とかそういうのを
App 側からフィードバックコメントに含めておけたりしても便利ですよね。

ベータ版なのでこれからそういうのもできるようになりそうで、
今後のますますの発展に期待ですね。以上です。