LoginSignup
9
13

More than 5 years have passed since last update.

AltBeaconでビーコン検出

Last updated at Posted at 2018-08-08

はじめに

KotlinでBeacon受信機能を持ったアプリの開発を行なったため、備忘録として残す。
今回は、AltBeaconを使って実装した。

開発環境

AndroidStudio 3.1

実装

AltBeaconをbuild.gradleにて追加

build.gradle(app/)
implementation 'org.altbeacon:android-beacon-library:2+'

AndroidManifestにパーミッションの追加

AndroidManifest.xml
<!-- BLE使用のためのパーミッション -->
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<!-- 対応しない端末へのインストールを禁止(この記述の追加は各自で判断) -->
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

以下、Activityの編集
onCreateで、BLE対応端末の確認・APIレベルが23以上の確認を行なっている。
APIレベ23以上の端末は、アプリ起動時にダイアログで権限の申請をする必要がある。
そのため、23以上の場合checkPermissionメソッドにて権限のリクエストを行う。
また、BeaconManagerのインスタンスの生成と、フォーマットの設定を行う。

今回は、複数のビーコンを検知した状態で、最もRSSIが大きい(端末から近い)ビーコンの種類によって、画面が切り替わる仕様になっている。

MainActivity.kt
class MainActivity : AppCompatActivity(), BeaconConsumer {

    // BeaconManager型変数の宣言
    private var beaconManager: BeaconManager? = null

    // uuidの指定
    private val uuidString: String = "00000000-0000-0000-0000-000000000000"
    private val uuid = Identifier.parse(uuidString)

    // ビーコンのフォーマット設定
    private val IBEACON_FORMAT: String = "m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"


    /**************************************************
     * AppCompatActivity内のメソッドをoverride
     **************************************************/
    // onCreate
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // デバイスのBLE対応チェック
        if (!packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {

            // 未対応の場合、Toast表示
            showToast("このデバイスはBLE未対応です", Toast.LENGTH_LONG)
        }

        // API 23以上かのチェック
        if (Build.VERSION.SDK_INT >= 23) {

            // パーミッションの要求
            checkPermission()
        }

        // ビーコンマネージャのインスタンスを生成
        beaconManager = BeaconManager.getInstanceForApplication(this)

        // BeaconManagerの設定
        beaconManager!!.beaconParsers.add(BeaconParser().setBeaconLayout(IBEACON_FORMAT))
    }

    // onResume
    override fun onResume() {
        super.onResume()

        // ビーコンサービスの開始
        beaconManager!!.bind(this)
    }

    // onPause
    override fun onPause() {
        super.onPause()

        // ビーコンサービスの停止
        beaconManager!!.unbind(this)
    }


    /**************************************************
     * BeaconConsumer内のメソッドをoverride
     **************************************************/
    // onBeaconServiceConnect
    override fun onBeaconServiceConnect() {

        try {
            // ビーコン情報の監視を開始、第3,4引数はmajor・minor値を指定する時に使用
            beaconManager!!.startMonitoringBeaconsInRegion(Region("ここは適用な文字列", uuid, null, null))
        }
        catch (e: RemoteException) {
            e.printStackTrace()
        }

        // モニタリングの通知受取り処理
        beaconManager!!.addMonitorNotifier(object: MonitorNotifier{

            // 領域内に侵入した時に呼ばれる
            override fun didEnterRegion(region: Region) {

                // レンジングの開始
                beaconManager!!.startRangingBeaconsInRegion(region)
            }

            // 領域外に退出した時に呼ばれる
            override fun didExitRegion(region: Region) {

                // レンジングの停止
                beaconManager!!.stopRangingBeaconsInRegion(region)
            }

            // 領域への侵入/退出のステータスが変化した時に呼ばれる
            override fun didDetermineStateForRegion(i: Int, region: Region) {
                //
            }
        })

        // レンジングの通知受け取り処理
        beaconManager!!.addRangeNotifier(object: RangeNotifier{

            // 範囲内のビーコン情報を受け取る
            override fun didRangeBeaconsInRegion(beacons: Collection<Beacon>, region: Region){

                var maxMajor: Int?
                var maxMinor: Int?

                // 範囲内の複数のビーコン情報を保持させる変数
                var getMajorList: ArrayList<Int> = ArrayList()
                var getMinorList: ArrayList<Int> = ArrayList()
                var getRssiList: ArrayList<Int> = ArrayList()

                // 範囲内にビーコンがある時の処理
                if (beacons.size > 0) {

                    // 範囲内のビーコンの数だけ繰り返す
                    for (beacon in beacons) {
                        // 複数のビーコン情報をArrayListに分割
                        getMajorList.add(beacon.id2.toInt())
                        getMinorList.add(beacon.id3.toInt())
                        getRssiList.add(beacon.rssi)
                    }

                    // RSSIが最も大きいインデックスを取得
                    var indexRssi: Int = getRssiList.indexOf(getRssiList.max())

                    // 取得したインデックスのmajor値・minor値を取得
                    maxMajor = getMajorList[indexRssi]
                    maxMinor = getMinorList[indexRssi]

                    // Viewの更新
                    viewUpdate(maxMajor, maxMinor)
                }
            }
        })
    }


    /**************************************************
     * メソッド
     **************************************************/
    // トースト表示のメソッド
    fun showToast(text: String, length: Int) {

        // トーストの生成と表示
        var toast: Toast = Toast.makeText(this, text, length)
        toast.show()
    }

    // パーミッションの許可チェック
    @RequiresApi(Build.VERSION_CODES.M)
    fun checkPermission() {

        // パーミッション未許可の時
        if (checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {

            // パーミッションの許可ダイアログの表示
            requestPermissions(arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION), 0)
        }
    }

    fun viewUpdate(major: Int?, minor: Int?) {

        // Viewの取得
        var majorTextView: TextView = findViewById(R.id.major) as TextView
        var minorTextView: TextView = findViewById(R.id.minor) as TextView

        majorTextView.text = "major:" + major
        minorTextView.text = "minor:" + minor
    }

}

なお、このコードでビーコンを検知するとアプリがクラッシュする。
エラーを見ると、Only the original thread that created a view hierarchy can touch its views.と表示される。
これは、ビーコンを検出する別スレッド上にて、viewUpdateメソッドを呼んだためである。(メインスレッド以外でUIの操作を行なってはいけないというAndroidの制約がある。)
そこで、以下のようにコードを変更する。

MainActivity.kt
// この中身はメインスレッドで実行される
class MainActivity : AppCompatActivity(), BeaconConsumer {

    ~省略~

    // ビーコンのフォーマット設定
    private val IBEACON_FORMAT: String = "m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"

    // Handlerクラスの変数の宣言(追加)
    private val handler: Handler = Handler()

    ~省略~

        // レンジングの通知受け取り処理
        beaconManager!!.addRangeNotifier(object: RangeNotifier{

            // 範囲内のビーコン情報を受け取る
            override fun didRangeBeaconsInRegion(beacons: Collection<Beacon>, region: Region){

                var maxMajor: Int?
                var maxMinor: Int?

                // 範囲内の複数のビーコン情報を保持させる変数
                var getMajorList: ArrayList<Int> = ArrayList()
                var getMinorList: ArrayList<Int> = ArrayList()
                var getRssiList: ArrayList<Int> = ArrayList()

                // 範囲内にビーコンがある時の処理
                if (beacons.size > 0) {

                    // 範囲内のビーコンの数だけ繰り返す
                    for (beacon in beacons) {
                        // 複数のビーコン情報をArrayListに分割
                        getMajorList.add(beacon.id2.toInt())
                        getMinorList.add(beacon.id3.toInt())
                        getRssiList.add(beacon.rssi)
                    }

                    // RSSIが最も大きいインデックスを取得
                    var indexRssi: Int = getRssiList.indexOf(getRssiList.max())

                    // 取得したインデックスのmajor値・minor値を取得
                    maxMajor = getMajorList[indexRssi]
                    maxMinor = getMinorList[indexRssi]

                    Log.d("Test_Major:", maxMajor.toString())
                    Log.d("Test_Minor", maxMinor.toString())

                    // メインスレッドで実装(追加)
                    handler.post {
                        // 空の引数を渡して、Viewの更新
                        viewUpdate(maxMajor, maxMinor)
                    }
                }
            }
        })

Handlerクラスを使うと、別スレッド中でもメインスレッドで処理を実装することができる。詳しい説明については参考サイトを参照してください...
これからここら辺の勉強しよ...

レイアウトは、TextView2つ用意して、idを"major","minor"に設定。
これで複数(一個でもいい)のBeaconをキャッチした時、Viewが以下の画像のように更新される。
image.png

さいごに

AndroidアプリにBeacon受信機能を実装することができた。
今回は、RSSIが最も大きいビーコン情報を表示するアプリにしたが、リスト表示することもできる。
また、Handlerクラスなど、スレッドに関する勉強もしよ...

9
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
13