본문 바로가기
Study/Android

nRF Beacon 앱 분석 - beacon 정보받아오기!!

by Answer Choi 2015. 3. 23.
반응형


이제 beacon 앱에서 beacon의 정보를 가져오는 코드입니다.


정확하게는 리스트에 저장된 비콘의 advertising 신호로 


신호의 세기를 가져와 거리, 동작등을 알려주는 코드입니다.


먼저 앞서 beacon을 검색해서 등록을 하거나, 


등록된 상태로 앱을 시작하면 


등록된 비콘의 advertising 신호를 받게됩니다.



1
2
3
4
5
public void startScanning() {
    if (mServiceConnected) {
        mBeaconsListFragment.startScanning(mServiceConnection);
    }
}
cs


신호는 service형태로 받게 되는되고, 등록된 beacon의 정보를 입력해 리스너에 자동으로 들어오게 됩니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void startScanning(final BeaconServiceConnection serviceConnection) {
    final Cursor cursor = mDatabaseHelper.getAllRegions();
    while (cursor.moveToNext()) {
        final UUID uuid = UUID.fromString(cursor.getString(2 /* UUID */));
        final int major = cursor.getInt(3 /* MAJOR */);
        final int minor = cursor.getInt(4 /* MINOR */);
        final int event = cursor.getInt(6 /* EVENT */);
        // We must start ranging for all beacons
        serviceConnection.startRangingBeaconsInRegion(uuid, major, minor, this);
        // And additionally start monitoring only for those with these two events set
        if (event == BeaconContract.EVENT_IN_RANGE || event == BeaconContract.EVENT_OUT_OF_RANGE)
            serviceConnection.startMonitoringForRegion(uuid, major, minor, this);
    }
}
cs


위 코드의 3~13 라인이 DB에 등록된 beacon을 검색하도록 리스너에 등록(9줄)하는 코드이고,


특별히 EVENT TYPE이 out of range이거나 in range일 경우에는 또다른 리스너(12줄)도 등록하고있습니다.


리스너에서는 advertising 받은 beacon의 정보를 저장하게 됩니다.


out of range

1
2
3
4
5
6
7
8
9
10
11
12
13
Messenger messenger = mBeaconsMessengers.get(listener);
if (messenger == null)
messenger = new Messenger(new BeaconsListenerHandler(listener, mRegionsByHash, mBeaconsByAddressHash));
 
final Message msg = Message.obtain();
msg.what = ServiceProxy.MSG_START_RANGING_BEACONS_IN_REGION;
msg.obj = uuid != null ? new ParcelUuid(uuid) : null;
msg.arg1 = major;
msg.arg2 = minor;
msg.getData().putInt(ServiceProxy.EXTRA_COMPANY_ID, companyId);
msg.replyTo = messenger;
mService.send(msg);
mBeaconsMessengers.put(listener, messenger);
cs


in range


1
2
3
4
5
6
7
8
9
10
11
12
Messenger messenger = mRegionMessengers.get(listener);
if (messenger == null)
messenger = new Messenger(new RegionListenerHandler(listener, mRegionsByHash));
 
final Message msg = Message.obtain();
msg.what = ServiceProxy.MSG_START_MONITORING_FOR_REGION;
msg.obj = uuid != null ? new ParcelUuid(uuid) : null;
msg.arg1 = major;
msg.arg2 = minor;
msg.replyTo = messenger;
mService.send(msg);
mRegionMessengers.put(listener, messenger);
cs


advertising 받은 신호는 위의 형태로, event 타입이 out of range 또는 in range인경우에는 


아래의 리스너도 동작하며, 각각의 핸들러에서 값을 받아 저장하게 됩니다.


핸들러에서는 비콘의 정보와 비콘의 accuracy, previousAccuracy,rssi등을 저장하게 됩니다.


event type이 out of range이거나 in range인경우, 그에대한 event정보 등도 저장됩니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
private static class BeaconsListenerHandler extends Handler {
        private final SparseArray<BeaconRegion> mRegionsByHash;
        private final SparseArray<Beacon> mBeaconsByAddressHash;
        private final BeaconsListener mListener;
 
        public BeaconsListenerHandler(final BeaconsListener listener, final SparseArray<BeaconRegion> regionsByHash, finalSparseArray<Beacon> beaconsByAddressHash) {
            mListener = listener;
            mRegionsByHash = regionsByHash;
            mBeaconsByAddressHash = beaconsByAddressHash;
        }
 
        @Override
        public void handleMessage(final Message msg) {
            switch (msg.what) {
            case ServiceProxy.MSG_BEACONS_IN_REGION: {
                final UUID regionUuid = msg.obj != null ? ((ParcelUuid) msg.obj).getUuid() : null;
                final int regionMajor = msg.arg1;
                final int regionMinor = msg.arg2;
 
                // Get the region reference from a storage or create a new one
                final int hash = regionUuid != null ? regionUuid.hashCode() ^ (regionMajor << 16) ^ regionMinor : 0;
                final BeaconRegion region = mRegionsByHash.get(hash, new BeaconRegion(regionUuid, regionMajor, regionMinor));
                mRegionsByHash.put(hash, region);
 
                // Get the beacons data
                final SparseArray<Beacon> beaconsCache = mBeaconsByAddressHash;
                final Bundle args = msg.getData();
                final int beaconsCount = args.getInt(ServiceProxy.EXTRA_COUNT);
                final Beacon[] beacons = new Beacon[beaconsCount];
                if (beaconsCount > 0) {
                    final String[] addresses = args.getStringArray(ServiceProxy.EXTRA_ADDRESSES);
                    final Parcelable[] uuids = args.getParcelableArray(ServiceProxy.EXTRA_UUIDS);
                    final int[] numbers = args.getIntArray(ServiceProxy.EXTRA_NUMBERS);
                    final float[] accuracies = args.getFloatArray(ServiceProxy.EXTRA_ACCURACIES);
                    final int[] rssis = args.getIntArray(ServiceProxy.EXTRA_RSSI_VALUES);
 
                    for (int i = 0; i < beaconsCount; ++i) {
                        final Beacon beacon = beacons[i] = beaconsCache.get(addresses[i].hashCode(), new Beacon(addresses[i]));
                        beaconsCache.put(addresses[i].hashCode(), beacon);
 
                        beacon.uuid = ((ParcelUuid) uuids[i]).getUuid();
                        beacon.major = numbers[i] >>> 16;
                        beacon.minor = numbers[i] & 0xFFFF;
                        beacon.previousAccuracy = beacon.accuracy;
                        beacon.accuracy = accuracies[i];
                        beacon.rssi = rssis[i];
                    }
                }
                // Notify the listener
                mListener.onBeaconsInRegion(beacons, region);
                break;
            }
            }
        }
    }
cs



그리고 핸들러의 50줄에 보면 onBeaconsInRegion을 호출하게 됩니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Override
public void onBeaconsInRegion(final Beacon[] beacons, final BeaconRegion region) {
 
    if (beacons.length > 0) {
    final Cursor cursor = mDatabaseHelper.findRegion(region);
    try {
    if (cursor.moveToNext()) {
            // Check and fire events
        final int event = cursor.getInt(6 /* EVENT */);
        for (final Beacon beacon : beacons) {
                if (event == BeaconContract.EVENT_ON_TOUCH && Proximity.IMMEDIATE.equals(beacon.getProximity()) &&Proximity.NEAR.equals(beacon.getPreviousProximity())) {
            fireEvent(cursor);
            break;
        }
        if (event == BeaconContract.EVENT_GET_NEAR && Proximity.NEAR.equals(beacon.getProximity()) &&Proximity.FAR.equals(beacon.getPreviousProximity())) {
            fireEvent(cursor);
            break;
        }
        }
        // Update signal strength in the database
        float accuracy = 5;
        for (final Beacon beacon : beacons)
                if (Proximity.UNKNOWN != beacon.getProximity() && beacon.getAccuracy() < accuracy)
                accuracy = beacon.getAccuracy();
        accuracy = -20 * accuracy + 100;
        mDatabaseHelper.updateRegionSignalStrength(cursor.getLong(0 /* _ID */), (int) accuracy);
        }
    } finally {
        cursor.close();
    }
    mAdapter.swapCursor(mDatabaseHelper.getAllRegions());
    }
}
cs


11줄과 15줄에서 event type이 at beacon이거나 near일때 조건이 만족하면 이벤트를 실행하게 됩니다.(fireEvent)


그리고 26줄에서는 db에 새로운 accuracy값을 저장하게 됩니다.


EnterRegion

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public void onEnterRegion(final BeaconRegion region) {
    final Cursor cursor = mDatabaseHelper.findRegion(region);
    try {
        if (cursor.moveToNext()) {
        final int event = cursor.getInt(6 /* EVENT */);
        if (event == BeaconContract.EVENT_IN_RANGE) {
            fireEvent(cursor);
        }
    }
    } finally {
    cursor.close();
    }
}
cs



Exit Region

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public void onExitRegion(final BeaconRegion region) {
    final Cursor cursor = mDatabaseHelper.findRegion(region);
    try {
    if (cursor.moveToNext()) {
    final int event = cursor.getInt(6 /* EVENT */);
        if (event == BeaconContract.EVENT_OUT_OF_RANGE) {
            fireEvent(cursor);
        }
    }
    } finally {
    cursor.close();
    }
}
cs


각각 event type이 in range와 out of range일 경우 실행되는 코드입니다.


검색된 비콘의 거리값등을 알고 싶으면 onBeaconsInRegion 메소드에서 로그를 남기시면 


event type에 관계없이 볼 수 있습니다.


PS. 거리등의 비콘정보는 Beacon.java에서 계산되어 집니다.


1
2
3
4
5
6
7
private final String mAddress;
/* package */UUID uuid;
/* package */int major;
/* package */int minor;
/* package */int rssi;
/* package */float accuracy;
/* package */float previousAccuracy;
cs


이 곳에서 다루는 데이터이며, 가져온 accuracy값은 아래의 메소드에서 계산되어 집니다.


1
2
3
4
5
6
7
8
9
10
11
12
private Proximity calculateProximity(final float accuracy) {
    if (accuracy == -1.0f) {
    return Proximity.UNKNOWN;
    }
    if (accuracy <= 0.26) {
    return Proximity.IMMEDIATE;
    }
    if (accuracy <= 2.0) {
    return Proximity.NEAR;
    }
    return Proximity.FAR;
}
cs


이벤트 조건의 부합되면 BeaconsListFragment.java fireEvent에서 미리 설정해 놓은 


이벤트를 발생시킵니다.

반응형

'Study > Android' 카테고리의 다른 글

android location (위치찾기)  (0) 2015.04.29
좌표계 변환하기  (0) 2015.04.28
nRF Beacon 앱 분석 - beacon 추가하기!!  (3) 2015.03.20
android imageview  (0) 2015.02.12
android의 uri값 전달하기  (4) 2015.02.12

인기글