AndroidでiBeaconの受信、送信をやってみる (AltBeacon)

[AltBeacon](http://altbeacon.org/)というライブラリを使って、iBeaconの**受信と送信のやり方**みたいなものを書きます。
※AltBeaconのバージョンは、***2.9***です。

AltBeaconを使わなくても、頑張れば実装できるのですが...
AndroidのBLE周りは、まあ...うん...アレなので、自力でやるとヤバイことになると思う...
この辺は`Android BLE ひどい`とかで検索すると大量に出てくるので、興味がある人はググってください（笑）

# AltBeaconの導入

build.gradleに以下を加えます。

```
repositories {
    jcenter()
}
```

```
dependencies {
    compile 'org.altbeacon:android-beacon-library:2+'
}
```

AndroidManifestには以下を加えます。

```AndroidManifest.xml
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <!--対応していない機種にインストールされないようにuser-futureを設定する-->
    <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
    <!--Android 6.0でBLEを使うのに位置情報のパーミッションが必要-->
    <uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/>
```

# iBeacon受信

## 受信

ただ受信するだけなら、これだけで大丈夫です。

※2.9から***setMonitorNotifierがDeprecated***になり、***addMonitorNotifier***を使用するようになっています。

```MainActivity.java
public class MainActivity extends AppCompatActivity implements BeaconConsumer {

    private static final String IBEACON_FORMAT = "m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24";
    private static final String UUID = "74A23A96-A479-4330-AEFF-2421B6CF443C";

    private BeaconManager beaconManager;
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        beaconManager = BeaconManager.getInstanceForApplication(this);
        beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout(IBEACON_FORMAT));

    }

    @Override
    protected void onResume() {
        super.onResume();
        // サービスの開始
        beaconManager.bind(this);
    }

    @Override
    protected void onPause() {
        super.onPause();
        // サービスの停止
        beaconManager.unbind(this);
    }

    @Override
    public void onBeaconServiceConnect() {
        Identifier uuid = Identifier.parse(UUID);
        Region mRegion = new Region("ibeacon", uuid, null, null);
        
        beaconManager.addMonitorNotifier(new MonitorNotifier() {
            @Override
            public void didEnterRegion(Region region) {
                 // 領域侵入
            }

            @Override
            public void didExitRegion(Region region) {
                 // 領域退出
            }

            @Override
            public void didDetermineStateForRegion(int i, Region region) {
                 // 領域に対する状態が変化
            }
        });

        try {
            beaconManager.startMonitoringBeaconsInRegion(mRegion);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

}
```

AltBeaconのデフォルト設定ではiBeaconを対象としていないので、こんな感じにBeaconParserの設定が必要です。

```java
beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));
```

`Region`と呼ばれる監視領域を設定することで、iBeaconを検知できるようになります。
`startMonitoringBeaconsInRegion`で、その監視領域の設定をします。

```java
beaconManager.startMonitoringBeaconsInRegion(new Region("ibeacon", null, null, null));
```

`Region`の引数は、このようになっているようです。

```java
Identifier uuid = Identifier.parse("DCE285F8-27B9-4D0E-981E-CF9C675972ED");
Identifier major = Identifier.parse("3");
Identifier minor = Identifier.parse("5");
new Region("任意のユニークID", uuid, major, minor);
```

## iBeaconの情報を取得

iBeaconの情報を取得するには**BeaconManagerからレンジングを行う必要**があります。

```java
@Override
public void didEnterRegion(Region region) {
     //レンジングの開始
     beaconManager.startRangingBeaconsInRegion(mRegion);
}

@Override
public void didExitRegion(Region region) {
    //レンジングの停止
    beaconManager.stopRangingBeaconsInRegion(mRegion);
}

beaconManager.addRangeNotifier(new RangeNotifier() {
   @Override
   public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
        // 検出したビーコンの情報
  }
});
```

`startRangingBeaconsInRegion`でレンジング開始です。
`stopRangingBeaconsInRegion`でレンジング停止。

今回は、didEnterRegionで開始して、didExitRegionで停止してます。

iBeaconの情報は`didRangeBeaconsInRegion`で、以下のような感じで取得できます。

- UUID: beacon.getId1()
- major: beacon.getId2()
- minor: beacon.getId3()
- Distance: beacon.getDistance()
- RSSI: beacon.getRssi()
- Name: beacon.getBluetoothName()

# iBeacon送信

iBeaconの送信ですが、こちらは制限があります。
Android5.0以上かつmultiple advertisementのHCIコマンドに対応している端末でのみ、送信可能のようです。
送信できる端末は[こちら](http://altbeacon.github.io/android-beacon-library/beacon-transmitter-devices.html)を参考にしてください。


まず始めに、Beaconの設定をします。
Id1はuuid, Id2はmajor, Id3はminorです。

```java
Beacon beacon = new Beacon.Builder()
                .setId1(UUID)
                .setId2("1")
                .setId3("80")
                .setManufacturer(0x004C)
                .build();
BeaconParser beaconParser = new BeaconParser()
                .setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24");
BeaconTransmitter beaconTransmitter = new BeaconTransmitter(getApplicationContext(), beaconParser);

//送信開始
beaconTransmitter.startAdvertising(beacon);
```

`startAdvertising`で送信開始します。
`AdvertiseCallback`で、送信成功か失敗かの通知を受け取ることが出来ます。

```java
 beaconTransmitter.startAdvertising(beacon, new AdvertiseCallback() {
                @Override
                public void onStartSuccess(AdvertiseSettings settingsInEffect) {
                    super.onStartSuccess(settingsInEffect);
                    //成功
                }

                @Override
                public void onStartFailure(int errorCode) {
                    //失敗
                }
            });
```

送信停止は`stopAdvertising`です。

```java
 beaconTransmitter.stopAdvertising();
```

`isStarted`で、送信しているかどうか分かります。

```java
beaconTransmitter.isStarted()
```

# まとめ

AltBeaconを使うと、Androidでも簡単にiBeaconを扱えます。
あ！Android6.0以上は`ACCESS_COARSE_LOCATION`の許可を取るのを忘れないでください。


# 参考サイト

- http://altbeacon.org/

- http://altbeacon.github.io/android-beacon-library/samples.html

- https://github.com/AltBeacon/android-beacon-library