NFCチップでAndroidアプリの特定の画面を開きたい

割とサクッとできたのでメモ
ミニCDで自作の音楽アプリを開きたかったので実践してみました。

## 用意するもの
- NFCチップ
    - Amazonでテキトーに安いやつを買った
- Android端末
    - Nothing Phone(2a)
    - NFC対応してれば多分なんでもいい
- Android studio
    - バージョンはLadybugだった

## NFC toolsアプリをインストール
Android端末の方にNFC toolsというアプリを入れます。
https://play.google.com/store/apps/details?id=com.wakdev.wdnfc&hl=ja&pli=1

このアプリで、NFCチップに色々書き込みができます。
まーじで簡単。URL入れて遊ぶだけでもとっても楽しい。

## NFCチップに書き込むレコードを作成

「書く」タブの、「レコードを追加」でNFCチップに書き込みたい情報を作成できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628307/551dc5bb-a6a7-0e72-b5fa-51ad897889c4.png)


今回必要なのは、①テキストデータと②アプリを直接開くアプリレコード？です。

①テキスト文字は、アプリを起動したときに開きたい画面のルート（画面遷移パス）を入れます。
今回は自作の音楽アプリだったので、特定の楽曲再生画面に遷移できるように指定しました。

②アプリレコードの方は、NFCにかざした時に開きたいアプリを選択します。USB接続をしたデバッグモードのアプリでも指定可能でした。

:::note warn
テキスト→アプリレコード、の順番じゃないとうまく動きませんでした

アプリを開いてからテキストデータを解析して...だから先にアプリレコードなんじゃないの？と思いましたが、なぜか先にテキストじゃないとうまく行きませんでした（なんで？）
:::

レコードを設定できたら、「書く」をタップしてスマホ端末をNFCチップにかざします。
これでNFCチップの設定は完了です。

ここまでは簡単

## アプリを立ち上げた時にテキストを解析して、一致すればその画面を表示する処理を入れる

NFCチップから読み取ったテキストデータにはメタデータも含まれており、そのまま渡すと開きたい画面に遷移してくれません。
なので、メタデータ部分をスキップする処理を入れないといけないのがちょいワナポイントでした。

``` NfcHandler.kt
class NfcHandler(
    private val navController: NavController?
) {
    fun handleNfcIntent(intent: Intent) {
        if (navController == null) {
            Log.e("NFC_DEBUG", "NavController is not initialized yet, retrying...")
            Handler(Looper.getMainLooper()).postDelayed({
                handleNfcIntent(intent)
            }, 100) // 100ms 後に再試行
            return
        }

        Log.d("NFC_DEBUG", "Intent received with action: ${intent.action}")

        if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) {
            val rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
            if (rawMsgs != null && rawMsgs.isNotEmpty()) {
                try {
                    val ndefMessage = rawMsgs[0] as NdefMessage
                    val ndefRecord = ndefMessage.records[0]
                    val rawPayload = ndefRecord.payload

                    // ペイロードの先頭 (メタデータ部分) をスキップ
                    val payload = String(rawPayload.copyOfRange(3, rawPayload.size)) // 3バイトをスキップ

                    // 読み取ったテキストをログに表示
                    Log.d("NFC_DEBUG", "Raw Payload: ${String(rawPayload)}")
                    Log.d("NFC_DEBUG", "Processed Payload: $payload")

                    val pathSegments = payload.split("/")
                    if (pathSegments.size == 3 && pathSegments[0] == "detail") {
                        val musicTitle = pathSegments[1]
                        val albumTitle = pathSegments[2]

                        Log.d("NFC_DEBUG", "Navigating to detail screen: $musicTitle, $albumTitle")

                        navController.navigate("detail/$musicTitle/$albumTitle")
                    } else {
                        Log.e("NFC_DEBUG", "Invalid payload format: $payload")
                    }
                } catch (e: Exception) {
                    Log.e("NFC_DEBUG", "Error reading NDEF message", e)
                }
            } else {
                Log.e("NFC_DEBUG", "No NDEF messages found")
            }
        } else {
            Log.e("NFC_DEBUG", "Unexpected intent action: ${intent.action}")
        }
    }
}

```

NFC_DEBUGで、自分の開きたい画面のルートを指定できているのを確認できればもう勝ち確と言っていいでしょう。あとはその画面を開くだけです。

## 雑な感想
ふとツイッターでミニCDを見かけて、自分でも作れるのでは？と思い実践
自分で作った曲だけを再生できるアプリを作っているのですが、ちょうど楽しく実験できました。

NFCチップにURLを書き込むだけだと本当にアプリ上で全部済んでしまうので、普通に自作の可愛いプレーリーカード作りたくなりました。