투케이2K

921. (Android/Java) [TargetSdk 33] observableBeaconScanList : Altbeacon 실시간 비콘 신호 활성 Advertising 코드 본문

Android

921. (Android/Java) [TargetSdk 33] observableBeaconScanList : Altbeacon 실시간 비콘 신호 활성 Advertising 코드

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

[개발 환경 설정]

개발 툴 : AndroidStudio

개발 언어 : Java / Kotlin

 

[소스 코드]

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

- 언어 : Java / Kotlin


- 개발 툴 : AndroidStudio


- 기술 구분 : Bluetooth / Beacon / Advertising

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






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

    /**
     * // --------------------------------------------------------
     * 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] : observableBeaconAdvertising : 비콘 실시간 신호 활성 수행
    // ----------------------------------------------------------
    // TODO [호출 방법 소스 코드]
    // ----------------------------------------------------------
    /*
    try {

        // [비콘 신호 활성에 필요한 변수 선언]
        String uuid = "03c3c219-588e-f909-db0a-320ac6deeff0";
        int major = 1;
        int minor = 36;

        // [비콘 신호 활성 수행]
        C_Beacon_Client_Module.observableBeaconAdvertising(A_Intro.this, uuid, major, minor)
                .subscribeOn(AndroidSchedulers.mainThread()) // [Observable (생성자) 로직을 IO 스레드에서 실행 : 백그라운드]
                .observeOn(Schedulers.io()) // [Observer (관찰자) 로직을 메인 스레드에서 실행]
                .subscribe(new Observer<String>() { // [Observable.create 타입 지정]
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                    }
                    @Override
                    public void onNext(@NonNull String value) {
                        S_Log._W_(ACTIVITY_NAME + " :: 비콘 실시간 신호 활성 :: onNext", new String[]{String.valueOf(value)});

                        // ------------------------------------------------------------
                        // TODO [실시간 비콘 신호 활성 성공 처리]
                        // ------------------------------------------------------------
                        if (String.valueOf(value).equals(C_Beacon_Client_Module.BEACON_ADVERTISING_START)){
                            S_Log.w("BEACON_ADVERTISING", "=================== [Start] : [Beacon Advertising] ===================");
                        }

                        // ------------------------------------------------------------
                        // TODO [실시간 비콘 신호 활성 종료 처리]
                        // ------------------------------------------------------------
                        if (String.valueOf(value).equals(C_Beacon_Client_Module.BEACON_ADVERTISING_STOP)){
                            S_Log.e("BEACON_ADVERTISING", "=================== [Stop] : [Beacon Advertising] ===================");
                        }

                    }
                    @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 BeaconTransmitter beaconTransmitter = null;
    static BluetoothAdapter ble_advertising_Adapter;
    static String beacon_advertising_LOG = "";
    static Handler advertisingHandler = null;

    public static final String BEACON_ADVERTISING_START = "BEACON_ADVERTISING_START";
    public static final String BEACON_ADVERTISING_STOP = "BEACON_ADVERTISING_STOP";
    public static final int BEACON_ADVERTISING_TIME_OUT = 30;

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

    public static Observable<String> observableBeaconAdvertising(Context mContext, String uuid, int major, int minor) {

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

            // [변수 초기화]
            beaconTransmitter = null;
            ble_advertising_Adapter = null;
            advertisingHandler = null;
            beacon_advertising_LOG = "";

            try {
                // ===============================================================
                S_Log._D_(ACTIVITY_NAME + " :: observableBeaconAdvertising :: 비콘 실시간 신호 활성 시작", new String[]{
                        "UUID :: " + String.valueOf(uuid),
                        "MAJOR :: " + String.valueOf(major),
                        "NINOR :: " + String.valueOf(minor)
                });
                // ===============================================================

                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_advertising_LOG = "[ERROR] : Beacon Advertising Scan Permission Location Not Granted (비콘 실시간 신호 활성에 필요한 권한을 확인해주세요. / 위치 권한)";

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

                            // [리턴 반환 실시]
                            if (subscriber != null && subscriber.isDisposed() == false) {
                                subscriber.onError(new Throwable(beacon_advertising_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
                                    || ActivityCompat.checkSelfPermission(mContext, Manifest.permission.BLUETOOTH_ADVERTISE) != PackageManager.PERMISSION_GRANTED) {

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

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

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

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

                            // [Input Name, UUID 널 방어 로직 체크]
                            if (C_Util.stringNotNull(uuid) == false || minor < 0 || major < 0){

                                // [인풋 데이터 Is Null]
                                beacon_advertising_LOG = "[ERROR] : Beacon Advertising Uuid , Major , Minor Is Null (비콘 실시간 신호 활성에 필요한 Uuid , Major , Minor 값을 확인해주세요.)";

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

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

                            }

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

                            // [BluetoothManager 객체 생성]
                            BluetoothManager bluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
                            ble_advertising_Adapter = bluetoothManager.getAdapter();

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

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

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

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

                            }

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

                            // TODO [isMultipleAdvertisementSupported] : 신호 광고 활성 지원 확인
                            if (ble_advertising_Adapter.isMultipleAdvertisementSupported()) {

                                // TODO [Beacon 비콘 신호 활성 정보 생성]
                                Beacon beacon = new Beacon.Builder()
                                        .setId1(uuid.toLowerCase()) //TODO UUID
                                        .setId2(String.valueOf(major))  // 비콘 major 값 정의 (정수) //TODO Major
                                        .setId3(String.valueOf(minor))  // 비콘 minor 값 정의 (정수) //TODO Minor
                                        .setManufacturer(0x004c) // TODO 아이폰에서도 받을 수 있게 하기 위함 - 아이폰은 지들 제조사하고 ibeacon밖에 스캔못함(안드로이드에서도 받아진다)
                                        .setTxPower(-59)  // 전송할 때의 신호 세기 dB (-65보다 -59가 더 큰 신호 즉, -59가 더 가까이있다)
                                        .setDataFields(Arrays.asList(new Long[]{0L}))
                                        .build();


                                // TODO [Layout 설정] : 아이폰에서도 받을 수 있게 하기 위함 - 아이폰은 지들 제조사하고 ibeacon 밖에 스캔못함 (안드로이드에서도 받아진다)
                                BeaconParser beaconParser = new BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24");
                                //beaconParser.setHardwareAssistManufacturerCodes(new int[]{0x4C00});


                                // TODO [비콘 신호 활성 종료 처리 핸들러 등록]
                                advertisingHandler = new Handler(Looper.getMainLooper());
                                advertisingHandler.postDelayed(new Runnable() {
                                    @Override
                                    public void run() {

                                        // [비콘 신호 활성 종료 로그]
                                        beacon_advertising_LOG = "[Stop] : Beacon Advertising Stop";

                                        S_Log._E_(ACTIVITY_NAME + " :: observableBeaconAdvertising :: 비콘 실시간 신호 활성 종료", new String[]{
                                                "M_LOG :: " + String.valueOf(beacon_advertising_LOG),
                                                "UUID :: " + String.valueOf(uuid),
                                                "MAJOR :: " + String.valueOf(major),
                                                "MINOR :: " + String.valueOf(minor)
                                        });

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

                                        // [비콘 신호 활성 종료]

                                        if (beaconTransmitter != null) {
                                            if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.BLUETOOTH_ADVERTISE) != PackageManager.PERMISSION_GRANTED) {
                                                return;
                                            }
                                            beaconTransmitter.stopAdvertising();
                                        }
                                        return;
                                    }

                                }, BEACON_ADVERTISING_TIME_OUT * 1000);


                                // TODO [비콘 신호 활성 수행]
                                beaconTransmitter = new BeaconTransmitter(mContext, beaconParser);
                                beaconTransmitter.startAdvertising(beacon, new AdvertiseCallback() {
                                    @Override
                                    public void onStartSuccess(AdvertiseSettings settingsInEffect) {
                                        super.onStartSuccess(settingsInEffect);

                                        // [비콘 신호 활성 성공 로그]
                                        beacon_advertising_LOG = "[Success] : Beacon Advertising Start";

                                        S_Log._W_(ACTIVITY_NAME + " :: observableBeaconAdvertising :: 비콘 실시간 신호 활성 수행", new String[]{
                                                "M_LOG :: " + String.valueOf(beacon_advertising_LOG),
                                                "UUID :: " + String.valueOf(uuid),
                                                "MAJOR :: " + String.valueOf(major),
                                                "MINOR :: " + String.valueOf(minor)
                                        });

                                        // [리턴 반환 실시]
                                        if (subscriber != null && subscriber.isDisposed() == false) {
                                            subscriber.onNext(BEACON_ADVERTISING_START);
                                            //subscriber.onComplete(); // TODO [완료 처리는 비콘 신호 활성 종료 핸들러에서 처리]
                                        }

                                    }
                                    @Override
                                    public void onStartFailure(int errorCode) {
                                        super.onStartFailure(errorCode);
                                        
                                        // [비콘 신호 활성 실패 로그]
                                        beacon_advertising_LOG = "[Error] : Beacon Advertising onStartFailure (ErrorCode = " +String.valueOf(errorCode)+ ")";

                                        S_Log._E_(ACTIVITY_NAME + " :: observableBeaconAdvertising :: 비콘 실시간 신호 활성 실패", new String[]{
                                                "M_LOG :: " + String.valueOf(beacon_advertising_LOG),
                                                "UUID :: " + String.valueOf(uuid),
                                                "MAJOR :: " + String.valueOf(major),
                                                "MINOR :: " + String.valueOf(minor)
                                        });

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

                                    }
                                });

                            }
                            else {

                                // TODO [블루투스가 Advertising 지원 하지 않은 경우 종료]
                                beacon_advertising_LOG = "[ERROR] : Bluetooth Advertising Support False (블루투스 신호 광고 활성 지원 확인 필요)";

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

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

                            }

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

                        }

                    }
                }, 0);

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

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

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

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

        });
    }

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






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

D///===========//: ================================================
I/: [LOG :: CLASS PLACE :: com.example.javaproject.C_Module.C_Beacon_Client_Module.lambda$observableBeaconAdvertising$1(C_Beacon_Client_Module.java:432)]
I/: ----------------------------------------------------
I/: [LOG :: DESCRIPTION :: C_Beacon_Client_Module :: observableBeaconAdvertising :: 비콘 실시간 신호 활성 시작]
I/: ----------------------------------------------------
I/: [LOG :: UUID :: 03c3c219-588e-f909-db0a-320ac6deeff0]
I/: ----------------------------------------------------
I/: [LOG :: MAJOR :: 1]
I/: ----------------------------------------------------
I/: [LOG :: NINOR :: 36]
D///===========//: ================================================


D/CompatibilityChangeReporter: Compat change id reported: 263076149; UID 10510; state: DISABLED
I/BluetoothAdapter: BluetoothAdapter() : com.example.javaproject.staging
D/BluetoothAdapter: getBleEnabledArray(): ON


I/BluetoothAdapter: BLE support array set: [true, true, true, true, true, true, true]


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


D/BluetoothAdapter: getBleEnabledArray(): ON
D/BluetoothAdapter: getBleEnabledArray(): ON


I/BluetoothAdapter: BluetoothAdapter() : com.example.javaproject.staging
D/BluetoothAdapter: getBleEnabledArray(): ON


D/BluetoothLeAdvertiser: onAdvertisingSetStarted(2, 1, 0)
I/BeaconTransmitter: Advertisement start succeeded.


W///===========//: ================================================
I/: [LOG :: CLASS PLACE :: com.example.javaproject.C_Module.C_Beacon_Client_Module$2$2.onStartSuccess(C_Beacon_Client_Module.java:595)]
I/: ----------------------------------------------------
I/: [LOG :: DESCRIPTION :: C_Beacon_Client_Module :: observableBeaconAdvertising :: 비콘 실시간 신호 활성 수행]
I/: ----------------------------------------------------
I/: [LOG :: M_LOG :: [Success] : Beacon Advertising Start]
I/: ----------------------------------------------------
I/: [LOG :: UUID :: 03c3c219-588e-f909-db0a-320ac6deeff0]
I/: ----------------------------------------------------
I/: [LOG :: MAJOR :: 1]
I/: ----------------------------------------------------
I/: [LOG :: MINOR :: 36]
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