Kotlinでアプリ内のhtmlファイルを表示してJavaScriptでカメラを利用する方法

Android StudioでKotlinを使って、アプリ内のhtmlファイルを表示して、さらにJavaScriptでカメラを利用するための作業メモです。

# Android Studioでプロジェクト作成
1　Android Studioを起動して、「New Project」を選択します。
2　「Empty Views Activity」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/509199/8a649e7e-d076-bd94-7621-c862b113c379.png)
3　「Next」をクリックます。
4　「Name」にプロジェクト名を入力して「Finish」をクリックします。

# build.gradle.kts に androidx.webkit を追加
1　Gradleフォルダのbuild.gradle.ktsをダブルクリックしてコードを表示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/509199/23494844-6e9d-3078-2ff4-156ff3c27496.png)
2　「dependencies」に
　implementation("androidx.webkit:webkit:1.12.1")
　を追加します。この行で、[Alt+Shift+Enter]キーを押すと、
　implementation(libs.androidx.webkit)
　に表示が変わります。
3　さらに、画面上部に次に表示が出ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/509199/0093a812-5f16-00c1-0ab7-4712b4b6cbe6.png)
この表示内の「Sync Now」をクリックします。

# マニュフェストにカメラとインターネット許可を追加
　AndroidManifest.xmlにカメラとインターネットの許可を追加する。
 ```xml:
    <uses-feature
        android:name="android.hardware.camera"
        android:required="false" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.INTERNET" />
```
　追加後のAndroidManifest.xmlはこちら、
```xml:AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-feature
        android:name="android.hardware.camera"
        android:required="false" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.WebView01"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>
```
# カメラ画像を表示するhtmlを用意
　Androidの場合、htmlページ内でボタンクリックされないと、カメラ画像が表示されません。カメラ画像表示のためのボタンをつけておきます。
 　↓サンプルhtmlです。
```html:camera.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Camera Test</title>
  <style>
  canvas, video{ border: 1px solid gray; }
  </style>
</head>
<body>
  <h1>HTML5カメラ</h1>
  <video id="camera" width="300" height="200"></video>
  <canvas id="picture" width="300" height="200"></canvas>
  <form>
    <button type="button" id="shutter">シャッター</button>
  </form>
  <script>
    window.onload = () => {
    const video  = document.querySelector("#camera");
    const canvas = document.querySelector("#picture");
    video.setAttribute('autoplay', '');
    video.setAttribute('muted', '');
    video.setAttribute('playsinline', '');
    const constraints = {
      audio: false,
      video: {
      width: 300,
      height: 200,
      facingMode: "user"   // フロントカメラを利用する
      // facingMode: { exact: "environment" }  // リアカメラを利用する場合
    }
  };
  navigator.mediaDevices.getUserMedia(constraints)
    .then( (stream) => {
      video.srcObject = stream;
      video.onloadedmetadata = (e) => {
      video.play();
    };
  })
  .catch( (err) => {
    console.log(err.name + ": " + err.message);
  });
  // シャッターボタン
  document.querySelector("#shutter").addEventListener("click", () => {
    const ctx = canvas.getContext("2d");
    video.pause();  // 映像を停止
    setTimeout( () => {
      video.play();    // 0.5秒後にカメラ再開
    }, 500);
    ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
  });
};
</script>
</body>
</html>
```

# 表示するhtmlファイルをAndroid Studioに取り込み
1　AndroidStudioProjectsフォルダ内の、作成したプロジェクトの
　app > src > main フォルダ内に「assetsフォルダ」を作成します。
2　この中に表示したいhtmlファイルを入れます。
　assetsフォルダ内にcamera.htmlを入れた場合、次のように表示されます。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/509199/89c1b134-a891-c640-f15a-35fc48fe7853.png)


# activity_main.xml でhtmlを全画面表示にする
　htmlファイルを全画面表示にするため、activity_main.xmlに記入します。
```html:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
```

# MainActivity.ktにプログラムを記入
　カメラ利用のために、次の2つの許可が必要です。
 　・アプリ起動時のパーミッション許可のダイアログ表示
 　・WebViewでonPermissionRequestをオーバーライド
 ↓こちらのページを参考にコードを書きました。
https://pisuke-code.com/android-webview-launch-camera/
 ```kotlin:MainActivity.kt
package abc.def.cameratest

import android.content.pm.PackageManager
import android.os.Bundle
import android.webkit.PermissionRequest
import android.webkit.WebChromeClient
import androidx.appcompat.app.AppCompatActivity
import android.webkit.WebView
import androidx.core.app.ActivityCompat

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //xmlのWebViewで指定したid
        val webView: WebView = findViewById(R.id.webView)
        //javascriptを有効にする
        webView.settings.javaScriptEnabled = true
        // 表示するhtmlファイル
        webView.loadUrl("file:///android_asset/camera.html")
        webView.webChromeClient = object : WebChromeClient(){
            override fun onPermissionRequest(request: PermissionRequest) {
                /// パーミッション許可
                request.grant(request.resources)
            }
        };
        /// パーミッション許可を取る
        if (
            ActivityCompat.checkSelfPermission(
                this,
                android.Manifest.permission.CAMERA
            )
            != PackageManager.PERMISSION_GRANTED
        ) {
            ActivityCompat.requestPermissions(
                this, arrayOf(
                    //Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    android.Manifest.permission.CAMERA
                ),
                12345
            )
        }
    }
}
 ```
# ブラウザとWebViewの表示の違い
　WebViewを利用してhtmlファイルを表示した場合、次のような特徴があります。
### 〇アドレスが表示されません
　内部htmlを利用するので、これでOKです。
 
### 〇アンカーリンク（aタグ）でリンク先のページを表示します
→　内部htmlへのリンクの場合、アプリ内のWebViewで表示ができます。
→　外部サイトのurlの場合、アプリから標準のブラウザに切り替わってWebページを表示します。

### 〇カメラ画像を表示するには「ボタンアクション」が必須です
　PCのブラウザのように「html読み込み→カメラ作動」とはなりません。Androidのブラウザ（WebView）でカメラ画像を表示する場合には、html内のボタンアクションが必要です。
→　「こういうもの」という基本でhtmlを作成しましょう。

### 〇画面より表示が長い場合、スクロールバーが表示されます
　表示長が画面内に収まっているとき、タッチして上下をすると、画面がビヨーンと移動（バウンス）します。これを防ぐには、htmlファイルのcssでスクロール禁止にします。
```css:
html {
    overflow: hidden;
    }
```

### 〇２本指で拡大縮小（ピンチイン、アウト）ができます
　拡大縮小を禁止したい場合には、htmlファイルで設定します。
```html:
<meta content="user-scalable = no">
```
　このタグを記入すると、ページが拡大縮小できないようになります。

# 最後に
　表示幅、文字の大きさなどは実機で確認する必要がありますが、htmlで記載しているのでアプリ作成が容易になると思います。

　SwiftUIでアプリ内のhtmlファイルを表示してJavaScriptでカメラを利用する方法はこちらをご覧ください。
 https://qiita.com/mvm43236/items/3be95443ca5312a3f0dd