はじめに
とある施設内にチェックポイントとしてiBeaconを置いて、施設内を回るスタンプラリー的なことをできるアプリを作った際のこと。
基本的に以下のサイトを参考にして実装しました。
AltBeaconを使ってAndroidでiBeaconを検知しよう
一応、AltBeaconのライブラリのコード全部見て、個人情報とか抜いてないことは確認した。
一部、ライブラリ本体には「android.permission.INTERNET」の権限付与されてないのに、ライブラリ内で「android.permission.INTERNET」を必要とするところがあったので見てみたら、距離計算のためのフォーマットファイル?をダウンロードしているだけだった。
「android.permission.INTERNET」が付与されてない場合はローカルファイルを読みに行く実装でした。
上記サイトと違う点
・言語がKotlin
・startRangingBeaconsInRegion/stopRangingBeaconsInRegionをするタイミング
・onBeaconServiceConnectした時にNotifier等を削除
言語がKotlin
Kotlin楽しいよ。Kotlin。わからないことだらけだけど!
配列の初期化とか無理やりチックに似できるのが楽しい(ぇ
startRangingBeaconsInRegion/stopRangingBeaconsInRegionをするタイミング
参考にしたサイトではdidEnterRegionでstart、didExitRegionでstopしてるんだけど、BeaconManagerをunbind→bindすると何故かonBeaconServiceConnect後にdidEnterRegionが呼ばない。
なので、startRangingBeaconsInRegionが呼べずにRegionのモニタリングができなくなった。
ってことでdidDetermineStateForRegionの出入りのタイミングで登録、削除をしてあげるようにした。
onBeaconServiceConnectした時にNotifier等を削除
BeaconActivityが破棄されて、もう一度生成され、BeaconManagerを取得しーのNotifierをセットするとaddされる感覚で通知がたくさん来るようになった。
まぁ、呼び出してるのadd~~~なんだけど、、、。
unbindしたタイミングで破棄してくれないみたい。
同じインスタンスでセットする分には重複チェックされてるのかaddされてない感じ。
実装上はaddしてるんだけど、CopyOnWriteArraySetってクラス使っててよくわからない(調べてない)
なのでとりあえず、onBeaconServiceConnect時に削除するようにした。
unbindないしonDestroyとかのタイミングで消しても良さそうっちゃ良さそうだが、、、。
ってことで実装
//
abstract class BeaconActivity : BaseActivity(), BeaconConsumer {
companion object {
private val TAG = "hoge"
// iBeaconのデータを認識するためのParserフォーマット
val IBEACON_FORMAT = "m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"
}
// BeaconManagerクラスの変数を定義
protected var mBeaconManager: BeaconManager? = null
// Beacon UUIDの作成
protected var mBeaconIdentifier = Identifier.parse("hogehage-0000-0000-0000-000000000000")
protected var mNearestBeacon: Beacon? = null
private val mRegion = Region("unique-id-001", mBeaconIdentifier, null, null)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// staticメソッドでBeaconManagerクラスのインスタンスを取得
mBeaconManager = BeaconManager.getInstanceForApplication(applicationContext)
// BeaconParseをBeaconManagerに設定
mBeaconManager?.beaconParsers?.add(BeaconParser().setBeaconLayout(IBEACON_FORMAT))
}
override fun onResume() {
super.onResume()
mBeaconManager?.bind(this)
}
override fun onPause() {
super.onPause()
mBeaconManager?.unbind(this)
}
override fun onBeaconServiceConnect() {
Log.d(TAG, "onBeaconServiceConnect")
// 2重登録されるので一旦削除
mBeaconManager?.removeAllMonitorNotifiers()
mBeaconManager?.removeAllRangeNotifiers()
mBeaconManager?.rangedRegions?.forEach {region ->
mBeaconManager?.stopRangingBeaconsInRegion(region)
}
// BeaconManagerクラスのモニタリング設定
mBeaconManager?.addMonitorNotifier(mMonitorNotifier)
// BeaconManagerクラスのレンジング設定
mBeaconManager?.addRangeNotifier(mRangeNotifier)
startMonitoringBeacons()
}
/**
* ビーコンのモニタリング開始
*/
private fun startMonitoringBeacons() {
try {
// モニタリングの開始
mBeaconManager?.startMonitoringBeaconsInRegion(mRegion)
} catch (e: RemoteException) {
e.printStackTrace()
}
}
/**
* ビーコンモニター
*/
private val mMonitorNotifier = object : MonitorNotifier {
override fun didEnterRegion(region: Region) {
// 領域進入時に実行
Log.d(TAG, "didEnterRegion")
}
override fun didExitRegion(region: Region) {
// 領域退出時に実行
Log.d(TAG, "didExitRegion")
}
override fun didDetermineStateForRegion(i: Int, region: Region) {
// 領域への侵入/退出のステータスが変化したときに実行
Log.d(TAG, "didDetermineStateForRegion i = " + i)
try {
if (i == 1) {
mBeaconManager?.startRangingBeaconsInRegion(region)
} else {
mBeaconManager?.stopRangingBeaconsInRegion(region)
}
} catch (e: RemoteException) {
Log.d(TAG, "didDetermineStateForRegion e = " + e.message)
}
}
}
/**
* ビーコンレンジ
*/
private val mRangeNotifier = RangeNotifier { beacons, region ->
// 検出したビーコンの情報を全部Logに書き出す
for (beacon in beacons) {
Log.d(TAG, "UUID:" + beacon.id1 + ", major:" + beacon.id2 +
", minor:" + beacon.id3 + ", Distance:" + beacon.distance +
",RSSI" + beacon.rssi + ", TxPower" + beacon.txPower)
}
}
}
これで受信できるようになるはず。
ただ、BlueToothがON状態じゃないと受信できないのと、6系以上は「Manifest.permission.ACCESS_COARSE_LOCATION」が許可されていないと受信できない。