相同的代码,蓝牙模块在 Android 11 上工作正常,但在 Android 11+ 中缺少设备

问题描述 投票:0回答:1

我正在使用 flutter_reactive_ble 和 flutter_blue_plus 对于这两个库,当代码在 android 11 上构建时,蓝牙模块工作得很好。对于 Andorid 11+,它不会显示某些扫描的设备。

清单

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- permissions handling -->

<!-- Bluetooth -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />



<!-- Notifications -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>


<!-- Camera -->
<uses-permission android:name="android.permission.CAMERA"/>
<!-- Location -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

<application
    android:label="melo"
    android:name="${applicationName}"
    android:icon="@mipmap/ic_launcher">
    <activity
        android:name=".MainActivity"
        android:exported="true"
        android:launchMode="singleTop"
        android:theme="@style/LaunchTheme"
        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
        android:hardwareAccelerated="true"
        android:windowSoftInputMode="adjustResize">
        <!-- Specifies an Android theme to apply to this Activity as soon as
             the Android process has started. This theme is visible to the user
             while the Flutter UI initializes. After that, this theme continues
             to determine the Window background behind the Flutter UI. -->
        <meta-data
          android:name="io.flutter.embedding.android.NormalTheme"
          android:resource="@style/NormalTheme"
          />
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>        
    <!-- Don't delete the meta-data below.
         This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
    <meta-data
        android:name="flutterEmbedding"
        android:value="2" />
</application>

扫描代码(在 Android 11 上工作正常,并且缺少 Android 11 及以上的某些设备):

import 'package:animated_custom_dropdown/custom_dropdown.dart';
import 'package:flutter/material.dart';
import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'package:melo/ble/ble_scanner.dart';
import 'package:melo/providers/new_instrument_provider.dart';
import 'package:melo/screens/instrument_enrollment/primary_info.dart';
import 'package:melo/utils/colors.dart';
import 'package:melo/widgets/add_instrument/bluetooth_scan_item.dart';
import 'package:melo/widgets/add_instrument/steps.dart';
import 'package:melo/widgets/others/theme_button.dart';

class SelectInstrumentScreen extends ConsumerStatefulWidget {
  const SelectInstrumentScreen({super.key, required this.collectionName});
  final String collectionName;

  @override
  ConsumerState<SelectInstrumentScreen> createState() =>
      _SelectInstrumentScreenState();
}

class _SelectInstrumentScreenState
    extends ConsumerState<SelectInstrumentScreen> {
  final BleScanner bleScanner = BleScanner(
    ble: FlutterReactiveBle(),
    logMessage: (message) {},
  );

  @override
  void initState() {
    super.initState();
    bleScanner.startScan([]);
  }

  @override
  Widget build(BuildContext context) {
    ref.watch(newInstrumentProvider);
    bool areValuesSelected = ref.read(newInstrumentProvider)['type'] != null &&
        ref.read(newInstrumentProvider)['macAddress'] != null;
    return Scaffold(
      backgroundColor: backgroundColor,
      appBar: AppBar(
        backgroundColor: Colors.white,
        leadingWidth: 0,
        foregroundColor: Colors.transparent,
        toolbarHeight: 70,
        title: Container(
          padding: const EdgeInsets.all(5),
          child: const StepsIndicator(step: 1),
        ),
      ),
      body: Column(
        children: [
          Container(
            padding: const EdgeInsets.symmetric(horizontal: 20),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                const SizedBox(height: 5),
                const Text(
                  'Select Sensor Device',
                  style: TextStyle(
                    fontWeight: FontWeight.bold,
                    fontSize: 15,
                  ),
                ),
                Text(
                  'Select the sensor device that you want to connect from the available devices.',
                  style: TextStyle(
                    color: secondaryColor,
                    fontSize: 13,
                  ),
                ),

                // select instrument from a list of options
                const SizedBox(height: 10),
                const Text(
                  'Choose Instrument',
                  style: TextStyle(
                    fontWeight: FontWeight.bold,
                    fontSize: 14,
                  ),
                ),
                const SizedBox(height: 5),
                CustomDropdown<String>(
                  closedHeaderPadding: const EdgeInsets.only(
                      top: 7, bottom: 8, left: 15, right: 10),
                  decoration: CustomDropdownDecoration(
                    closedBorderRadius: BorderRadius.circular(100),
                    closedBorder: Border.all(
                      color: const Color.fromRGBO(220, 220, 220, 1),
                    ),
                  ),
                  overlayHeight: MediaQuery.of(context).size.height * 0.5,
                  hintText: 'Choose Instrument',
                  items: _listInstruments,
                  initialItem: ref.read(newInstrumentProvider)['type'],
                  onChanged: (value) {
                    ref
                        .read(newInstrumentProvider.notifier)
                        .update('type', value);
                  },
                ),

                // bluetooth scanned devices
                const SizedBox(height: 10),
                const Text(
                  'Available Bluetooth Devices',
                  style: TextStyle(
                    fontWeight: FontWeight.bold,
                    fontSize: 14,
                  ),
                ),
                const SizedBox(height: 5),
                // reactive ble devices
              ],
            ),
          ),
          Expanded(
            child: StreamBuilder<BleScannerState>(
              stream: bleScanner.state,
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  final discoveredDevices = snapshot.data!.discoveredDevices;
                  // sorting: nearest device first
                  discoveredDevices.sort((a, b) => b.rssi.compareTo(a.rssi));
                  return ListView.builder(
                    itemCount: discoveredDevices.length,
                    itemBuilder: (context, index) {
                      final device = discoveredDevices[index];
                      return Material(
                        child: InkWell(
                          onTap: () {
                            ref
                                .read(newInstrumentProvider.notifier)
                                .update('macAddress', device.id);
                            ref.read(newInstrumentProvider.notifier).update(
                                'name',
                                device.name.isNotEmpty
                                    ? device.name
                                    : 'Unknown Name');
                          },
                          child: BluetoothScanItem(device: device),
                        ),
                      );
                    },
                  );
                } else {
                  return const Text('nothing so far');
                }
              },
            ),
          ),
          Container(
            height: 60,
            padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
            child: Row(
              children: [
                Expanded(
                  flex: 2,
                  child: ThemeButton(
                    onTap: () {
                      Navigator.pop(context);
                    },
                    name: 'Back',
                    isSimple: true,
                  ),
                ),
                const SizedBox(width: 10),
                Expanded(
                  flex: 3,
                  child: ThemeButton(
                    onTap: () {
                      if (areValuesSelected) {
                        Navigator.push(context,
                            MaterialPageRoute(builder: (context) {
                          return PrimaryInfoScreen(
                              collectionName: widget.collectionName);
                        }));
                      }
                    },
                    name: 'Next',
                    isDisabled: !areValuesSelected,
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

const List<String> _listInstruments = [
  'Acoustic',
  'Bass',
  'Bowed',
  'Brass',
  'Drum',
  'Electric',
  'Mandolin',
  'Piano',
  'Reed',
  'Other',
];
android bluetooth android-manifest flutter-androidmanifest
1个回答
0
投票

自Android 12发布以来,引入了新的蓝牙权限。根据 documentation,BLUETOOTH_SCAN、BLUETOOTH_CONNECT 和 BLUETOOTH_ADVERTISE 被视为运行时权限。

这意味着,如果您的应用程序面向运行 Android 12 或更高版本的设备,您必须自行处理这些权限并向用户明确请求这些权限。

例如,当您打算扫描BLE设备时,您必须请求BLUETOOTH_SCAN权限。请注意,此权限将作为“附近设备”权限呈现给用户。

未能请求并获得这些权限可能会导致您的 BleScanner 无法检测到任何附近的设备。

请注意,对于低于 Android 12 的设备,您仍然需要请求运行时位置权限。

© www.soinside.com 2019 - 2024. All rights reserved.