AndroidでQRコードを読み取る

# QRコードを読み取るには？

最近はAndroid端末に標準でQRコードが読めるカメラアプリ、アプリ内でもQRコード読取の機能を持ったものがありますが、どうやって実現しているのでしょうか？調べてみました。

使用するライブラリが二種類あります。まずはカメラで画像を読み込むライブラリ。読み込んだ画像をQRコードとして解析するライブラリです。

## カメラライブラリ
本家 Android developer
[Choose a camera library](https://developer.android.com/training/camera/choose-camera-library?hl=ja)
ライブラリは3種類あって、よほどの理由がない限り、CameraXを使用しろと書いてあります。Cameara2はローレベルAPI(CameraXはCamera2の上に構築されている)なのでローベルのカメラの制御が必要な場合のみ使用するのがお勧めだと。

* [CameraX](https://developer.android.com/training/camerax?hl=ja)
* [Camera2](https://developer.android.com/training/camera2?hl=ja)
* [Camera (サポート終了)](https://developer.android.com/training/camera-deprecated?hl=ja)

# バーコード解析ライブラリ

本家 Android developerで既に用意されています。
[ML Kit Analyzer](https://developer.android.com/training/camerax/mlkitanalyzer?hl=ja)
[ML Kit Android リファレンス](https://developers.google.com/ml-kit/reference/android)
ML Kit AnalyzerはQRコード以外のバーコードの解析にも使えます。

既に、[gitHub](https://github.com/android/camera-samples)にサンプルコードも用意されていて、ここのCameraX-MLKitをAndroidStudioで開くとすぐ実行できます。
![Screenshot_20221215_110916.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933408/0e991071-6e7c-73c8-f91f-c6a860aa804b.png)
ここまでお膳立てができていればあとは応用は簡単なのですが、QRバーコードは仕様として、日本語全角文字も可能です。ところが、QRバーコードのデータに日本語全角文字を使用すると読み取ってくれませんでした。
QRコードの仕様を色々調べてみると

QRコードの正式な仕様
* JIS X 0510
* ISO/IEC18004

は有料じゃないと仕様書をみることができないので
[QRコードに使用できる文字](https://www.cman.jp/QRcode/faq/qa2_character_type/#:~:text=QR%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AF%E3%80%8CJIS%20X,%E3%81%9F%E3%82%81%E6%B3%A8%E6%84%8F%E3%81%8C%E5%BF%85%E8%A6%81%E3%81%A7%E3%81%99%E3%80%82)

> QRコードは「JIS X 0208」基準の文字を使用できます。

上記のページだと、JIS X 0208とあります・・・なんだかカオスな文字コードの世界に入ってしまうのですが、JIS X 0208は文字集合を意味します。
符号化でいうと・・・ここらへんからカオスになってきます。専門家の視点から言えば違ううんじゃねぇ？的なご意見もあると思いますが、今回はQRコードにフォーカスした記事なのでご容赦ください

* Shift-JIS(ニアリーイコールで)
* UTF-8
* EUC

QRコードはEUCはダメらしいです。(なんで？)一般的にはShift-JISかUTF-8らしいです。しかも、バーコードを作成する側のプログラムと読み取る側のプログラムで合わせる必要があります。合わないと文字化けします。

# googleのCameraXとML Kit Analyzerのサンプルはどうなってる？

QRコードの仕様としては全角文字もサポートしているのに、ML Kit Analyzerのライブラリがサポートしていないのは変だな？と思いました。
少々、当てずっぽうですが、一部のソースプログラムを以下のように書き換えると全角文字も読み取れるようになりました。

```diff_kotlin
x_mlkit/QrCodeViewModel.kt
index 8f4d052..be7c927 100644
--- a/CameraX-MLKit/app/src/main/java/com/example/camerax_mlkit/QrCodeViewModel.kt
+++ b/CameraX-MLKit/app/src/main/java/com/example/camerax_mlkit/QrCodeViewModel.kt
@@ -51,7 +51,7 @@ class QrCodeViewModel(barcode: Barcode) {
             // Add other QR Code types here to handle other types of data,
             // like Wifi credentials.
             else -> {
-                qrContent = "Unsupported data type: ${barcode.rawValue.toString()}"
+                barcode.displayValue?.let { qrContent = "barcode: ${barcode.displayValue}" }
             }
         }
     }
```
![Screenshot_20221215_131446.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933408/d20b8e39-0f73-f438-32ff-f37547d39a17.png)

## 何が違うのか？
APIを調べてみると

[public String getRawValue ()](https://developers.google.com/android/reference/com/google/mlkit/vision/barcode/common/Barcode#getRawValue())
> Returns barcode value as it was encoded in the barcode. Structured values are not parsed, for example: 'MEBKM:TITLE:Google;URL://www.google.com;;'.
>
> It's only available when the barcode is encoded in the UTF-8 format, and for non-UTF8 ones use getRawBytes() instead.
>
> Returns null if the raw value can not be determined.

[public String getDisplayValue ()](https://developers.google.com/android/reference/com/google/mlkit/vision/barcode/common/Barcode#getDisplayValue())
> Returns barcode value in a user-friendly format.
> 
> This method may omit some of the information encoded in the barcode. For example, if getRawValue() returns 'MEBKM:TITLE:Google;URL://www.google.com;;', the display value might be '//www.google.com'.
>
> This value may be multiline, for example, when line breaks are encoded into the original TEXT barcode value. May include the supplement value.
>
> Returns null if nothing found.

getRawValueはUTF-8でエンコードされている場合のみ使用できるとあります。なんで、UTF-8で決め打ちしているんだろう？
それに対して、getDisplayValueは「ユーザーフレンドリーな形式で」って書いてありますが、この「ユーザーフレンドリー」って、玉虫色で正確には何を指しているのかよくわかりません。

QRコードを作成するツールがWindows版なので、おそらくQRコードはShift-JISでエンコードされているのだと推測されます。getRawValue()では読めないけど、getDisplayValue()だとShift-JISは読めるようです。

QRコードが半角文字しかないのであれば、どちらでも構いません。