이번에 버전업 중에 사용한 안드로이드 스튜디오 버전.
최신 버전은 아니지만, 일단 회사 데스크톱에 설치되어 있는 에디터 버전이 이렇기 때문에 얘 기준으로 세팅을 맞췄습니다.
Android beacon library 를 사용한 유니티 플러그인을 만든건 2년이지만, 그나마 최근이라고 해야하나?
당시 기기의 OS 레벨에 맞춰야 하는 상황과 스토어에서 요구하는 앱의 sdk 최저 레벨에 관련하여 대응해야하는 상황이 있어서 어느정도 손을 본 버전입니다.
올바르게 사용했다고는 생각들지는 않지만, 동작시켜서 원하는 기능 수행에 주목적을 두고 만들었습니다.
앞선 글들을 통해서 간단하게 AAR 파일을 만들어서 유니티에 동작을 시켜보셨기 때문에, 그 기준으로 진행을 해보겠습니다.
아래 스샷들은 해당 프로젝트를 하면서 추가해줘야하는 권한 및 필요한 gradle 작성 관련입니다.
없어져도 되는 부분도 있긴 할텐데, 쓸데없는 노파심에 예전에 넣어둔걸 그대로 냅둔 부분도 있긴 합니다.
- 코드 -
package com.example.beaconplugin;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Debug;
import android.util.Log;
import com.unity3d.player.UnityPlayer;
import org.altbeacon.beacon.Beacon;
import org.altbeacon.beacon.BeaconManager;
import org.altbeacon.beacon.BeaconParser;
import org.altbeacon.beacon.InternalBeaconConsumer;
import org.altbeacon.beacon.MonitorNotifier;
import org.altbeacon.beacon.RangeNotifier;
import org.altbeacon.beacon.Region;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class AndroidNativeBeacon implements InternalBeaconConsumer
{
private static final String LOG_TAG = "Android";
private BeaconManager beaconManager;
private Beacon beacon;
private Map<String, Double> bDistances = new HashMap<String, Double>();
private float catchBeaconDistance = 1.2f;
private String gameObject;
private String nitfyCallback;
private String errorCallback;
// private String enterCallback;
// private String exitCallback;
public AndroidNativeBeacon()
{
Log.d(LOG_TAG, "AndroidNativeBeacon");
beaconManager = null;
}
public void Init(Context context, String gameObject, String nitfyCallback, String errorCallback)//, String enterCallback, String exitCallback)
{
Log.d(LOG_TAG, "AndroidNativeBeacon Init");
// beaconManager
beaconManager = BeaconManager.getInstanceForApplication(context);
beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25"));
this.gameObject = gameObject;
this.nitfyCallback = nitfyCallback;
this.errorCallback = errorCallback;
// this.enterCallback = enterCallback;
// this.exitCallback = exitCallback;
}
public void SetCatchDistance(float _value)
{
catchBeaconDistance = _value;
Log.d(LOG_TAG, "AndroidNativeBeacon.SetCatchDistance : " + catchBeaconDistance);
}
public void OnNotify(String data)
{
Log.i(LOG_TAG, "AndroidNativeBeacon.OnNotify to gameObject=" + gameObject);
UnityPlayer.UnitySendMessage(gameObject, nitfyCallback, data);
}
public void OnError(String errorMessage)
{
Log.i(LOG_TAG, "AndroidNativeBeacon.OnError message:" + errorMessage);
UnityPlayer.UnitySendMessage(gameObject, errorCallback, errorMessage);
}
// public void OnEnterBeacon()
// {
// Log.i(LOG_TAG, "AltBeaconClient.OnEnterBeacon to gameObject=" + gameObject);
// UnityPlayer.UnitySendMessage(gameObject, enterCallback, "");
// }
//
// public void OnExitBeacon()
// {
// Log.i(LOG_TAG, "AltBeaconClient.OnExitBeacon to gameObject=" + gameObject);
// UnityPlayer.UnitySendMessage(gameObject, exitCallback, "");
// }
@Override
public void onBeaconServiceConnect()
{
Log.d(LOG_TAG, "AndroidNativeBeacon.onBeaconServiceConnect");
beaconManager.addMonitorNotifier(new MonitorNotifier() {
@Override
public void didEnterRegion(Region region) {
Log.i(LOG_TAG, "didEnterRegion - 비콘 연결됨");
beacon = null;
//EnterIBeacon();
}
@Override
public void didExitRegion(Region region) {
Log.i(LOG_TAG, "didExitRegion - 비콘 끊김");
beacon = null;
NotiyIBeacon(null);
//ExitIBeacon();
}
@Override
public void didDetermineStateForRegion(int state, Region region) {
Log.i(LOG_TAG, "I have just switched from seeing/not seeing beacons: "+state);
}
});
beaconManager.addRangeNotifier(new RangeNotifier() {
@Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
// 검출한 비콘의 정보를 전부 로그에 써내다
if(beacons.size() > 0)
{
Log.i(LOG_TAG, "didRangeBeaconsInRegion - 비콘들 검출함." + beacons.size());
NotiyIBeacon(beacons);
}
}
});
try {
beaconManager.startMonitoring(new Region("unique-id-001", null, null, null));
// 비콘 정보 감시를 개시
beaconManager.startRangingBeacons(new Region("unique-id-001", null, null, null));
} catch (Exception e) {
e.printStackTrace();
}
}
public void Bind()
{
Log.d(LOG_TAG, "AndroidNativeBeacon.Bind");
AltBeaconManagerBind(BindType.Bind);
}
public void Unbind()
{
Log.d(LOG_TAG, "AndroidNativeBeacon.UnBind");
AltBeaconManagerBind(BindType.Unbind);
}
// private boolean AltBeaconBindStatus = false;
private void AltBeaconManagerBind(BindType type)
{
Log.d(LOG_TAG, "AndroidNativeBeacon.AltBeaconManagerBind type:" + type.toString());
switch (type)
{
case Bind:
Log.d(LOG_TAG, "AndroidNativeBeacon.AltBeaconManagerBind bind");
beaconManager.bindInternal(this);
break;
// AltBeaconBindStatus = true;
// case Auto:
// if(AltBeaconBindStatus)
// {
// if (clients.size() == 0) {
// Log.d(LOG_TAG, "AndroidNativeBeacon.AltBeaconManagerBind unbind");
// beaconManager.unbindInternal(this);
// }
// else {
// Log.d(LOG_TAG, "AndroidNativeBeacon.AltBeaconManagerBind bind");
// beaconManager.bindInternal(this);
// }
// }
// break;
case Unbind:
Log.d(LOG_TAG, "AndroidNativeBeacon.AltBeaconManagerBind unbind");
beaconManager.unbindInternal(this);
// AltBeaconBindStatus = false;
break;
}
}
private void NotiyIBeacon(Collection<Beacon> beacons)
{
if(beacons != null)
{
//발열이 심해서 거리 계산 하는 반복문을 따로 해보자.
for(Beacon b : beacons)
{
Double _Value = new Double(calculateDistance_double(b));
bDistances.put(b.getId1().toString(), _Value);
}
//발열이 심해서 거리 계산 하는 반복문을 따로 해보자.
Log.i(LOG_TAG, "AndroidNativeBeacon.NotiyIBeacon beacons is null.");
Log.d(LOG_TAG, "AndroidNativeBeacon.NotiyIBeacon catchBeaconDistance : " + catchBeaconDistance);
for (Beacon b : beacons)
{
Log.i(LOG_TAG, "AndroidNativeBeacon.NotiyIBeacon inside forech");
if (beacon == null)
{
Double beaconDistance = null;
if(bDistances.get(b.getId1().toString()) != null)
{
beaconDistance = (Double) bDistances.get(b.getId1().toString());//calculateDistance(b);
}
if(beaconDistance != null)
{
if(beaconDistance.doubleValue() <= catchBeaconDistance)
{
Log.i(LOG_TAG, "AndroidNativeBeacon.NotiyIBeacon beacon is null. put in b data");
beacon = b;
}
else
{
beacon = null;
}
}
else if(beaconDistance == null)
{
Log.i(LOG_TAG, "beaconDistance is NULL");
}
}
else if (beacon != null)
{
Log.i(LOG_TAG, "AndroidNativeBeacon.NotiyIBeacon beacon is not null.");
if (!beacon.getId1().toString().equals(b.getId1().toString()))
{
Double beaconDistance = null;
Double bDistance = null;
if(bDistances.get(beacon.getId1().toString()) != null)
{
beaconDistance = (Double) bDistances.get(beacon.getId1().toString());//calculateDistance(b);
}
if(bDistances.get(b.getId1().toString()) != null)
{
bDistance = (Double) bDistances.get(b.getId1().toString());//calculateDistance(b);
}
Log.i(LOG_TAG, "AndroidNativeBeacon.NotiyIBeacon UUID is not same");
Log.i(LOG_TAG, "beacon.getId1() : " + beacon.getId1() + " , b.getId1() : " + b.getId1());
if(beaconDistance != null && bDistance != null)
{
Log.i(LOG_TAG, "beacon.getId1().Dis : " + beaconDistance.doubleValue() + " , b.getId1().Dis : " + bDistance.doubleValue());
if(beaconDistance.doubleValue() > bDistance.doubleValue())//if (beacon.getDistance() > b.getDistance())
{
if(bDistance.doubleValue() <= catchBeaconDistance)
{
Log.i(LOG_TAG, "AndroidNativeBeacon.NotiyIBeacon b.Distance is short. put in b data");
beacon = b;
}
else
{
beacon = null;
}
}
}
else if(beaconDistance != null && bDistance == null)
{
Log.i(LOG_TAG, "beacon.getId1().Dis : " + beaconDistance.doubleValue());
if(beaconDistance.doubleValue() <= catchBeaconDistance)
{
Log.i(LOG_TAG, "AndroidNativeBeacon.NotiyIBeacon beacon.Distance is short.");
Log.i(LOG_TAG, "bDistance is NULL");
}
else
{
beacon = null;
}
}
else if(beaconDistance == null && bDistance != null)
{
Log.i(LOG_TAG, "b.getId1().Dis : " + bDistance.doubleValue());
if(bDistance.doubleValue() <= catchBeaconDistance)
{
Log.i(LOG_TAG, "AndroidNativeBeacon.NotiyIBeacon b.Distance is short. put in b data");
Log.i(LOG_TAG, "beaconDistance is NULL");
beacon = b;
}
else
{
beacon = null;
}
}
}
else if (beacon.getId1().toString().equals(b.getId1().toString()))
{
Double beaconDistance = (Double) bDistances.get(beacon.getId1().toString());//calculateDistance_double(beacon);
if(beaconDistance.doubleValue() <= catchBeaconDistance)
{
Log.i(LOG_TAG, "AndroidNativeBeacon.NotiyIBeacon UUID is same");
beacon = b;
}
else
{
beacon = null;
}
}
}
}
}
bDistances.clear();
NotiyInform(beacon);
}
protected static double calculateDistance_double(Beacon b)
{
int measuredPower = b.getTxPower();
double rssi = b.getRssi();
if (rssi == 0)
{
Log.i(LOG_TAG, "beacon.getId1() : " + b.getId1().toString() + " , rssi : 0");
return -1.0;
// if we cannot determine distance, return -1.
}
double ratio = rssi*1.0/measuredPower;
if (ratio < 1.0)
{
double other = Math.pow(ratio,10);
Log.i(LOG_TAG, "beacon.getId1() : " + b.getId1().toString() + " , other : " + other);
return Math.pow(ratio,10);
}
else
{
double distance = (0.89976) * Math.pow(ratio, 7.7095) + 0.111;
Log.i(LOG_TAG, "beacon.getId1() : " + b.getId1().toString() + " , distance : " + distance);
return distance;
}
}
protected static Double calculateDistance_Double(Beacon b)
{
int measuredPower = b.getTxPower();
double rssi = b.getRssi();
if (rssi == 0)
{
return -1.0;
// if we cannot determine distance, return -1.
}
double ratio = rssi*1.0/measuredPower;
if (ratio < 1.0)
{
return Math.pow(ratio,10);
}
else
{
Double distance = (0.89976) * Math.pow(ratio, 7.7095) + 0.111;
return distance;
}
}
private void NotiyInform(Beacon b)
{
String data = "";
// "UUID:" + b.getId1()
// "major:" + b.getId2()
// "minor:" + b.getId3()
// "RSSI" + b.getRssi()
// "Name" + b.getBluetoothName();
// "Address" + b.getBluetoothAddress();
if(b != null)
{
data = String.format("%s,%s,%s,%s,%s,%s,%s,%s", System.currentTimeMillis(), b.getId1(), b.getId2(), b.getId3(), b.getTxPower(), b.getRssi(), b.getBluetoothName(), b.getBluetoothAddress());
}
// 로그 출력
Log.i(LOG_TAG, "AndroidNativeBeacon.NotiyInform " + data);
// 각 클라이언트에 진찰한 비콘 정보를 브로드캐스트
// for (int i = 0; i < clients.size(); i++)
// clients.get(i).
OnNotify(data);
}
private enum BindType
{
Auto,
Bind,
Unbind,
};
@Override
public Context getApplicationContext() {
return null;
}
@Override
public void unbindService(ServiceConnection connection) {
}
@Override
public boolean bindService(Intent intent, ServiceConnection connection, int mode) {
return false;
}
}
맨위의 package com.example.beaconplugin; 의 example 은 다들 아시다싶이, 회사명이 들어가는 자리 혹은 관련 명칭이 들어가는 곳이라서 example 으로 대체해놓았습니다.
코드는 예전에 참고로 찾아서 본 투케이2K 님의 https://kkh0977.tistory.com/3314 글을 보고 작업한 것을 이리저리 다른 글도 참고하면서 만든겁니다.
구문을 보시다보면, 본래는 없던 메서드들이 자리하고 있는 것으로 보이는게 있을겁니다.
제가 관련 프로젝트에서 필요한 기능을 추가하려고 구글링을 통해서 이리저리 찾은 메서드들을 집어넣은거라 가볍게 넘어가주시면 됩니다. 그리고 사용하지 않는 메서드들은 왜 남아있는지 의문이 드실수도 있는데, 제가 순수한 안드로이드 프로그래머가 아니라서 노파심에 사용한 예시 코드의 모습이 그대로 남아 있는 상태입니다.
준비가 다되었다면, 빌드탭에서 저 망치를 한번 눌러주시면 빌드를 진행하게 됩니다.
빌드를 진행하면 아래와 같은 진행상태가 나올겁니다.
빌드가 정상적으로 된다면 이전글에서 확인한 해당 경로에 aar 파일이 존재할텐데, 디버그 빌드로 나온 것을 확인하셨을 겁니다.
이것을 릴리즈로 빌드하려면 아래와 같이 우측의 탭을 눌러서 창을 나오게 한 다음에 재생 버튼 같은 것을 눌러주시면 됩니다.
그러면 아래와 같은 창이 하나가 나올텐데, 스샷에 나온것처럼 해당 부분을 더블클릭을 해주면 릴리즈 빌드가 실시 됩니다.
빌드가 끝나고 경로를 가보면 아래와 같이 aar 파일들이 나온 것을 확인 할 수 있습니다.
다음글은 이 aar 파일을 이용해서 유니티에서 비콘을 동작시켜보는 코드들과 사용법을 알려드리겠습니다.
아래 링크는 최근에 발견한 안드로이드 비콘 라이브러리 사용관련 깃허브 글입니다.
제 글보다는 이 글이 더 나은 것 같아서, 첨부했습니다.
'Unity3D+Android' 카테고리의 다른 글
Android beacon library 를 Unity3D에서 사용하기. - 02 [AAR 만들어보기 - 2] (0) | 2023.11.16 |
---|---|
Android beacon library 를 Unity3D에서 사용하기. - 01 [AAR 만들어보기 - 1] (0) | 2023.01.10 |