【Android】Kotlin + Bluetooth Low Energy 1
はじめに
- AndroidでBLEを使う(ver.4.4編)
- Android5.0〜でBLEを使う(Central編)
- Android6.0でBLEを使う(Central編)
- Android6.0でBLEを使う(Peripheral編) その1
- Android6.0でBLEを使う(Peripheral編) その2
これまでも何度かAndroidでのBluetoothLowEnergy(BLE)を試してきましたが、ふともう一度細かい部分にも気をつけながら再挑戦してみることにしました。
ただ、せっかくなので興味があったKotlinを使ってみることにしました。
Activityを開く
変数の宣言などは異なっていても、基本的にはJAVAと同じように書けるのかな?と思っていたら盛大につまづきました。
画面遷移にはIntentを使用するのですが、(おそらく)1.0からは以下のように書く必要があります。
var intentCentral = Intent(this, CentralActivity::class.java) startActivity(intentCentral)
これを「javaClass
自動変換、便利ですねぇ。
位置情報
2つ目に引っかかったのがこの位置情報(GPS)です。
以前JAVAで作成したサンプルを元にCentral側のコードを書き、 Nexus5Xで動作させるとエラーはでないのにスキャンがされない(ScanCallbackのonScanResultが呼ばれない)、という問題が発生しました。
もしやAndroidWearにつないでいるのが原因か?など色々調べてみたところ、以下に行き着きました。
どうやらtargetSdkVersion 23からは、デバイスのスキャンをするにはGPSをオンにする必要があるようです。
22までのバージョンでも、位置情報のパーミッション自体は必要になっていましたが、更に変更があったと。
関連するクラスにて使用する、ということのようですが、実際何に使っているのでしょうね。
iOSでiBeaconを利用するときにも位置情報を使用していた気がしますが、この辺りと関連しているのでしょうか。
LocationAccesser.kt
import android.content.IntentSender import android.os.Bundle import android.support.v7.app.AlertDialog import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.api.GoogleApiClient import com.google.android.gms.location.LocationRequest import com.google.android.gms.location.LocationServices import com.google.android.gms.location.LocationSettingsRequest import com.google.android.gms.location.LocationSettingsStatusCodes class LocationAccesser : GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { private var apiClient: GoogleApiClient? = null final var REQUEST_NUM_LOCATION = 2 override fun onConnectionFailed(result: ConnectionResult) { } override fun onConnectionSuspended(cause: Int) { } override fun onConnected(bundle: Bundle?) { } fun checkIsGpsOn(activity: CentralActivity) { // OS Version 6.0以降はGPSがOffだとScanできないのでチェック. val locationRequest = LocationRequest.create() locationRequest.priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY locationRequest.interval = 3000L locationRequest.fastestInterval = 500L val builder = LocationSettingsRequest.Builder().addLocationRequest(locationRequest) builder.setAlwaysShow(true) if (apiClient == null) { apiClient = GoogleApiClient .Builder(activity.applicationContext) .enableAutoManage(activity, this) .addApi(LocationServices.API) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .build() } val result = LocationServices.SettingsApi.checkLocationSettings(apiClient, builder.build()) result.setResultCallback { settingsResult -> val status = settingsResult.status when (status.statusCode) { LocationSettingsStatusCodes.SUCCESS ->{ // GPSがOnならScan開始. activity.onGpsEnabled() } LocationSettingsStatusCodes.RESOLUTION_REQUIRED ->{ try { // GPSがOffならIntent表示. onActivityResultで結果取得. status.startResolutionForResult( activity, REQUEST_NUM_LOCATION) } catch (ex: IntentSender.SendIntentException) { activity.runOnUiThread { val alert = AlertDialog.Builder(activity) alert.setTitle(activity.getString(R.string.error_title)) alert.setMessage(ex.message) alert.setPositiveButton(activity.getString(android.R.string.ok), null) alert.show() } } } LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> { // Locationが無効なら無視. } } } } }
接続を切る
以前作成したサンプルでは、一度Peripheralデバイスに接続したあとActivityを閉じると、次にCentralのActivityを開いても再接続ができませんでした。
これは最初に接続した状態が保持されるためで、再接続するためには一旦接続を切る必要があります。
CentralActivity.kt
~省略~ override fun onDestroy(){ super.onDestroy() // reset. if(bleAdapter!!.isEnabled) { bleScanner!!.stopScan(bleScanCallback) // 接続中のデバイスがあれば切断して閉じる. if (!bleManager!!.getConnectedDevices(BluetoothProfile.GATT).isEmpty()) { bleGatt!!.disconnect() bleGatt!!.close() } bleGatt = null } } ~省略~
なお、Bluetoothがオフの状態でstopScanなどを実行すると、NullPointerExceptionが出たりします。
終わりに
2回めのKotlinへの挑戦となりましたが、すこしずつ慣れてくるとコード量が減って良いのかも、という気になってきました。
今回はCentral側のプログラムでしたが、次回はPeripheral側を作成する予定です。