【Android/Kotlin】Bluetoothをスキャン Bluetoothの機能を有効にする

## 今回は、以下の挙動をするアプリを作成する
1. アプリが起動すると、画面には「スキャン」というボタンが表示される
2. 「スキャン」ボタンが押されると、Bluetoothの許可を求めるダイアログが表示される
3. ユーザーがBluetoothの許可を与えた場合、Bluetooth機能が有効かどうかをチェックする
3. Bluetooth機能が無効であれば、ユーザーにBluetoothを有効にするように促すダイアログを表示する
4. Bluetoothが有効であれば、「BT接続できます」というメッセージが表示される

## 実装
**AndroidManifest.xml** に以下を追加
```kotlin
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
```

**MainActivity** 
Bluetoothをスキャン(Bluetoothを使用して通信可能な近くのBluetoothデバイスを検索すること)するために必要な許可をユーザーに求め、許可された場合にBluetoothの機能を有効にする処理を行う
```kotlin
class MainActivity : AppCompatActivity() {
    private var btPermission = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    fun scanBt(view: View) {
        val bluetoothManager: BluetoothManager = getSystemService(BluetoothManager::class.java)
        val bluetoothAdapter: BluetoothAdapter? = bluetoothManager.adapter

        if (bluetoothAdapter == null) {
            Toast.makeText(this, "BT接続が許可されていません", Toast.LENGTH_LONG).show()
        } else {
            if (VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                blueToothPermissionLauncher.launch(android.Manifest.permission.BLUETOOTH_CONNECT)
            } else {
                blueToothPermissionLauncher.launch(android.Manifest.permission.BLUETOOTH)
            }
        }
    }

    private val blueToothPermissionLauncher = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { isGranted: Boolean ->
        if (isGranted) {
            val bluetoothManager: BluetoothManager = getSystemService(BluetoothManager::class.java)
            val bluetoothAdapter: BluetoothAdapter? = bluetoothManager.adapter
            btPermission = true

            if (bluetoothAdapter?.isEnabled == false) {
                val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
                btActivityResultLauncher.launch(enableBtIntent)
            } else {
                btScan()
            }
        } else {
            btPermission = false
        }
    }

    private val btActivityResultLauncher = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()
    ) {
        if (it.resultCode == RESULT_OK) {
            btScan()
        }
    }

    private fun btScan() = Toast.makeText(this, "BT接続できます", Toast.LENGTH_LONG).show()
}
```

### ひとつづつ解説していく
```kotlin
private var btPermission = false
```
- Bluetoothアクセスの許可があるかどうかを示すブール変数
- 初期値は `false`

```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
}
```
- `onCreate`関数はアクティビティが最初に作成されたときに呼び出される
- ここではレイアウトを設定しているだけ
- `R.layout.activity_main`を紐づけている


```kotlin
fun scanBt(view: View) {
        val bluetoothManager: BluetoothManager = getSystemService(BluetoothManager::class.java)
        val bluetoothAdapter: BluetoothAdapter? = bluetoothManager.adapter

        if (bluetoothAdapter == null) {
            Toast.makeText(this, "BT接続が許可されていません", Toast.LENGTH_LONG).show()
        } else {
            if (VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                // Manifestはandroidの方
                blueToothPermissionLauncher.launch(android.Manifest.permission.BLUETOOTH_CONNECT)
            } else {
                blueToothPermissionLauncher.launch(android.Manifest.permission.BLUETOOTH)
            }
        }
    }
```

**このメソッドがおこなっていること**
- Bluetoothが有効になっている場合はBluetoothスキャンを開始し、それ以外の場合はBluetoothアクセスの許可をリクエストする

**ひとつひとつ説明する**
- `BluetoothManager` オブジェクトを取得する
- `BluetoothManager`は、Bluetooth関連の機能を提供するAndroidのAPIの一部で、Bluetoothデバイスとの通信を行うために必要
- `BluetoothManager` は、Bluetoothデバイスの検出を管理するのに使用する
- `bluetoothAdapter` オブジェクトを`BluetoothManager`から取得する
- `bluetoothAdapter` は、Bluetoothデバイスとの通信を管理するクラスで、Bluetooth接続の許可を確認するために必要
- もし`BluetoothAdapter`が`null`であれば、「BT接続が許可されていません」というトーストメッセージが表示する
- `BluetoothAdapter`が`null`である場合、Bluetoothにアクセスするために必要な許可がユーザーに与えられていないことを意味する
- `BluetoothAdapter`が`null`でなければ、SDKのバージョンに応じてBluetooth接続の許可を確認するための許可要求を送信する
- Android APIバージョンが11以上であれば `BLUETOOTH_CONNECT`パーミッションをリクエストし、それ以外の場合は `BLUETOOTH`パーミッションをリクエストする(APIバージョンによって、サポートされているパーミッションが異なるため)
- それぞれの要求は、`blueToothPermissionLauncher.launch()`メソッドを介して行われる


```kotlin
private val blueToothPermissionLauncher = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { isGranted: Boolean ->
        if (isGranted) {
            val bluetoothManager: BluetoothManager = getSystemService(BluetoothManager::class.java)
            val bluetoothAdapter: BluetoothAdapter? = bluetoothManager.adapter
            btPermission = true

            if (bluetoothAdapter?.isEnabled == false) {
                val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
                btActivityResultLauncher.launch(enableBtIntent)
            } else {
                btScan()
            }
        } else {
            btPermission = false
        }
    }
```
**このメソッドがおこなっていること**
- Bluetooth接続のために必要なパーミッションのリクエストを処理する

**ひとつひとつ説明する**
- `registerForActivityResult` は、アクティビティでアクティビティ結果を受信するためのコールバックが含まれる
- このコールバックは、ユーザーがパーミッションリクエストを許可または拒否した場合に呼び出される
- `isGranted` パラメータはBluetoothアクセスが許可されたかどうかを示し、Bluetoothアクセスが許可されている場合は、Bluetoothアダプタが有効になっているかどうかを確認し、有効であればBluetoothスキャンを開始する
- `BluetoothManager`と`BluetoothAdapter`のインスタンスを取得し、`isEnabled`メソッドを使用してBluetoothが有効になっているかどうかを確認する
- Bluetoothが無効になっている場合は、`ACTION_REQUEST_ENABLE`というBluetoothを有効にするためのIntentを作成し、`btActivityResultLauncher`に対して送信する
- Bluetoothが有効になっている場合、`btScan`関数が呼び出される
- Bluetoothへのアクセスを許可された場合は、`btPermission`フラグを`true`に設定する
- パーミッションが拒否された場合は、`btPermission`フラグを`false`に設定する


```kotlin
 private fun btScan() = Toast.makeText(this, "BT接続できます", Toast.LENGTH_LONG).show()
```
- `btScan` 関数は、Bluetoothスキャンが完了したときに表示されるメッセージを定義する


**activity_main.xml**
```html
<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="スキャン"
        android:onClick="scanBt"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

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


![scan.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917260/64e01b4a-cf06-bd44-8038-d02af7d8d5c7.png)

![on.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917260/26f92e14-be63-3558-a705-9e49abc0a78e.png)



## おわりに
Bluethoothを仕事で使用することがありそうな予感がしたので、勉強してみました。
Bluethoothの接続を促すという初歩の初歩ができるようになりました。

がんばるぞい！


## 参考
- [Bluetooth の概要](https://developer.android.com/guide/topics/connectivity/bluetooth?hl=ja)
- [Bluetooth通信が可能か確認する処理の作成](https://www.hiramine.com/programming/bluetoothcommunicator/01_is_bluetooth_enabled.html)
- [パーミッション の Normal と Dangerous の違い](https://qiita.com/kaleidot725/items/b4f3240e1c0eeeae9962)