Android13-蓝牙-发现,配对,连接-例子

Android 13 蓝牙设备发现与显示列表完整实现

一、权限处理与配置

1. AndroidManifest.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <!-- Android 12+ (API 31+) 需要的权限 -->
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN"
        android:usesPermissionFlags="neverForLocation" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
    
    <!-- Android 6.0+ 需要的位置权限(用于扫描) -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    
    <!-- Android 10+ 后台位置权限(可选) -->
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" 
        tools:ignore="ManifestOrder" />
    
    <!-- Android 11及以下兼容权限 -->
    <uses-permission android:name="android.permission.BLUETOOTH"
        android:maxSdkVersion="30" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
        android:maxSdkVersion="30" />
    
    <!-- 如果是 Android 8.0 以下,还需要这些权限 -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"
        android:maxSdkVersion="28" />
    
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.BluetoothDiscovery">
        
        <activity
            android:name=".DeviceDiscoveryActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        
    </application>
</manifest>

2. 权限请求工具类

java 复制代码
// PermissionUtils.java
public class PermissionUtils {
    
    public static final int REQUEST_CODE_BLUETOOTH_PERMISSIONS = 1001;
    public static final int REQUEST_CODE_LOCATION_PERMISSIONS = 1002;
    
    // 检查并请求蓝牙权限
    public static boolean checkAndRequestBluetoothPermissions(Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            // Android 12+ 需要 BLUETOOTH_SCAN 和 BLUETOOTH_CONNECT
            List<String> permissionsNeeded = new ArrayList<>();
            
            if (ContextCompat.checkSelfPermission(activity, 
                    Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
                permissionsNeeded.add(Manifest.permission.BLUETOOTH_SCAN);
            }
            
            if (ContextCompat.checkSelfPermission(activity, 
                    Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
                permissionsNeeded.add(Manifest.permission.BLUETOOTH_CONNECT);
            }
            
            if (!permissionsNeeded.isEmpty()) {
                ActivityCompat.requestPermissions(activity, 
                    permissionsNeeded.toArray(new String[0]), 
                    REQUEST_CODE_BLUETOOTH_PERMISSIONS);
                return false;
            }
        } else {
            // Android 11及以下需要传统蓝牙权限
            if (ContextCompat.checkSelfPermission(activity, 
                    Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED ||
                ContextCompat.checkSelfPermission(activity, 
                    Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED) {
                
                ActivityCompat.requestPermissions(activity, 
                    new String[] {
                        Manifest.permission.BLUETOOTH,
                        Manifest.permission.BLUETOOTH_ADMIN
                    }, 
                    REQUEST_CODE_BLUETOOTH_PERMISSIONS);
                return false;
            }
        }
        
        return true;
    }
    
    // 检查并请求位置权限
    public static boolean checkAndRequestLocationPermissions(Activity activity) {
        // Android 6.0+ 需要位置权限进行蓝牙扫描
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            List<String> permissionsNeeded = new ArrayList<>();
            
            // 检查精确位置权限
            if (ContextCompat.checkSelfPermission(activity, 
                    Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                permissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION);
            }
            
            // 检查粗略位置权限
            if (ContextCompat.checkSelfPermission(activity, 
                    Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                permissionsNeeded.add(Manifest.permission.ACCESS_COARSE_LOCATION);
            }
            
            // Android 10+ 如果需要在后台扫描,需要后台位置权限
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                if (ContextCompat.checkSelfPermission(activity,
                        Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                    // 注意:后台位置权限需要单独请求
                }
            }
            
            if (!permissionsNeeded.isEmpty()) {
                ActivityCompat.requestPermissions(activity, 
                    permissionsNeeded.toArray(new String[0]), 
                    REQUEST_CODE_LOCATION_PERMISSIONS);
                return false;
            }
        }
        
        return true;
    }
    
    // 检查是否所有必要权限都已授予
    public static boolean hasAllRequiredPermissions(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            return ContextCompat.checkSelfPermission(context, 
                    Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED &&
                   ContextCompat.checkSelfPermission(context, 
                    Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED &&
                   ContextCompat.checkSelfPermission(context, 
                    Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
        } else {
            return ContextCompat.checkSelfPermission(context, 
                    Manifest.permission.BLUETOOTH) == PackageManager.PERMISSION_GRANTED &&
                   ContextCompat.checkSelfPermission(context, 
                    Manifest.permission.BLUETOOTH_ADMIN) == PackageManager.PERMISSION_GRANTED &&
                   ContextCompat.checkSelfPermission(context, 
                    Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
        }
    }
    
    // 解释为什么需要权限
    public static void showPermissionRationale(Activity activity, String permission) {
        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        builder.setTitle("需要权限")
               .setMessage(getPermissionRationaleMessage(permission))
               .setPositiveButton("确定", null)
               .show();
    }
    
    private static String getPermissionRationaleMessage(String permission) {
        switch (permission) {
            case Manifest.permission.BLUETOOTH_SCAN:
                return "需要蓝牙扫描权限来发现附近的蓝牙设备";
            case Manifest.permission.BLUETOOTH_CONNECT:
                return "需要蓝牙连接权限来配对和连接蓝牙设备";
            case Manifest.permission.ACCESS_FINE_LOCATION:
                return "需要位置权限来扫描蓝牙设备,这是Android系统的要求";
            default:
                return "需要此权限来使用蓝牙功能";
        }
    }
}

二、主界面实现

1. DeviceDiscoveryActivity

java 复制代码
// DeviceDiscoveryActivity.java
public class DeviceDiscoveryActivity extends AppCompatActivity {
    private static final String TAG = "DeviceDiscovery";
    
    // UI组件
    private Button btnScan;
    private Button btnStop;
    private Button btnEnableBluetooth;
    private TextView tvStatus;
    private ProgressBar progressBar;
    private RecyclerView recyclerView;
    private TextView tvEmptyView;
    
    // 蓝牙相关
    private BluetoothAdapter bluetoothAdapter;
    private BluetoothLeScanner bleScanner;
    private DeviceListAdapter deviceAdapter;
    private List<BluetoothDevice> deviceList = new ArrayList<>();
    private Map<String, DeviceInfo> deviceInfoMap = new HashMap<>();
    
    // 扫描状态
    private boolean isScanning = false;
    private boolean isClassicScanning = false;
    private long scanStartTime = 0;
    
    // 广播接收器
    private final BroadcastReceiver bluetoothReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            
            if (action == null) return;
            
            switch (action) {
                case BluetoothAdapter.ACTION_STATE_CHANGED:
                    handleBluetoothStateChange(intent);
                    break;
                    
                case BluetoothDevice.ACTION_FOUND:
                    handleDeviceFound(intent);
                    break;
                    
                case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
                    handleDiscoveryStarted();
                    break;
                    
                case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
                    handleDiscoveryFinished();
                    break;
                    
                case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
                    handleBondStateChanged(intent);
                    break;
                    
                case BluetoothDevice.ACTION_ACL_CONNECTED:
                case BluetoothDevice.ACTION_ACL_DISCONNECTED:
                    handleConnectionStateChanged(intent, action);
                    break;
            }
        }
    };
    
    // BLE扫描回调
    private ScanCallback bleScanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);
            handleBleDeviceFound(result);
        }
        
        @Override
        public void onBatchScanResults(List<ScanResult> results) {
            super.onBatchScanResults(results);
            for (ScanResult result : results) {
                handleBleDeviceFound(result);
            }
        }
        
        @Override
        public void onScanFailed(int errorCode) {
            super.onScanFailed(errorCode);
            handleBleScanFailed(errorCode);
        }
    };
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_device_discovery);
        
        initViews();
        initBluetooth();
        setupRecyclerView();
        
        // 检查并请求权限
        if (!checkPermissions()) {
            requestPermissions();
        } else {
            setupBluetooth();
        }
    }
    
    private void initViews() {
        btnScan = findViewById(R.id.btn_scan);
        btnStop = findViewById(R.id.btn_stop);
        btnEnableBluetooth = findViewById(R.id.btn_enable_bluetooth);
        tvStatus = findViewById(R.id.tv_status);
        progressBar = findViewById(R.id.progress_bar);
        recyclerView = findViewById(R.id.recycler_view);
        tvEmptyView = findViewById(R.id.tv_empty);
        
        btnScan.setOnClickListener(v -> startScanning());
        btnStop.setOnClickListener(v -> stopScanning());
        btnEnableBluetooth.setOnClickListener(v -> enableBluetooth());
        
        updateUIState();
    }
    
    private void initBluetooth() {
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (bluetoothAdapter == null) {
            showErrorDialog("设备不支持蓝牙");
            finish();
            return;
        }
        
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            bleScanner = bluetoothAdapter.getBluetoothLeScanner();
        }
    }
    
    private void setupRecyclerView() {
        deviceAdapter = new DeviceListAdapter(deviceList, new DeviceListAdapter.OnDeviceClickListener() {
            @Override
            public void onDeviceClick(BluetoothDevice device) {
                showDeviceOptions(device);
            }
            
            @Override
            public void onPairClick(BluetoothDevice device) {
                startPairing(device);
            }
            
            @Override
            public void onConnectClick(BluetoothDevice device) {
                connectToDevice(device);
            }
        });
        
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(deviceAdapter);
        recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
    }
    
    private void registerBluetoothReceivers() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        filter.addAction(BluetoothDevice.ACTION_FOUND);
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
        filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
        filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
        
        registerReceiver(bluetoothReceiver, filter);
    }
    
    private void unregisterBluetoothReceivers() {
        try {
            unregisterReceiver(bluetoothReceiver);
        } catch (IllegalArgumentException e) {
            // 忽略未注册的异常
        }
    }
    
    private boolean checkPermissions() {
        return PermissionUtils.hasAllRequiredPermissions(this);
    }
    
    private void requestPermissions() {
        PermissionUtils.checkAndRequestBluetoothPermissions(this);
        PermissionUtils.checkAndRequestLocationPermissions(this);
    }
    
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 
                                         @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        
        if (requestCode == PermissionUtils.REQUEST_CODE_BLUETOOTH_PERMISSIONS ||
            requestCode == PermissionUtils.REQUEST_CODE_LOCATION_PERMISSIONS) {
            
            boolean allGranted = true;
            for (int result : grantResults) {
                if (result != PackageManager.PERMISSION_GRANTED) {
                    allGranted = false;
                    break;
                }
            }
            
            if (allGranted) {
                setupBluetooth();
            } else {
                showPermissionDeniedDialog();
            }
        }
    }
    
    private void setupBluetooth() {
        registerBluetoothReceivers();
        loadBondedDevices();
        updateUIState();
    }
    
    private void updateUIState() {
        boolean isBluetoothEnabled = bluetoothAdapter != null && bluetoothAdapter.isEnabled();
        boolean canScan = isBluetoothEnabled && !isScanning;
        
        btnEnableBluetooth.setVisibility(isBluetoothEnabled ? View.GONE : View.VISIBLE);
        btnScan.setEnabled(canScan);
        btnStop.setVisibility(isScanning ? View.VISIBLE : View.GONE);
        progressBar.setVisibility(isScanning ? View.VISIBLE : View.GONE);
        
        if (isScanning) {
            long elapsedSeconds = (System.currentTimeMillis() - scanStartTime) / 1000;
            tvStatus.setText(String.format("扫描中... %ds", elapsedSeconds));
        } else {
            tvStatus.setText(isBluetoothEnabled ? "就绪" : "蓝牙未启用");
        }
        
        tvEmptyView.setVisibility(deviceList.isEmpty() ? View.VISIBLE : View.GONE);
        recyclerView.setVisibility(deviceList.isEmpty() ? View.GONE : View.VISIBLE);
    }
    
    private void loadBondedDevices() {
        if (bluetoothAdapter == null) return;
        
        Set<BluetoothDevice> bondedDevices = bluetoothAdapter.getBondedDevices();
        for (BluetoothDevice device : bondedDevices) {
            addOrUpdateDevice(device, DeviceType.CLASSIC, -1, true);
        }
    }
    
    private void startScanning() {
        if (!checkPermissions()) {
            requestPermissions();
            return;
        }
        
        if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
            Toast.makeText(this, "请先启用蓝牙", Toast.LENGTH_SHORT).show();
            return;
        }
        
        clearDeviceList();
        isScanning = true;
        scanStartTime = System.currentTimeMillis();
        
        // 同时开始经典蓝牙扫描和BLE扫描
        startClassicBluetoothScan();
        startBleScan();
        
        // 设置扫描超时(30秒)
        new Handler(Looper.getMainLooper()).postDelayed(this::stopScanning, 30000);
        
        updateUIState();
        Toast.makeText(this, "开始扫描设备", Toast.LENGTH_SHORT).show();
    }
    
    private void startClassicBluetoothScan() {
        if (bluetoothAdapter.isDiscovering()) {
            bluetoothAdapter.cancelDiscovery();
        }
        
        isClassicScanning = true;
        boolean started = bluetoothAdapter.startDiscovery();
        
        if (!started) {
            Log.e(TAG, "经典蓝牙扫描启动失败");
            isClassicScanning = false;
        }
    }
    
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void startBleScan() {
        if (bleScanner == null) {
            bleScanner = bluetoothAdapter.getBluetoothLeScanner();
            if (bleScanner == null) {
                Log.e(TAG, "BLE扫描器不可用");
                return;
            }
        }
        
        ScanSettings settings = new ScanSettings.Builder()
                .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
                .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
                .setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
                .setNumOfMatches(ScanSettings.MATCH_NUM_MAX_ADVERTISEMENT)
                .setReportDelay(0)
                .build();
        
        List<ScanFilter> filters = new ArrayList<>();
        // 可以添加过滤器,例如只扫描特定服务的设备
        // filters.add(new ScanFilter.Builder()
        //     .setServiceUuid(ParcelUuid.fromString(SERVICE_UUID))
        //     .build());
        
        try {
            bleScanner.startScan(filters, settings, bleScanCallback);
        } catch (SecurityException e) {
            Log.e(TAG, "BLE扫描权限错误", e);
            Toast.makeText(this, "缺少BLE扫描权限", Toast.LENGTH_SHORT).show();
        } catch (IllegalStateException e) {
            Log.e(TAG, "蓝牙未启用", e);
        }
    }
    
    private void stopScanning() {
        if (!isScanning) return;
        
        isScanning = false;
        
        // 停止经典蓝牙扫描
        if (bluetoothAdapter != null && bluetoothAdapter.isDiscovering()) {
            bluetoothAdapter.cancelDiscovery();
        }
        isClassicScanning = false;
        
        // 停止BLE扫描
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && bleScanner != null) {
            try {
                bleScanner.stopScan(bleScanCallback);
            } catch (Exception e) {
                Log.e(TAG, "停止BLE扫描失败", e);
            }
        }
        
        updateUIState();
        Toast.makeText(this, "扫描停止", Toast.LENGTH_SHORT).show();
    }
    
    private void enableBluetooth() {
        if (bluetoothAdapter == null) return;
        
        if (!bluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, 1);
        }
    }
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        
        if (requestCode == 1) {
            if (resultCode == RESULT_OK) {
                Toast.makeText(this, "蓝牙已启用", Toast.LENGTH_SHORT).show();
                setupBluetooth();
            } else {
                Toast.makeText(this, "蓝牙启用被拒绝", Toast.LENGTH_SHORT).show();
            }
        }
    }
    
    private void handleBluetoothStateChange(Intent intent) {
        int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
        
        switch (state) {
            case BluetoothAdapter.STATE_ON:
                Log.d(TAG, "蓝牙已启用");
                loadBondedDevices();
                break;
            case BluetoothAdapter.STATE_OFF:
                Log.d(TAG, "蓝牙已禁用");
                clearDeviceList();
                stopScanning();
                break;
            case BluetoothAdapter.STATE_TURNING_ON:
                Log.d(TAG, "蓝牙正在启用");
                break;
            case BluetoothAdapter.STATE_TURNING_OFF:
                Log.d(TAG, "蓝牙正在禁用");
                break;
        }
        
        updateUIState();
    }
    
    private void handleDeviceFound(Intent intent) {
        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
        if (device == null) return;
        
        // 获取RSSI信号强度
        short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
        
        // 获取设备类别(如果可用)
        BluetoothClass bluetoothClass = intent.getParcelableExtra(BluetoothDevice.EXTRA_CLASS);
        
        addOrUpdateDevice(device, DeviceType.CLASSIC, rssi, false);
    }
    
    private void handleBleDeviceFound(ScanResult result) {
        BluetoothDevice device = result.getDevice();
        if (device == null) return;
        
        int rssi = result.getRssi();
        ScanRecord scanRecord = result.getScanRecord();
        String deviceName = device.getName();
        
        // 从广播数据中获取更多信息
        List<ParcelUuid> serviceUuids = null;
        String manufacturerData = null;
        
        if (scanRecord != null) {
            serviceUuids = scanRecord.getServiceUuids();
            Map<Integer, byte[]> manufacturerDataMap = scanRecord.getManufacturerSpecificData();
            if (manufacturerDataMap != null && !manufacturerDataMap.isEmpty()) {
                for (Map.Entry<Integer, byte[]> entry : manufacturerDataMap.entrySet()) {
                    manufacturerData = bytesToHex(entry.getValue());
                    break;
                }
            }
        }
        
        DeviceInfo info = new DeviceInfo();
        info.device = device;
        info.deviceType = DeviceType.BLE;
        info.rssi = rssi;
        info.lastSeen = System.currentTimeMillis();
        info.serviceUuids = serviceUuids;
        info.manufacturerData = manufacturerData;
        
        deviceInfoMap.put(device.getAddress(), info);
        addOrUpdateDevice(device, DeviceType.BLE, rssi, false);
    }
    
    private void handleDiscoveryStarted() {
        Log.d(TAG, "经典蓝牙扫描开始");
    }
    
    private void handleDiscoveryFinished() {
        Log.d(TAG, "经典蓝牙扫描结束");
        isClassicScanning = false;
        
        // 如果BLE扫描也结束了,则完全停止扫描
        if (!isClassicScanning && isScanning) {
            // BLE扫描需要手动停止
        }
    }
    
    private void handleBleScanFailed(int errorCode) {
        Log.e(TAG, "BLE扫描失败,错误码: " + errorCode);
        
        String errorMessage;
        switch (errorCode) {
            case ScanCallback.SCAN_FAILED_ALREADY_STARTED:
                errorMessage = "扫描已在进行中";
                break;
            case ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED:
                errorMessage = "应用注册失败";
                break;
            case ScanCallback.SCAN_FAILED_INTERNAL_ERROR:
                errorMessage = "内部错误";
                break;
            case ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED:
                errorMessage = "不支持此功能";
                break;
            case ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES:
                errorMessage = "硬件资源不足";
                break;
            default:
                errorMessage = "未知错误";
        }
        
        runOnUiThread(() -> Toast.makeText(this, "BLE扫描失败: " + errorMessage, 
            Toast.LENGTH_SHORT).show());
    }
    
    private void handleBondStateChanged(Intent intent) {
        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
        int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 
                                          BluetoothDevice.ERROR);
        
        if (device != null) {
            // 更新设备列表中的绑定状态
            updateDeviceBondState(device, bondState);
        }
    }
    
    private void handleConnectionStateChanged(Intent intent, String action) {
        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
        if (device != null) {
            boolean isConnected = action.equals(BluetoothDevice.ACTION_ACL_CONNECTED);
            updateDeviceConnectionState(device, isConnected);
        }
    }
    
    private void addOrUpdateDevice(BluetoothDevice device, DeviceType type, 
                                  int rssi, boolean isBonded) {
        String address = device.getAddress();
        
        // 检查是否已存在
        boolean exists = false;
        for (BluetoothDevice existing : deviceList) {
            if (existing.getAddress().equals(address)) {
                exists = true;
                break;
            }
        }
        
        if (!exists) {
            deviceList.add(device);
        }
        
        // 更新设备信息
        DeviceInfo info = deviceInfoMap.get(address);
        if (info == null) {
            info = new DeviceInfo();
        }
        
        info.device = device;
        info.deviceType = type;
        info.rssi = rssi;
        info.lastSeen = System.currentTimeMillis();
        info.isBonded = isBonded || device.getBondState() == BluetoothDevice.BOND_BONDED;
        
        deviceInfoMap.put(address, info);
        
        // 按信号强度排序
        sortDeviceList();
        
        runOnUiThread(() -> {
            deviceAdapter.notifyDataSetChanged();
            updateUIState();
        });
    }
    
    private void sortDeviceList() {
        Collections.sort(deviceList, (device1, device2) -> {
            DeviceInfo info1 = deviceInfoMap.get(device1.getAddress());
            DeviceInfo info2 = deviceInfoMap.get(device2.getAddress());
            
            if (info1 == null || info2 == null) return 0;
            
            // 首先按绑定状态排序(已绑定的在前)
            if (info1.isBonded != info2.isBonded) {
                return info1.isBonded ? -1 : 1;
            }
            
            // 然后按信号强度排序(信号强的在前)
            if (info1.rssi != info2.rssi) {
                return Integer.compare(info2.rssi, info1.rssi);
            }
            
            // 最后按设备名称排序
            String name1 = device1.getName();
            String name2 = device2.getName();
            if (name1 == null) name1 = "";
            if (name2 == null) name2 = "";
            
            return name1.compareToIgnoreCase(name2);
        });
    }
    
    private void clearDeviceList() {
        // 不清除已绑定的设备
        List<BluetoothDevice> bondedDevices = new ArrayList<>();
        for (BluetoothDevice device : deviceList) {
            if (device.getBondState() == BluetoothDevice.BOND_BONDED) {
                bondedDevices.add(device);
            }
        }
        
        deviceList.clear();
        deviceList.addAll(bondedDevices);
        deviceAdapter.notifyDataSetChanged();
        updateUIState();
    }
    
    private void updateDeviceBondState(BluetoothDevice device, int bondState) {
        DeviceInfo info = deviceInfoMap.get(device.getAddress());
        if (info != null) {
            info.isBonded = bondState == BluetoothDevice.BOND_BONDED;
        }
        sortDeviceList();
        runOnUiThread(() -> deviceAdapter.notifyDataSetChanged());
    }
    
    private void updateDeviceConnectionState(BluetoothDevice device, boolean isConnected) {
        DeviceInfo info = deviceInfoMap.get(device.getAddress());
        if (info != null) {
            info.isConnected = isConnected;
        }
        runOnUiThread(() -> deviceAdapter.notifyDataSetChanged());
    }
    
    private void showDeviceOptions(BluetoothDevice device) {
        DeviceInfo info = deviceInfoMap.get(device.getAddress());
        if (info == null) return;
        
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("设备选项")
               .setMessage(getDeviceInfoString(device, info));
        
        List<CharSequence> options = new ArrayList<>();
        List<Runnable> actions = new ArrayList<>();
        
        // 配对/取消配对选项
        if (info.isBonded) {
            options.add("取消配对");
            actions.add(() -> unpairDevice(device));
        } else {
            options.add("配对");
            actions.add(() -> startPairing(device));
        }
        
        // 连接选项
        if (!info.isConnected && info.isBonded) {
            options.add("连接");
            actions.add(() -> connectToDevice(device));
        } else if (info.isConnected) {
            options.add("断开连接");
            actions.add(() -> disconnectDevice(device));
        }
        
        // 查看详细信息
        options.add("详细信息");
        actions.add(() -> showDeviceDetails(device, info));
        
        // 忘记设备
        options.add("从列表中移除");
        actions.add(() -> removeDevice(device));
        
        builder.setItems(options.toArray(new CharSequence[0]), 
            (dialog, which) -> actions.get(which).run());
        
        builder.setNegativeButton("取消", null);
        builder.show();
    }
    
    private String getDeviceInfoString(BluetoothDevice device, DeviceInfo info) {
        StringBuilder sb = new StringBuilder();
        sb.append("名称: ").append(device.getName() != null ? device.getName() : "未知")
          .append("\n地址: ").append(device.getAddress())
          .append("\n类型: ").append(info.deviceType == DeviceType.BLE ? "BLE" : "经典蓝牙")
          .append("\n绑定状态: ").append(info.isBonded ? "已绑定" : "未绑定")
          .append("\n连接状态: ").append(info.isConnected ? "已连接" : "未连接");
        
        if (info.rssi != -1) {
            sb.append("\n信号强度: ").append(info.rssi).append(" dBm");
        }
        
        if (info.lastSeen > 0) {
            long secondsAgo = (System.currentTimeMillis() - info.lastSeen) / 1000;
            sb.append("\n发现时间: ").append(secondsAgo).append("秒前");
        }
        
        return sb.toString();
    }
    
    private void startPairing(BluetoothDevice device) {
        try {
            boolean result = device.createBond();
            if (result) {
                Toast.makeText(this, "正在配对...", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "配对请求失败", Toast.LENGTH_SHORT).show();
            }
        } catch (SecurityException e) {
            Log.e(TAG, "配对权限错误", e);
            Toast.makeText(this, "缺少蓝牙连接权限", Toast.LENGTH_SHORT).show();
        }
    }
    
    private void connectToDevice(BluetoothDevice device) {
        // 这里需要根据设备类型实现具体的连接逻辑
        Toast.makeText(this, "开始连接设备", Toast.LENGTH_SHORT).show();
        // 实际连接逻辑需要根据具体设备类型(A2DP, HFP, GATT等)实现
    }
    
    private void showDeviceDetails(BluetoothDevice device, DeviceInfo info) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("设备详细信息")
               .setMessage(getDetailedDeviceInfo(device, info))
               .setPositiveButton("确定", null)
               .show();
    }
    
    private String getDetailedDeviceInfo(BluetoothDevice device, DeviceInfo info) {
        StringBuilder sb = new StringBuilder();
        sb.append(getDeviceInfoString(device, info));
        
        try {
            // 获取设备类型
            int deviceType = device.getType();
            sb.append("\n设备类型: ").append(getDeviceTypeString(deviceType));
            
            // 获取蓝牙类别
            BluetoothClass bluetoothClass = device.getBluetoothClass();
            if (bluetoothClass != null) {
                sb.append("\n设备类别: ").append(bluetoothClass.getDeviceClass());
                sb.append("\n主要类别: ").append(bluetoothClass.getMajorDeviceClass());
                
                // 获取服务类别
                int services = bluetoothClass.getServices();
                if (services > 0) {
                    sb.append("\n支持的服务: ");
                    List<String> serviceList = new ArrayList<>();
                    if ((services & BluetoothClass.Service.AUDIO) != 0) 
                        serviceList.add("音频");
                    if ((services & BluetoothClass.Service.CAPTURE) != 0) 
                        serviceList.add("捕获");
                    if ((services & BluetoothClass.Service.INFORMATION) != 0) 
                        serviceList.add("信息");
                    if ((services & BluetoothClass.Service.LIMITED_DISCOVERABILITY) != 0) 
                        serviceList.add("有限发现");
                    if ((services & BluetoothClass.Service.NETWORKING) != 0) 
                        serviceList.add("网络");
                    if ((services & BluetoothClass.Service.OBJECT_TRANSFER) != 0) 
                        serviceList.add("对象传输");
                    if ((services & BluetoothClass.Service.POSITIONING) != 0) 
                        serviceList.add("定位");
                    if ((services & BluetoothClass.Service.RENDER) != 0) 
                        serviceList.add("渲染");
                    if ((services & BluetoothClass.Service.TELEPHONY) != 0) 
                        serviceList.add("电话");
                    
                    sb.append(String.join(", ", serviceList));
                }
            }
            
        } catch (Exception e) {
            Log.e(TAG, "获取设备详细信息失败", e);
        }
        
        return sb.toString();
    }
    
    private String getDeviceTypeString(int deviceType) {
        switch (deviceType) {
            case BluetoothDevice.DEVICE_TYPE_CLASSIC:
                return "经典蓝牙";
            case BluetoothDevice.DEVICE_TYPE_LE:
                return "低功耗蓝牙(BLE)";
            case BluetoothDevice.DEVICE_TYPE_DUAL:
                return "双模蓝牙";
            default:
                return "未知";
        }
    }
    
    private void unpairDevice(BluetoothDevice device) {
        try {
            Method method = device.getClass().getMethod("removeBond");
            boolean result = (boolean) method.invoke(device);
            
            if (result) {
                Toast.makeText(this, "正在取消配对", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "取消配对失败", Toast.LENGTH_SHORT).show();
            }
        } catch (Exception e) {
            Log.e(TAG, "取消配对失败", e);
            Toast.makeText(this, "取消配对失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }
    
    private void disconnectDevice(BluetoothDevice device) {
        Toast.makeText(this, "断开连接", Toast.LENGTH_SHORT).show();
        // 实际断开连接逻辑需要根据具体连接类型实现
    }
    
    private void removeDevice(BluetoothDevice device) {
        deviceList.remove(device);
        deviceInfoMap.remove(device.getAddress());
        deviceAdapter.notifyDataSetChanged();
        updateUIState();
    }
    
    private void showErrorDialog(String message) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("错误")
               .setMessage(message)
               .setPositiveButton("确定", (dialog, which) -> finish())
               .setCancelable(false)
               .show();
    }
    
    private void showPermissionDeniedDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("权限被拒绝")
               .setMessage("需要蓝牙和位置权限才能扫描设备。\n\n请在设置中授予权限。")
               .setPositiveButton("去设置", (dialog, which) -> openAppSettings())
               .setNegativeButton("退出", (dialog, which) -> finish())
               .setCancelable(false)
               .show();
    }
    
    private void openAppSettings() {
        Intent intent = new Intent(Settings.ACTION_APPLICATION_D
相关推荐
阿巴斯甜17 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker17 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952718 小时前
Andorid Google 登录接入文档
android
黄林晴19 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android