투케이2K

156. (TWOK/UTIL) [Android/Java] C_Bluetooth_Advertising_Scan_Module : 블루투스 스캔 및 신호 활성 클래스 본문

투케이2K 유틸파일

156. (TWOK/UTIL) [Android/Java] C_Bluetooth_Advertising_Scan_Module : 블루투스 스캔 및 신호 활성 클래스

투케이2K 2025. 1. 12. 10:56

[설 명]

프로그램 : Android / Java

설 명 : C_Bluetooth_Advertising_Scan_Module : 블루투스 스캔 및 신호 활성 클래스

 

[소스 코드]

 

package com.example.javaproject.C_Module;

import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertiseSettings;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelUuid;

import androidx.core.app.ActivityCompat;

import com.example.javaproject.C_Util;
import com.example.javaproject.S_FinalData;
import com.example.javaproject.S_Log;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import io.reactivex.rxjava3.core.Observable;

public class C_Bluetooth_Advertising_Scan_Module {


    /**
     * // --------------------------------------------------------------------------------------
     * TODO [클래스 설명]
     * // --------------------------------------------------------------------------------------
     * 1. TODO [설명] : 블루투스 리스트 스캔 및 신호 활성 수행 클래스
     * // --------------------------------------------------------------------------------------
     * 2. 필요 퍼미션 권한 : 위치 및 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] : observableBluetoothPairingScanList : 블루투스 페어링 목록 스캔 결과 반환
     * // --------------------------------------------------------------------------------------
     * [SEARCH FAST] : observableBluetoothLeScanList : 블루투스 실시간 목록 스캔 결과 반환
     *
     * >> IOS 에서 Advertising 한 Name 정보로 구분 필요
     * // --------------------------------------------------------------------------------------
     * [SEARCH FAST] : observableBluetoothAdvertising : 블루투스 실시간 신호 활성 수행
     *
     * >> IOS 에서 블루투스 스캔 시 Name 정보 확인 가능
     * // --------------------------------------------------------------------------------------
     *
     * // --------------------------------------------------------------------------------------
     *
     * // --------------------------------------------------------------------------------------
     */





    // ------------------------------------------------------------------------------------------
    // TODO [사용 방법]
    // ------------------------------------------------------------------------------------------
    /*
     */
    // ------------------------------------------------------------------------------------------





    // ------------------------------------------------------------------------------------------
    // TODO [전역 변수 선언]
    // ------------------------------------------------------------------------------------------
    private static String ACTIVITY_NAME = "C_Bluetooth_Advertising_Scan_Module";





    // -----------------------------------------------------------------------------------------
    // TODO [SEARCH FAST] : observableBluetoothPairingScanList : 블루투스 페어링 목록 스캔 결과 반환
    // -----------------------------------------------------------------------------------------
    // TODO [호출 방법 소스 코드]
    // -----------------------------------------------------------------------------------------
    /*
    try {

        C_Bluetooth_Advertising_Scan_Module.observableBluetoothPairingScanList(A_Intro.this)
                .subscribeOn(AndroidSchedulers.mainThread()) // [Observable (생성자) 로직을 IO 스레드에서 실행 : 백그라운드]
                .observeOn(Schedulers.io()) // [Observer (관찰자) 로직을 메인 스레드에서 실행]
                .subscribe(new Observer<ArrayList>() { // [Observable.create 타입 지정]
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                    }
                    @Override
                    public void onNext(@NonNull ArrayList 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_FinalData.LOG_BUG_STATE, null, e);
    }
    */
    // -----------------------------------------------------------------------------------------
    static BluetoothAdapter ble_Pair_Adapter;
    static int ble_Pair_COUNT = 0;
    static String ble_Pair_LOG = "";
    static ArrayList ble_Paired_List = null;

    public static Observable<ArrayList> observableBluetoothPairingScanList(Context mContext) {

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

            // [변수 초기화]
            ble_Pair_Adapter = null;
            ble_Pair_COUNT = 0;
            ble_Pair_LOG = "";
            ble_Paired_List = null;

            try {
                // ===============================================================
                S_Log._D_(ACTIVITY_NAME + " :: observableBluetoothPairingScanList :: 블루투스 페어링 목록 리스트 스캔 시작", 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) {

                            // [퍼미션이 부여되어있지 않은 경우 종료]
                            ble_Pair_LOG = "[ERROR] : Bluetooth Pairing Scan Permission Location Not Granted (블루투스 페어링 목록 스캔에 필요한 권한을 확인해주세요. / 위치 권한)";

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

                            // [리턴 반환 실시]
                            if (subscriber != null && subscriber.isDisposed() == false) {
                                subscriber.onError(new Throwable(ble_Pair_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) {

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

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

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

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

                            // [블루투스 페어링 목록 리스트 스캔 실시]

                            BluetoothManager bluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
                            ble_Pair_Adapter = bluetoothManager.getAdapter();

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

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

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

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

                            }

                            Set<BluetoothDevice> pairedDevices = ble_Pair_Adapter.getBondedDevices();

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

                            if (pairedDevices != null && pairedDevices.size() > 0) { // [스캔 된 목록이 있는 경우]

                                ble_Pair_LOG = "[Success] : Bluetooth Pairing List Found";
                                ble_Pair_COUNT = pairedDevices.size(); // [카운트 추가]
                                ble_Paired_List = new ArrayList<>(); // [리스트 객체 초기화]

                                for (BluetoothDevice device : pairedDevices) {

                                    //TODO [JSON 형식으로 포맷 실시]
                                    Map map = new HashMap();
                                    try {

                                        map.put("name", String.valueOf(device.getName()));
                                        map.put("address", String.valueOf(device.getAddress()));

                                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                                            map.put("alias", String.valueOf(device.getAlias()));
                                        }

                                        map.put("uuid", String.valueOf(Arrays.toString(device.getUuids())).replaceAll("\\[", "").replaceAll("\\]", ""));
                                        map.put("state", String.valueOf(device.getBondState()));
                                        map.put("type", String.valueOf(device.getType()));

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

                                    ble_Paired_List.add(map);
                                }

                            } else {

                                // [페어링 된 목록이 없는 경우]
                                ble_Pair_LOG = "[Success] : Bluetooth Pairing List Not Found";
                                ble_Pair_COUNT = 0;
                                ble_Paired_List = new ArrayList<>();
                            }

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

                            S_Log._W_(ACTIVITY_NAME + " :: observableBluetoothPairingScanList :: 블루투스 페어링 리스트 확인", new String[]{
                                    "M_LOG :: " + String.valueOf(ble_Pair_LOG),
                                    "SCAN_COUNT :: " + String.valueOf(ble_Pair_COUNT),
                                    "SCAN_LIST :: " + String.valueOf(ble_Paired_List)
                            });

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

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

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

                        }

                    }
                }, 0);

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

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

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

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

        });
    }





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

        C_Bluetooth_Advertising_Scan_Module.observableBluetoothLeScanList(A_Intro.this)
                .subscribeOn(AndroidSchedulers.mainThread()) // [Observable (생성자) 로직을 IO 스레드에서 실행 : 백그라운드]
                .observeOn(Schedulers.io()) // [Observer (관찰자) 로직을 메인 스레드에서 실행]
                .subscribe(new Observer<ArrayList>() { // [Observable.create 타입 지정]
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                    }
                    @Override
                    public void onNext(@NonNull ArrayList 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_FinalData.LOG_BUG_STATE, null, e);
    }
    */
    // -----------------------------------------------------------------------------------------
    static BluetoothAdapter ble_Le_Adapter;
    static int ble_Le_COUNT = 0;
    static String ble_Le_LOG = "";
    static ArrayList ble_Le_List = null;
    public static final int blue_Le_TimeOut = 5;

    public static Observable<ArrayList> observableBluetoothLeScanList(Context mContext) {

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

            // [변수 초기화]
            ble_Le_Adapter = null;
            ble_Le_COUNT = 0;
            ble_Le_LOG = "";
            ble_Le_List = null;

            try {
                // ===============================================================
                S_Log._D_(ACTIVITY_NAME + " :: observableBluetoothLeScanList :: 블루투스 실시간 목록 리스트 스캔 시작", 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) {

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

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

                            // [리턴 반환 실시]
                            if (subscriber != null && subscriber.isDisposed() == false) {
                                subscriber.onError(new Throwable(ble_Le_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) {

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

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

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

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

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

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

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

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

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

                            }

                            BluetoothLeScanner bluetoothLeScanner = ble_Le_Adapter.getBluetoothLeScanner();
                            ble_Le_List = new ArrayList<>(); // [리스트 객체 초기화]

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

                            // TODO [블루투스 리스트 스캔 콜백 함수 정의] : [IOS 에서 블루투스 advertising 시 NAME 정보는 표시 됨]
                            ScanCallback scanCallback = new ScanCallback() {

                                // TODO [BLE 광고가 발견되면 콜백합니다.]
                                @Override
                                public void onScanResult(int callbackType, ScanResult result) {
                                    BluetoothDevice device = result.getDevice();

                                    HashMap<String, Object> map = new HashMap<>();
                                    map.put("address", device.getAddress());

                                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                                        if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
                                            return;
                                        }
                                        map.put("alias", device.getAlias());
                                    }

                                    map.put("name", device.getName());
                                    map.put("bondState", device.getBondState());
                                    map.put("type", device.getType());
                                    map.put("uuid", device.getUuids());

                                    // [리스트에 추가]
                                    if (subscriber != null && subscriber.isDisposed() == false
                                            && ble_Le_List != null && ble_Le_List.toString().contains(map.toString()) == false){
                                        S_Log.w("BLE_SCAN", map.toString());
                                        ble_Le_List.add(map);
                                    }
                                }

                                // TODO [일괄 처리 결과가 전달될 때 콜백합니다.]
                                @Override
                                public void onBatchScanResults(List<ScanResult> results) {
                                    for (ScanResult result : results) {
                                        BluetoothDevice device = result.getDevice();

                                        HashMap<String, Object> map = new HashMap<>();
                                        map.put("address", device.getAddress());

                                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                                            if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
                                                return;
                                            }
                                            map.put("alias", device.getAlias());
                                        }

                                        map.put("name", device.getName());
                                        map.put("bondState", device.getBondState());
                                        map.put("type", device.getType());
                                        map.put("uuid", device.getUuids());

                                        // [리스트에 추가]
                                        if (subscriber != null && subscriber.isDisposed() == false
                                                && ble_Le_List != null && ble_Le_List.toString().contains(map.toString()) == false){
                                            S_Log.w("BLE_BATCH", map.toString());
                                            ble_Le_List.add(map);
                                        }

                                    }
                                }

                                // TODO [스캔을 시작할 수 없을 때 콜백합니다.]
                                @Override
                                public void onScanFailed(int errorCode) {
                                    ble_Le_LOG = "[Error] : Bluetooth onScanFailed ErrorCode (" + String.valueOf(errorCode) + ")";

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

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

                            // TODO [블루투스 리스트 스캔 시작]
                            bluetoothLeScanner.startScan(scanCallback);

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

                            // TODO [블루투스 리스트 스캔 타임 아웃 핸들러 지정]
                            Handler handler = new Handler(Looper.getMainLooper());
                            handler.postDelayed(new Runnable() {
                                @Override
                                public void run() {

                                    // [로그 추가]
                                    if (C_Util.stringNotNull(ble_Le_LOG) == false) { // TODO [에러가 발생하지 않은 경우]
                                        ble_Le_LOG = "[Success] : Bluetooth Le List Scan";
                                    }

                                    S_Log._W_(ACTIVITY_NAME + " :: observableBluetoothLeScanList :: 블루투스 실시간 리스트 목록 스캔", new String[]{
                                            "M_LOG :: " + String.valueOf(ble_Le_LOG),
                                            "SCAN_COUNT :: " + String.valueOf(ble_Le_List.size()),
                                            "SCAN_LIST :: " + String.valueOf(ble_Le_List)
                                    });

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


                                    // TODO [블루투스 리스트 스캔 종료]
                                    bluetoothLeScanner.stopScan(scanCallback);


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

                                }

                            }, blue_Le_TimeOut * 1000);

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

                        }

                    }
                }, 0);

            } catch (final Exception e) {

                S_Log._printStackTrace_(mContext, S_FinalData.LOG_BUG_STATE, null, e);

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

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

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

        });
    }






    // -----------------------------------------------------------------------------------------
    // TODO [SEARCH FAST] : observableBluetoothAdvertising : 블루투스 실시간 신호 활성 수행
    // -----------------------------------------------------------------------------------------
    // TODO [호출 방법 소스 코드]
    // -----------------------------------------------------------------------------------------
    /*
    try {

        // 랜덤 : 0000ff00-0000-cccc-8000-00805f9b34fb
        // 캐릭터1 : 0000ff01-0000-1000-8000-00805f9b34fb    READ WRITE (디바이스 정보)
        // 캐릭터2 : 0000ff02-0000-1000-8000-00805f9b34fb    디바이스 이름
        // 캐릭터3 : 0000ff03-0000-1000-8000-00805f9b34fb    Notification

        String ble_name = "TWOK-BLUETOOTH";
        String ble_uuid = "0000ff01-0000-1000-8000-00805f9b34fb";

        C_Bluetooth_Advertising_Scan_Module.observableBluetoothAdvertising(A_Intro.this, ble_name, ble_uuid)
                .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_Bluetooth_Advertising_Scan_Module.BLE_ADVERTISING_START)){
                            S_Log.w("BLUE_ADVERTISING", "=================== [Start] : [Bluetooth Advertising] ===================");
                        }

                        // ------------------------------------------------------------
                        // TODO [실시간 블루투스 신호 활성 종료 처리]
                        // ------------------------------------------------------------
                        if (String.valueOf(value).equals(C_Bluetooth_Advertising_Scan_Module.BLE_ADVERTISING_STOP)){
                            S_Log.e("BLUE_ADVERTISING", "=================== [Stop] : [Bluetooth 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_FinalData.LOG_BUG_STATE, null, e);
    }
    */
    // -----------------------------------------------------------------------------------------
    static BluetoothAdapter ble_advertising_Adapter;
    static String ble_advertising_LOG = "";
    static BluetoothLeAdvertiser ble_Advertiser = null;
    static Handler advertisingHandler = null;

    public static final String BLE_ADVERTISING_START = "BLE_ADVERTISING_START";
    public static final String BLE_ADVERTISING_STOP = "BLE_ADVERTISING_STOP";
    public static final int BLE_ADVERTISING_TIME_OUT = 60;

    public static Observable<String> observableBluetoothAdvertising(Context mContext, String name, String uuid) {

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

            // [변수 초기화]
            ble_advertising_Adapter = null;
            ble_Advertiser = null;
            advertisingHandler = null;
            ble_advertising_LOG = "";

            try {
                // ===============================================================
                S_Log._D_(ACTIVITY_NAME + " :: observableBluetoothAdvertising :: 블루투스 실시간 신호 활성 시작", new String[]{
                        "NAME :: " + String.valueOf(name),
                        "UUID :: " + String.valueOf(uuid)
                });
                // ===============================================================

                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) {

                            // [퍼미션이 부여되어있지 않은 경우 종료]
                            ble_advertising_LOG = "[ERROR] : Bluetooth Advertising Scan Permission Location Not Granted (블루투스 실시간 신호 활성에 필요한 권한을 확인해주세요. / 위치 권한)";

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

                            // [리턴 반환 실시]
                            if (subscriber != null && subscriber.isDisposed() == false) {
                                subscriber.onError(new Throwable(ble_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) {

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

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

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

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

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

                                // [인풋 데이터 Is Null]
                                ble_advertising_LOG = "[ERROR] : Bluetooth Advertising Name , UUID Is Null (블루투스 실시간 신호 활성에 필요한 NAME , UUID 값을 확인해주세요.)";

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

                                // [리턴 반환 실시]
                                if (subscriber != null && subscriber.isDisposed() == false) {
                                    subscriber.onError(new Throwable(ble_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) {

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

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

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

                            }

                            // [블루투스 getBluetoothLeAdvertiser 획득]
                            ble_advertising_Adapter.setName(name);
                            ble_Advertiser = ble_advertising_Adapter.getBluetoothLeAdvertiser();

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

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


                                // TODO [블루투스 신호 활성 이름 설정] : 휴대폰 블루투스 설정에서 검색 시 표시 되는 Name
                                ble_advertising_Adapter.setName(name);


                                // TODO [블루투스 신호 활성 UUID 데이터 및 옵션 설정]
                                int manufacturerId = 0x004C; // Apple 의 Manufacturer ID
                                byte[] manufacturerData = new byte[]{0x02, 0x15, // iBeacon 규격 (예: iOS 장치에서 인식 가능하도록 설정)
                                        (byte) 0xF7, (byte) 0x82, (byte) 0x6D, (byte) 0xA6, (byte) 0x4F, (byte) 0xA2, (byte) 0x4E, (byte) 0x98, (byte) 0x80,
                                        (byte) 0xD2, (byte) 0x5A, (byte) 0x1B, (byte) 0x60, (byte) 0x9F, (byte) 0xE0}; // UUID

                                ParcelUuid pUuid = new ParcelUuid(UUID.fromString(uuid));

                                AdvertiseData ad_data = new AdvertiseData.Builder()
                                        .setIncludeTxPowerLevel(true) // [Tx Power 포함]
                                        .addServiceUuid(pUuid) // [서비스 UUID 설정 : 서비스 DATA 와 중복 설정 불가능]
                                        //.addServiceData(pUuid, "Data".getBytes(StandardCharsets.UTF_8)) // [서비스 DATA 설정]
                                        .addManufacturerData(manufacturerId, manufacturerData) // TODO [IOS 호환 제조사 설정]
                                        .build();


                                // TODO [블루투스 신호 활성 정보 설정]
                                AdvertiseSettings ad_settings = new AdvertiseSettings.Builder()
                                        .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY) // [낮은 대기 시간, 높은 전력 모드에서 Bluetooth LE 광고를 수행]
                                        .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) // [높은 TX 전력 수준을 사용하여 광고]
                                        .setConnectable(true) // [블루투스 연결 설정]
                                        .setTimeout(BLE_ADVERTISING_TIME_OUT * 1000) // [신호 활성 시간]
                                        .build();


                                // TODO [블루투스 스캔 시 표시 되는 이름 표시 여부 설정]
                                AdvertiseData sc_data = new AdvertiseData.Builder()
                                        .setIncludeDeviceName(true) // [기기 이름 포함]
                                        .build();


                                // TODO [블루투스 신호 활성 콜백 응답 리스너 선언]
                                AdvertiseCallback advertiseCallback = new AdvertiseCallback() {
                                    @Override
                                    public void onStartSuccess(AdvertiseSettings settingsInEffect) {
                                        super.onStartSuccess(settingsInEffect);

                                        // [블루투스 정상 신호 활성 로그]
                                        ble_advertising_LOG = "[Success] : Bluetooth Advertising onStartSuccess";

                                        S_Log._W_(ACTIVITY_NAME + " :: observableBluetoothAdvertising :: 블루투스 실시간 신호 활성 수행", new String[]{
                                                "M_LOG :: " + String.valueOf(ble_advertising_LOG),
                                                "NAME :: " + String.valueOf(name),
                                                "UUID :: " + String.valueOf(uuid)
                                        });

                                        // [리턴 반환 실시]
                                        if (subscriber != null && subscriber.isDisposed() == false) {
                                            subscriber.onNext(BLE_ADVERTISING_START);
                                            //subscriber.onComplete(); // TODO [완료 처리는 지정 된 핸들러 타임 아웃 시간 이후 리턴 반환]
                                        }
                                    }

                                    @Override
                                    public void onStartFailure(int errorCode) {
                                        super.onStartFailure(errorCode);

                                        String errorMessage = String.valueOf(errorCode);
                                        switch (errorCode) {
                                            case AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED:
                                                errorMessage += " / " + "ADVERTISE_FAILED_ALREADY_STARTED";
                                                break;
                                            case AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE:
                                                errorMessage += " / " + "ADVERTISE_FAILED_DATA_TOO_LARGE";
                                                break;
                                            case AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED:
                                                errorMessage += " / " + "ADVERTISE_FAILED_FEATURE_UNSUPPORTED";
                                                break;
                                            case AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR:
                                                errorMessage += " / " + "ADVERTISE_FAILED_INTERNAL_ERROR";
                                                break;
                                            case AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS:
                                                errorMessage += " / " + "ADVERTISE_FAILED_TOO_MANY_ADVERTISERS";
                                                break;
                                        }

                                        // [블루투스가 신호 활성 에러 발생]
                                        ble_advertising_LOG = "[Error] : Bluetooth Advertising onStartFailure (" + String.valueOf(errorMessage) + ")";

                                        S_Log._E_(ACTIVITY_NAME + " :: observableBluetoothAdvertising :: 에러 발생", new String[]{
                                                "M_LOG :: " + String.valueOf(ble_advertising_LOG),
                                                "NAME :: " + String.valueOf(name),
                                                "UUID :: " + String.valueOf(uuid)
                                        });

                                        // [리턴 반환 실시]
                                        if (subscriber != null && subscriber.isDisposed() == false) {
                                            subscriber.onError(new Throwable(ble_advertising_LOG));
                                            subscriber.onComplete(); // TODO [완료 처리는 지정 된 핸들러 타임 아웃 시간 이후 리턴 반환]
                                        }

                                        // [핸들러 종료 처리 수행]
                                        if (advertisingHandler != null) {
                                            try {
                                                advertisingHandler.removeMessages(0);
                                                advertisingHandler.removeCallbacks(null);
                                            } catch (Exception es) {
                                                es.printStackTrace();
                                            }
                                        }
                                    }
                                };


                                // TODO [블루투스 신호 활성 시작] : [콜백 리스너 지정] : [IOS 에서 블루투스 LE 스캔 시 UUID 는 표시 되지 않고 NAME 정보는 표시 됨]
                                ble_Advertiser.startAdvertising(ad_settings, ad_data, sc_data, advertiseCallback);


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

                                        // [블루투스가 신호 활성 종료 로그]
                                        ble_advertising_LOG = "[Stop] : Bluetooth Advertising Stop";

                                        S_Log._E_(ACTIVITY_NAME + " :: observableBluetoothAdvertising :: 블루투스 실시간 신호 활성 종료", new String[]{
                                                "M_LOG :: " + String.valueOf(ble_advertising_LOG),
                                                "NAME :: " + String.valueOf(name),
                                                "UUID :: " + String.valueOf(uuid)
                                        });

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

                                        // [블루투스 신호 활성 종료]
                                        if (ble_Advertiser != null) {
                                            if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.BLUETOOTH_ADVERTISE) != PackageManager.PERMISSION_GRANTED) {
                                                return;
                                            }
                                            ble_Advertiser.stopAdvertising(advertiseCallback);
                                        }
                                    }

                                }, BLE_ADVERTISING_TIME_OUT * 1000);

                            }
                            else {

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

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

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

                            }

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

                        }

                    }
                }, 0);

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

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

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

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

        });
    }


} // TODO [클래스 종료]

 

반응형
Comments