투케이2K

920. (Android/Java) [TargetSdk 33] observableBeaconScanList : Altbeacon 실시간 비콘 스캔 Scan 소스 코드 본문

Android

920. (Android/Java) [TargetSdk 33] observableBeaconScanList : Altbeacon 실시간 비콘 스캔 Scan 소스 코드

투케이2K 2024. 12. 24. 17:28

[개발 환경 설정]

개발 툴 : AndroidStudio

개발 언어 : Java / Kotlin

 

[소스 코드]

// --------------------------------------------------------------------------------------
[개발 및 테스트 환경]
// --------------------------------------------------------------------------------------

- 언어 : Java / Kotlin


- 개발 툴 : AndroidStudio


- 기술 구분 : Bluetooth / Beacon / Scan

// --------------------------------------------------------------------------------------






// --------------------------------------------------------------------------------------
[사전) 라이브러리 의존성 설정 및 퍼미션 권한 설정 정리]
// --------------------------------------------------------------------------------------

    /**
     * // --------------------------------------------------------
     * TODO [클래스 설명]
     * // --------------------------------------------------------
     * 1. TODO [설명] : 비콘 스캔 및 신호 활성 수행 클래스 : 안드로이드 상위 버전 전용 (S 안드로이드 12 이상)
     * // --------------------------------------------------------
     * 2. 라이브러리 추가 방법 : Git (https://github.com/AltBeacon/android-beacon-library)
     *
     *     // TODO [altbeacon : 비콘 스캔 및 신호 활성 라이브러리]
     *     implementation 'org.altbeacon:android-beacon-library:2.19'
     *     //noinspection GradleCompatible
     *     implementation 'com.android.support:localbroadcastmanager:28.0.0'
     *
     * // --------------------------------------------------------
     * 3. 필요 퍼미션 권한 : 위치 및 GPS 권한 , 블루투스 권한 - SCAN , ADVERTISE , CONNECT
     *
     * // TODO [공통]
     * <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
     * <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
     *
     * <uses-permission android:name="android.permission.BLUETOOTH"/>
     * <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
     * <uses-feature android:name="android.hardware.bluetooth_le" />
     *
     * // TODO [안드로이드 12 이상 : S 버전]
     * <uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>
     * <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE"/>
     * <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
     * // --------------------------------------------------------
     * */

// --------------------------------------------------------------------------------------






// --------------------------------------------------------------------------------------
[소스 코드]
// --------------------------------------------------------------------------------------

    // ------------------------------------------------------------
    // TODO [SEARCH FAST] : observableBeaconScanList : 비콘 실시간 목록 스캔 결과 반환
    // ------------------------------------------------------------
    // TODO [호출 방법 소스 코드]
    // ------------------------------------------------------------
    /*
    try {

        C_Beacon_Client_Module.observableBeaconScanList(A_Intro.this)
                .subscribeOn(AndroidSchedulers.mainThread()) // [Observable (생성자) 로직을 IO 스레드에서 실행 : 백그라운드]
                .observeOn(Schedulers.io()) // [Observer (관찰자) 로직을 메인 스레드에서 실행]
                .subscribe(new Observer<ArrayList<HashMap<String, Object>>>() { // [Observable.create 타입 지정]
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                    }
                    @Override
                    public void onNext(@NonNull ArrayList<HashMap<String, Object>> value) {
                        S_Log._W_(ACTIVITY_NAME + " :: 비콘 실시간 스캔 :: onNext", new String[]{String.valueOf(value)});
                    }
                    @Override
                    public void onError(@NonNull Throwable e) {
                        S_Log._E_(ACTIVITY_NAME + " :: 비콘 실시간 스캔 :: onError", new String[]{String.valueOf(e.getMessage())});
                    }
                    @Override
                    public void onComplete() {
                    }
                });

    } catch (Exception e) {
        S_Log._printStackTrace_(mContext, S_FinalMsg.LOG_BUG_STATE, null, e);
    }
    */
    // ------------------------------------------------------------

    // [필요 변수 선언]
    static BeaconManager beaconManager = null;
    static BluetoothAdapter ble_Adapter;
    static int beacon_COUNT = 0;
    static String beacon_LOG = "";
    static ArrayList<HashMap<String, Object>> beacon_List = null;
    public static final int beacon_Scan_TimeOut = 10;

    // ------------------------------------------------------------

    public static Observable<ArrayList<HashMap<String, Object>>> observableBeaconScanList(Context mContext) {

        // [로직 처리 실시]
        return Observable.create(subscriber -> {

            // [변수 초기화]
            ble_Adapter = null;
            beacon_COUNT = 0;
            beacon_LOG = "";
            beacon_List = null;
            beaconManager = null;

            try {
                // ===============================================================
                S_Log._D_(ACTIVITY_NAME + " :: observableBeaconScanList :: 비콘 실시간 목록 리스트 스캔 시작", null);
                // ===============================================================

                new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
                    @Override
                    public void run() {

                        // [퍼미션 권한 체크 실시]
                        if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
                                || ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {

                            // [퍼미션이 부여되어있지 않은 경우 종료]
                            beacon_LOG = "[ERROR] : Beacon Scan Permission Location Not Granted (비콘 실시간 목록 스캔에 필요한 권한을 확인해주세요. / 위치 권한)";

                            S_Log._E_(ACTIVITY_NAME + " :: observableBeaconScanList :: 에러 발생", new String[]{String.valueOf(beacon_LOG)});

                            // [리턴 반환 실시]
                            if (subscriber != null && subscriber.isDisposed() == false) {
                                subscriber.onError(new Throwable(beacon_LOG));
                                subscriber.onComplete();
                            }
                            return;

                        } else {

                            // ---------------------------------------------------------------

                            // TODO [Android 12 이상 권한 체크 방어 로직]
                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
                                    && ActivityCompat.checkSelfPermission(mContext, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED
                                    || ActivityCompat.checkSelfPermission(mContext, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {

                                // [퍼미션이 부여되어있지 않은 경우 종료]
                                beacon_LOG = "[ERROR] : Beacon Scan Permission Bluetooth Scan Or Connect Not Granted (비콘 실시간 목록 스캔에 필요한 권한을 확인해주세요. / 블루투스 스캔 및 연결 권한)";

                                S_Log._E_(ACTIVITY_NAME + " :: observableBeaconScanList :: 에러 발생", new String[]{String.valueOf(beacon_LOG)});

                                // [리턴 반환 실시]
                                if (subscriber != null && subscriber.isDisposed() == false) {
                                    subscriber.onError(new Throwable(beacon_LOG));
                                    subscriber.onComplete();
                                }
                                return;
                            }

                            // ---------------------------------------------------------------

                            // [블루투스 페어링 목록 리스트 스캔 실시]
                            BluetoothManager bluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
                            ble_Adapter = bluetoothManager.getAdapter();

                            if (ble_Adapter == null || ble_Adapter.isEnabled() == false) {

                                // [블루투스가 활성화 되어 있지 않은 경우 종료]
                                beacon_LOG = "[ERROR] : Bluetooth Adapter Is Null Or Enabled False (블루투스 기능 지원 여부 및 블루투스 기능 활성 상태 확인 필요)";

                                S_Log._E_(ACTIVITY_NAME + " :: observableBeaconScanList :: 에러 발생", new String[]{String.valueOf(beacon_LOG)});

                                // [리턴 반환 실시]
                                if (subscriber != null && subscriber.isDisposed() == false) {
                                    subscriber.onError(new Throwable(beacon_LOG));
                                    subscriber.onComplete();
                                }
                                return;

                            }

                            // ---------------------------------------------------------------

                            // [ArrayList 객체 생성]
                            beacon_List = new ArrayList();

                            // ---------------------------------------------------------------

                            // TODO [BeaconManager 인스턴스 획득 및 Region 등록]
                            beaconManager = BeaconManager.getInstanceForApplication(mContext); // [객체 생성]
                            Region region = new Region("myRangingUniqueId", null, null, null); // [Region 지정]


                            // TODO [Beacon 모니터링 addRangeNotifier 등록]

                            beaconManager.addRangeNotifier(new RangeNotifier() {
                                @Override
                                public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
                                    if (beacons.size() > 0) {
                                        S_Log.w("DID_RANGE_BEACON", "========================= [Beacon Scan Size : "+String.valueOf(beacons.size())+"] =========================");

                                        ArrayList<Beacon> beaconList = new ArrayList<>(beacons);

                                        for (int i=0; i<beaconList.size(); i++){
                                            Beacon beacon = (Beacon) beaconList.get(i);

                                            HashMap<String, Object> map = new HashMap<>();

                                            map.put("Uuid", beacon.getId1().toString());
                                            map.put("Major", beacon.getId2().toString());
                                            map.put("Minor", beacon.getId3().toString());

                                            if (beacon_List.contains(map) == false){
                                                beacon_List.add(map);
                                            }
                                        }

                                    }
                                    else {
                                        S_Log.e("DID_RANGE_BEACON", "========================= [Beacon Scan Size Null] =========================");
                                    }
                                }
                            });


                            // TODO [블루투스가 스캔을 중지하지 않도록 설정]
                            beaconManager.setRegionStatePersistenceEnabled(false);


                            // TODO [레이아웃 지정 - IOS , Android 모두 스캔 가능]
                            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"));


                            // TODO [실시간 비콘 스캔 모니터링 시작]
                            beaconManager.startRangingBeacons(region);

                            // ---------------------------------------------------------------

                            // TODO [비콘 스캔 종료 타이머 설정]
                            Handler handler = new Handler(Looper.getMainLooper());
                            handler.postDelayed(new Runnable() {
                                @Override
                                public void run() {

                                    // [로그 추가]
                                    beacon_LOG = "[Success] : Beacon List Scan";

                                    S_Log._W_(ACTIVITY_NAME + " :: observableBeaconScanList :: 비콘 실시간 리스트 목록 스캔", new String[]{
                                            "M_LOG :: " + String.valueOf(beacon_LOG),
                                            "SCAN_COUNT :: " + String.valueOf(beacon_List.size()),
                                            "SCAN_LIST :: " + String.valueOf(beacon_List)
                                    });

                                    // [리턴 반환 실시]
                                    if (subscriber != null && subscriber.isDisposed() == false) {
                                        subscriber.onNext(beacon_List);
                                        subscriber.onComplete();
                                    }

                                    if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
                                        return;
                                    }

                                    // TODO [비콘 리스트 스캔 종료]
                                    if (beaconManager != null){
                                        beaconManager.stopRangingBeacons(region);
                                    }

                                }

                            }, beacon_Scan_TimeOut * 1000);

                            // ---------------------------------------------------------------

                        }

                    }
                }, 0);

            } catch (final Exception e) {
                S_Log._printStackTrace_(mContext, S_FinalMsg.LOG_BUG_STATE, null, e);

                S_Log._E_(ACTIVITY_NAME + " :: observableBeaconScanList :: EXCEPTION", new String[]{String.valueOf(e.getMessage())});

                try {
                    // [에러 메시지 삽입]
                    beacon_LOG = "[EXCEPTION] : " + String.valueOf(e.getMessage());

                    // [리턴 반환 실시]
                    if (subscriber != null && subscriber.isDisposed() == false) {
                        subscriber.onError(new Throwable(beacon_LOG));
                        subscriber.onComplete();
                    }
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }

        });
    }

// --------------------------------------------------------------------------------------






// --------------------------------------------------------------------------------------
[결과 출력]
// --------------------------------------------------------------------------------------

D///===========//: ================================================
I/: [LOG :: CLASS PLACE :: com.example.javaproject.C_Module.C_Beacon_Client_Module.lambda$observableBeaconScanList$0(C_Beacon_Client_Module.java:167)]
I/: ----------------------------------------------------
I/: [LOG :: DESCRIPTION :: C_Beacon_Client_Module :: observableBeaconScanList :: 비콘 실시간 목록 리스트 스캔 시작]
D///===========//: ================================================


I/BluetoothAdapter: BluetoothAdapter() : com.example.javaproject.staging
I/BeaconManager: BeaconManager started up on pid 14020 named 'com.example.javaproject.staging' for application package 'com.example.javaproject.staging'.  isMainProcess=true


D/BeaconParser: Parsing beacon layout: m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25
D/BeaconParser: Parsing beacon layout: m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25


I/ScanJob: ScanJob Lifecycle START: org.altbeacon.beacon.service.ScanJob@6572009
I/CycledLeScanner: Using Android O scanner
I/ScanJob: Using immediateScanJobId from manifest: 208352939
I/ScanJob: Running immediate scan job: instance is org.altbeacon.beacon.service.ScanJob@6572009
I/ScanJob: scanJob version 2.19 is starting up on the main process


W/ModelSpecificDistanceCalculator: Cannot find match for this device.  Using default
W/ModelSpecificDistanceCalculator: Cannot find match for this device.  Using default


I/BluetoothAdapter: BluetoothAdapter() : com.example.javaproject.staging
I/BluetoothAdapter: STATE_ON
I/BluetoothAdapter: STATE_ON


D/BluetoothLeScanner: Stop Scan with callback intent
I/BluetoothAdapter: STATE_ON
I/BluetoothAdapter: STATE_ON


I/ScanJob: Scan job running for 300000 millis
I/BluetoothAdapter: STATE_ON
I/BluetoothAdapter: STATE_ON


D/BluetoothLeScanner: Start Scan with callback
D/BluetoothLeScanner: onScannerRegistered() - status=0 scannerId=13 mScannerId=0


W///===========//: ================================================
I/: [LOG :: CLASS PLACE :: com.example.javaproject.C_Module.C_Beacon_Client_Module$1$2.run(C_Beacon_Client_Module.java:292)]
I/: ----------------------------------------------------
I/: [LOG :: DESCRIPTION :: C_Beacon_Client_Module :: observableBeaconScanList :: 비콘 실시간 리스트 목록 스캔]
I/: ----------------------------------------------------
I/: [LOG :: M_LOG :: [Success] : Beacon List Scan]
I/: ----------------------------------------------------
I/: [LOG :: SCAN_COUNT :: 2]
I/: ----------------------------------------------------
I/: [LOG :: SCAN_LIST :: [{Uuid=03c3c219-588e-f909-db0a-320ac6deeff0, Major=0, Minor=263}, {Uuid=03e6addb-1a0c-5909-db0d-450ac9deeff0, Major=0, Minor=263}]]
W///===========//: ================================================

// --------------------------------------------------------------------------------------






// --------------------------------------------------------------------------------------
[참고 사이트]
// --------------------------------------------------------------------------------------

[altbeacon 라이브러리 사용해 실시간 비콘 신호 스캔 및 비콘 신호 활성 실시]

https://blog.naver.com/kkh0977/222346562441?trackingCode=blog_bloghome_searchlist


[alt beacon 비콘 빌드 오류 - debugCompileClasspath , Bad Gateway 502]

https://blog.naver.com/kkh0977/222623395643?trackingCode=blog_bloghome_searchlist


[비콘 (beacon) 스캔이 되지 않는 경우 해결 조치]

https://blog.naver.com/kkh0977/222696189077?trackingCode=blog_bloghome_searchlist

// --------------------------------------------------------------------------------------
 
반응형
Comments