【Kotlin/Android Studio】Bluetoothのパーミッション周りの実装方法!
この記事からわかること
- Android StudioでBluetooth機能を利用する
- パーミッション(権限)周りの実装方法
- Android9(APIレベル28)で必要になる権限は?
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
環境
- Android Studio:Koala
- Kotlin:1.9.0
公式リファレンスに乗っているBluetoothのパーミッション周りの実装方法を自分なりにまとめておきたいと思います。
Bluetooth機能実装に必要なパーミッション
Androidアプリからデバイスの機能などを利用する際にはパーミッションを宣言する必要があり、レベルがdangerous
のものはユーザーに対して許可申請ダイアログを表示し承諾を得る必要があります。
おすすめ記事:【Kotlin/Android Studio】パーミッション(権限)許可のリクエスト方法!requestPermissions
Bluetooth機能を利用する際も同様にいくつかパーミッションを宣言する必要があり、その中にはdangerous
レベルのものがあり許可申請ダイアログを表示させる必要があります。Bluetooth機能ではAndroid9(APIレベル28)を境目に必要になるパーミッションが異なります。
Android9(APIレベル28)以上
- android.permission.BLUETOOTH
- android.permission.BLUETOOTH_ADMIN
- android.permission.ACCESS_FINE_LOCATION
Android9(APIレベル28)以下
- android.permission.BLUETOOTH
- android.permission.BLUETOOTH_ADMIN
- android.permission.ACCESS_COARSE_LOCATION
基本的にBluetoothを利用するにはBluetooth自体の権限と位置情報の権限を宣言する必要があるようです。
機能によって必要になるパーミッション
さらにそこからBluetoothの機能であるスキャンや接続、アドバタイズなどを実装したい場合は以下のパーミッションを必要になるようです。
おすすめ記事:BLE(Bluetooth Low Energy)とは?セントラルやペリフェラルの意味と用語集
機能によって必要なもの
- android.permission.BLUETOOTH_ADVERTISE
- android.permission.BLUETOOTH_CONNECT
- android.permission.BLUETOOTH_SCAN
それぞれの権限の概要について先に見ておきます。
BLUETOOTH
アプリケーションがペアリングされた Bluetooth デバイスに接続できるようにします。
レベル:normal
<uses-permission android:name="android.permission.BLUETOOTH" />
BLUETOOTH_ADMIN
アプリケーションが Bluetooth デバイスを検出してペアリングできるようにします。
レベル:normal
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
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 デバイスにアドバタイズできるようにするために必要です。
レベル:dangerous
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
BLUETOOTH_CONNECT
ペアリングされた Bluetooth デバイスに接続できるようにするために必要です。
レベル:dangerous
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
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()
}
}
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。