概要
今回は前に書いた記事の続きで、フォアグラウンドで Beacon を検知してみたいと思います。
フォアグラウンドで検知処理を行うことで、アプリをキルした状態でも Beacon の領域監視を行うことができます。
ライブラリは altbeacon という Bluetooth ライブラリを使用します。
altbeacon ライブラリを使った Beacon 検知についての詳しい説明については前回の記事で解説させていただきましたので、使い方をご存知でない方はこちらの記事を参考にしていただけますと幸いです。
環境
Android Studio:2021.1.1 Patch 3
kotlin:1.6.20
targetSdkVersion:32
minSdkVersion:27
準備
まずは実装の準備として以下のライブラリを gradle(appレベル) に追加します。
implementation 'org.altbeacon:android-beacon-library:2.19.4'
次に Manifest ファイルに以下の権限を追加します。
Bluetooth 関連のほか、フォアグラウンドにおけるサービスの実行を許可する必要があります。( Bluetooth 関連の権限呼び出し方法は割愛します)
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="true" />
次に Beacon 検知を開始する Activity にRangeNotifier
, MonitorNotifier
インターフェースを引き継ぎます。
class MainActivity : AppCompatActivity(), RangeNotifier, MonitorNotifier {
その後各種メソッドをオーバーライドします。
override fun didEnterRegion(region: Region?) {
//領域への入場を検知
Log.d("iBeacon", "Enter Region ${region?.uniqueId}")
}
override fun didExitRegion(region: Region?) {
//領域からの退場を検知
Log.d("iBeacon", "Exit Region ${region?.uniqueId}")
}
override fun didRangeBeaconsInRegion(beacons: MutableCollection<Beacon>?, region: Region?) {
// 検知したBeaconの情報
Log.d("MainActivity", "beacons.size ${beacons?.size}")
beacons?.let {
for (beacon in beacons) {
Log.d("MainActivity", "UUID: ${beacon.id1}, major: ${beacon.id2}, minor: ${beacon.id3}, RSSI: ${beacon.rssi}, TxPower: ${beacon.txPower}, Distance: ${beacon.distance}")
}
}
}
override fun didDetermineStateForRegion(state: Int, region: Region?) {
//領域への入退場のステータス変化を検知(INSIDE: 1, OUTSIDE: 0)
Log.d("MainActivity", "Determine State: $state")
}
ここまで出来たら監視する領域を定義する Region
と、Beacon を扱うための行うための BeaconManager
を作成します。
private lateinit var mRegion: Region
private lateinit var beaconManager: BeaconManager
private val IBEACON_FORMAT = "m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"
--- 省略 ---
mRegion = Region("iBeacon", Identifier.parse(uuid), null, null)
beaconManager = BeaconManager.getInstanceForApplication(this)
beaconManager.beaconParsers.add(BeaconParser().setBeaconLayout(IBEACON_FORMAT)) // iBeaconのフォーマット指定
実装
ここからフォアグラウンドにおける領域監視の実装を行います。
通常フォアグラウンドで何らかの処理を行う際はフォアグラウンドサービスを作成する必要がありますが、altbeacon ライブラリのメソッドにはフォアグラウンドサービスを開始する処理が含まれているため、自分で作成する必要はありません。
フォアグラウンドで領域監視を始めるコードは以下になります。
if (!beaconManager.isAnyConsumerBound) {
// 通知を作成
val channelId = "0"
val channel = NotificationChannel(channelId, "Beacon service", NotificationManager.IMPORTANCE_HIGH)
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
val builder = Notification.Builder(this, channelId)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Beacon検知中")
.setContentText("領域監視を実行しています。")
// PendingIntentを作成
val intent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this, 0, intent, PendingIntent.FLAG_IMMUTABLE)
builder.setContentIntent(pendingIntent)
// フォアグラウンドサービスを許可
beaconManager.enableForegroundServiceScanning(builder.build(), 0)
beaconManager.setEnableScheduledScanJobs(false)
beaconManager.foregroundBetweenScanPeriod = 0
beaconManager.foregroundScanPeriod = 1100
// 領域監視開始
beaconManager.addMonitorNotifier(this)
beaconManager.addRangeNotifier(this)
beaconManager.startMonitoring(mRegion)
beaconManager.startRangingBeacons(mRegion)
}
まず、1行目の if
文でフォアグラウンドでの検知が開始済みだった場合にアプリが落ちるのを防ぎます。
次にフォアグラウンドで処理を行っていることをユーザーに認識させるために通知センターに表示する通知を作成します。
以下は上記のコードのうち通知に関する部分を抜粋したものです。
// 通知を作成
val channelId = "0"
val channel = NotificationChannel(channelId, "Beacon service", NotificationManager.IMPORTANCE_HIGH)
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
val builder = Notification.Builder(this, channelId)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Beacon検知中")
.setContentText("領域監視を実行しています。")
// PendingIntentを作成
val intent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this, 0, intent, PendingIntent.FLAG_IMMUTABLE)
builder.setContentIntent(pendingIntent)
通知を作成後に PendingIntent
を作成し、ユーザーが通知をタップした時に MainActivity
を呼び出せるようにしています。
なお、通知を作成する際は setSmallIcon()
メソッドでアイコンを指定する必要があります。
そして作成した通知を enableForegroundServiceScanning()
メソッドの引数に渡します。
// フォアグラウンドサービスを許可
beaconManager.enableForegroundServiceScanning(builder.build(), 0)
beaconManager.setEnableScheduledScanJobs(false)
ちなみに、
beaconManager.foregroundBetweenScanPeriod = 0
beaconManager.foregroundScanPeriod = 1100
ではフォアグラウンドでのスキャン時間およびスキャンとスキャンの間隔を指定しています。(デフォルト値と同じ値を設定しています。)
ここまで書けたら以下の領域監視を開始するコードを書いてください。(これより前に書くとエラーになります)
// 領域監視開始
beaconManager.addMonitorNotifier(this)
beaconManager.addRangeNotifier(this)
beaconManager.startMonitoring(mRegion)
beaconManager.startRangingBeacons(mRegion)
なお、フォアグラウンドでの領域監視を停止するときですが、停止したい箇所に以下のコードを記述してください。
beaconManager.stopMonitoring(mRegion)
beaconManager.stopRangingBeacons(mRegion)
まとめ
今回はフォアグラウンドでの領域監視を行う方法について説明しました。
自分でフォアグラウンドサービスを立ち上げる必要がなく、手軽に実装できるのでおすすめです。
ご興味のある方はぜひ参考にしていただければと思います。