【Kotlin/Android Studio】Bluetoothのパーミッション周りの実装方法!

【Kotlin/Android Studio】Bluetoothのパーミッション周りの実装方法!

この記事からわかること

  • Android StudioBluetooth機能を利用する
  • パーミッション(権限)周り実装方法
  • Android9(APIレベル28)で必要になる権限は?

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

環境

公式:Bluetoothの概要

公式リファレンスに乗っているBluetoothのパーミッション周りの実装方法を自分なりにまとめておきたいと思います。

Bluetooth機能実装に必要なパーミッション

Androidアプリからデバイスの機能などを利用する際にはパーミッションを宣言する必要があり、レベルがdangerousのものはユーザーに対して許可申請ダイアログを表示し承諾を得る必要があります。

おすすめ記事:【Kotlin/Android Studio】パーミッション(権限)許可のリクエスト方法!requestPermissions

【Kotlin/Android Studio】パーミッション(権限)許可のリクエスト方法!requestPermissions

Bluetooth機能を利用する際も同様にいくつかパーミッションを宣言する必要があり、その中にはdangerousレベルのものがあり許可申請ダイアログを表示させる必要があります。Bluetooth機能ではAndroid9(APIレベル28)を境目に必要になるパーミッションが異なります

Android9(APIレベル28)以上

Android9(APIレベル28)以下

基本的にBluetoothを利用するにはBluetooth自体の権限と位置情報の権限を宣言する必要があるようです。

機能によって必要になるパーミッション

さらにそこからBluetoothの機能であるスキャンや接続、アドバタイズなどを実装したい場合は以下のパーミッションを必要になるようです。

おすすめ記事:BLE(Bluetooth Low Energy)とは?セントラルやペリフェラルの意味と用語集

機能によって必要なもの

それぞれの権限の概要について先に見ておきます。

BLUETOOTH

公式リファレンス:BLUETOOTH

アプリケーションがペアリングされた Bluetooth デバイスに接続できるようにします。

レベル:normal

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

BLUETOOTH_ADMIN

公式リファレンス:BLUETOOTH_ADMIN

アプリケーションが Bluetooth デバイスを検出してペアリングできるようにします。

レベル:normal

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

ACCESS_FINE_LOCATION

公式リファレンス:ACCESS_FINE_LOCATION

アプリが正確な位置情報にアクセスできるようにします

レベル:dangerous

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

ACCESS_COARSE_LOCATION

公式リファレンス:ACCESS_COARSE_LOCATION

アプリがおおよその位置情報にアクセスできるようにします。

レベル:dangerous

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

BLUETOOTH_ADVERTISE

公式リファレンス:BLUETOOTH_ADVERTISE

近くの Bluetooth デバイスにアドバタイズできるようにするために必要です。

レベル:dangerous

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

BLUETOOTH_CONNECT

公式リファレンス:BLUETOOTH_CONNECT

ペアリングされた Bluetooth デバイスに接続できるようにするために必要です。

レベル:dangerous

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

BLUETOOTH_SCAN

公式リファレンス:BLUETOOTH_SCAN

近くの Bluetooth デバイスを検出してペアリングできるようにするために必要です。

レベル:dangerous

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

これらのパーミッションは「AndroidManifest.xml」に宣言します。


<!-- Bluetooth -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

<!-- Bluetooth-option -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />

<!-- Android9(APIレベル28)以上 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Android9(APIレベル28)以下 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

パーミッション申請ダイアログを実装する

パーミッション申請ダイアログを表示させるにはregisterForActivityResultメソッドを使用して以下のように実装します。


class MainActivity : AppCompatActivity() {

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

        launcher.launch(
            arrayOf(
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.ACCESS_COARSE_LOCATION,
                Manifest.permission.BLUETOOTH_CONNECT,
                Manifest.permission.BLUETOOTH_SCAN
            )
        )
    }

    private val launcher =
        registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
            val fineLocation = it[Manifest.permission.ACCESS_FINE_LOCATION] ?: false
            val coarseLocation = it[Manifest.permission.ACCESS_COARSE_LOCATION] ?: false
            val blueConnect = it[Manifest.permission.BLUETOOTH_CONNECT] ?: false
            val blueScanner = it[Manifest.permission.BLUETOOTH_SCAN] ?: false

            if (fineLocation && coarseLocation && blueConnect && blueScanner) {
                Toast.makeText(this, "許可されました", Toast.LENGTH_SHORT)
                    .show()
            } else {
                Toast.makeText(this, "否認されました", Toast.LENGTH_SHORT)
                    .show()
            }
        }
}

Bluetoothをサポートしているデバイスかチェックする

パーミッションだけでなく使用している端末がそもそもBluetoothをサポートしているかどうかもチェックが必要です。サポートしているかを確認するにはBluetoothAdapter型のisEnabledから取得します。

 /** bluetoothAdapterの定義 */
private val bluetoothAdapter: BluetoothAdapter? by lazy(LazyThreadSafetyMode.NONE) {
    val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
    bluetoothManager.adapter
}
  
/** Bluetoothサポートしているかのチェック */
private val checkSupport: Boolean
    get() = bluetoothAdapter?.isEnabled ?: false

管理するクラスを実装してみる

Bluetoothのパーミションとサポートの有無をチェックする実装を管理クラスに切り出してみました。Bluetooth機能が有効かどうかはBluetoothState型で外部に公開し識別できるようにしています。GitHubで公開もしているので参考にしてください。

/** Bluetoothを使用するためのパーミッションを確認&リクエストするクラス */
class BleActiveStateManager(
    private val activity: ComponentActivity,
    private val bluetoothAdapter: BluetoothAdapter?
) {
    /** パーミッションの状態を保持する StateFlow */
    private val _permissionState = MutableStateFlow<BluetoothState>(BluetoothState.Initial)
    val permissionState: StateFlow<BluetoothState> = _permissionState

    /** Bluetoothサポートしているかのチェック */
    private val checkSupport: Boolean
        get() = bluetoothAdapter?.isEnabled ?: false

    /** Bluetooth有効状態チェック開始 */
    public fun checking() {
        // Bluetooth非サポート
        if (!checkSupport) {
            _permissionState.value = BluetoothState.NotSupport
            return
        }
        // 全ての権限がマニフェスト内で承認済みかチェック
        if (!permissionCheck) {
            // リクエストを投げたいパーミションをまとめる
            val permissions = arrayOf(
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.BLUETOOTH_CONNECT,
                Manifest.permission.BLUETOOTH_SCAN
            )
            // リクエスト送信 → onRequestPermissionsResultコールバック
            launcher.launch(permissions)
        } else {
            // 有効
            _permissionState.value = BluetoothState.Active
        }
    }

    /** リクエスト送信前にパーミッションが定義されているかチェックする */
    private val permissionCheck: Boolean
        get()  {
            val result = ActivityCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
                    && ActivityCompat.checkSelfPermission(activity, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED
                    && ActivityCompat.checkSelfPermission(activity, Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED
            return  result
        }

    /** 許可ダイアログの表示と結果の処理を実装するランチャー */
    private var launcher =
        activity.registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
            val location = it[Manifest.permission.ACCESS_FINE_LOCATION] ?: false
            val connect = it[Manifest.permission.BLUETOOTH_CONNECT] ?: false
            val scan = it[Manifest.permission.BLUETOOTH_SCAN] ?: false
            if (location && connect && scan) {
                // 有効
                _permissionState.value = BluetoothState.Active
            } else {
                // 権限否認
                _permissionState.value = BluetoothState.Denied
            }
        }

    /** パーミッションの状態を表す sealed class */
    sealed class BluetoothState {
        /** 初期状態 */
        object Initial : BluetoothState()
        /** 有効 */
        object Active : BluetoothState()
        /** 初期状態 */
        object NotSupport : BluetoothState()
        /** 初期状態 */
        object Denied : BluetoothState()
    }
}

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index