Add BLE permissions handling with graceful fallback
- Create permissions helper module with comprehensive error handling - Update BLEManager to use new permission system - Add permission state tracking in BLEContext - Improve add-sensor screen with permission error banner - Add "Open Settings" button for permission issues - Handle Android 12+ and older permission models - Provide user-friendly error messages for all states 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
d499d9d62a
commit
5d40da0409
@ -7,12 +7,12 @@ import {
|
||||
TouchableOpacity,
|
||||
Alert,
|
||||
ActivityIndicator,
|
||||
Linking,
|
||||
} from 'react-native';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { router, useLocalSearchParams, useFocusEffect } from 'expo-router';
|
||||
import { useBLE } from '@/contexts/BLEContext';
|
||||
import { useBeneficiary } from '@/contexts/BeneficiaryContext';
|
||||
import {
|
||||
AppColors,
|
||||
BorderRadius,
|
||||
@ -24,14 +24,16 @@ import {
|
||||
|
||||
export default function AddSensorScreen() {
|
||||
const { id } = useLocalSearchParams<{ id: string }>();
|
||||
const { currentBeneficiary } = useBeneficiary();
|
||||
const {
|
||||
foundDevices,
|
||||
isScanning,
|
||||
connectedDevices,
|
||||
isBLEAvailable,
|
||||
error,
|
||||
permissionError,
|
||||
scanDevices,
|
||||
stopScan,
|
||||
clearError,
|
||||
} = useBLE();
|
||||
|
||||
const [selectedDevices, setSelectedDevices] = useState<Set<string>>(new Set());
|
||||
@ -80,12 +82,25 @@ export default function AddSensorScreen() {
|
||||
|
||||
const handleScan = async () => {
|
||||
try {
|
||||
// Clear any previous errors
|
||||
clearError();
|
||||
|
||||
// Perform scan
|
||||
await scanDevices();
|
||||
} catch (error: any) {
|
||||
Alert.alert('Scan Failed', error.message || 'Failed to scan for sensors. Please try again.');
|
||||
// Error is already set in BLE context, but show alert for critical issues
|
||||
if (!permissionError) {
|
||||
// Non-permission errors - show generic alert
|
||||
Alert.alert('Scan Failed', error.message || 'Failed to scan for sensors. Please try again.');
|
||||
}
|
||||
// Permission errors are already shown with proper dialogs by checkBLEReadiness
|
||||
}
|
||||
};
|
||||
|
||||
const handleOpenSettings = () => {
|
||||
Linking.openSettings();
|
||||
};
|
||||
|
||||
const handleAddSelected = () => {
|
||||
if (selectedCount === 0) {
|
||||
Alert.alert('No Sensors Selected', 'Please select at least one sensor to add.');
|
||||
@ -143,6 +158,20 @@ export default function AddSensorScreen() {
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* Permission Error Banner */}
|
||||
{permissionError && error && (
|
||||
<View style={styles.permissionError}>
|
||||
<View style={styles.permissionErrorContent}>
|
||||
<Ionicons name="warning" size={20} color={AppColors.error} />
|
||||
<Text style={styles.permissionErrorText}>{error}</Text>
|
||||
</View>
|
||||
<TouchableOpacity style={styles.settingsButton} onPress={handleOpenSettings}>
|
||||
<Text style={styles.settingsButtonText}>Open Settings</Text>
|
||||
<Ionicons name="settings-outline" size={16} color={AppColors.error} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<ScrollView style={styles.content} contentContainerStyle={styles.scrollContent}>
|
||||
{/* Instructions */}
|
||||
<View style={styles.instructionsCard}>
|
||||
@ -157,7 +186,7 @@ export default function AddSensorScreen() {
|
||||
<View style={styles.stepNumber}>
|
||||
<Text style={styles.stepNumberText}>2</Text>
|
||||
</View>
|
||||
<Text style={styles.stepText}>Tap "Scan for Sensors" to search for available devices</Text>
|
||||
<Text style={styles.stepText}>Tap “Scan for Sensors” to search for available devices</Text>
|
||||
</View>
|
||||
<View style={styles.step}>
|
||||
<View style={styles.stepNumber}>
|
||||
@ -312,7 +341,7 @@ export default function AddSensorScreen() {
|
||||
<Text style={styles.helpTitle}>Troubleshooting</Text>
|
||||
</View>
|
||||
<Text style={styles.helpText}>
|
||||
• Sensor not showing up? Make sure it's powered on and the LED is blinking{'\n'}
|
||||
• Sensor not showing up? Make sure it's powered on and the LED is blinking{'\n'}
|
||||
• Weak signal? Move closer to the sensor{'\n'}
|
||||
• Connection fails? Try restarting the sensor{'\n'}
|
||||
• Still having issues? Contact support for assistance
|
||||
@ -364,6 +393,42 @@ const styles = StyleSheet.create({
|
||||
fontWeight: FontWeights.medium,
|
||||
flex: 1,
|
||||
},
|
||||
permissionError: {
|
||||
backgroundColor: AppColors.errorLight,
|
||||
paddingVertical: Spacing.sm,
|
||||
paddingHorizontal: Spacing.md,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: AppColors.error,
|
||||
gap: Spacing.sm,
|
||||
},
|
||||
permissionErrorContent: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-start',
|
||||
gap: Spacing.xs,
|
||||
},
|
||||
permissionErrorText: {
|
||||
fontSize: FontSizes.sm,
|
||||
color: AppColors.error,
|
||||
fontWeight: FontWeights.medium,
|
||||
flex: 1,
|
||||
lineHeight: 20,
|
||||
},
|
||||
settingsButton: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: AppColors.white,
|
||||
paddingVertical: Spacing.xs,
|
||||
paddingHorizontal: Spacing.md,
|
||||
borderRadius: BorderRadius.sm,
|
||||
gap: Spacing.xs,
|
||||
alignSelf: 'flex-start',
|
||||
},
|
||||
settingsButtonText: {
|
||||
fontSize: FontSizes.sm,
|
||||
fontWeight: FontWeights.semibold,
|
||||
color: AppColors.error,
|
||||
},
|
||||
content: {
|
||||
flex: 1,
|
||||
},
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
// BLE Context - Global state for Bluetooth management
|
||||
|
||||
import React, { createContext, useContext, useState, useCallback, useEffect, ReactNode } from 'react';
|
||||
import { bleManager, WPDevice, WiFiNetwork, WiFiStatus, isBLEAvailable } from '@/services/ble';
|
||||
import { bleManager, WPDevice, WiFiNetwork, WiFiStatus, isBLEAvailable, checkBLEReadiness } from '@/services/ble';
|
||||
import { setOnLogoutBLECleanupCallback } from '@/services/api';
|
||||
import { BleManager } from 'react-native-ble-plx';
|
||||
|
||||
interface BLEContextType {
|
||||
// State
|
||||
@ -11,6 +12,7 @@ interface BLEContextType {
|
||||
connectedDevices: Set<string>;
|
||||
isBLEAvailable: boolean;
|
||||
error: string | null;
|
||||
permissionError: boolean; // true if error is related to permissions
|
||||
|
||||
// Actions
|
||||
scanDevices: () => Promise<void>;
|
||||
@ -23,6 +25,7 @@ interface BLEContextType {
|
||||
rebootDevice: (deviceId: string) => Promise<void>;
|
||||
cleanupBLE: () => Promise<void>;
|
||||
clearError: () => void;
|
||||
checkPermissions: () => Promise<boolean>; // Manual permission check with UI prompts
|
||||
}
|
||||
|
||||
const BLEContext = createContext<BLEContextType | undefined>(undefined);
|
||||
@ -32,17 +35,51 @@ export function BLEProvider({ children }: { children: ReactNode }) {
|
||||
const [isScanning, setIsScanning] = useState(false);
|
||||
const [connectedDevices, setConnectedDevices] = useState<Set<string>>(new Set());
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [permissionError, setPermissionError] = useState(false);
|
||||
|
||||
// Lazy BleManager instance for permission checks
|
||||
const [bleManagerInstance] = useState(() => new BleManager());
|
||||
|
||||
const isPermissionError = (errorMessage: string): boolean => {
|
||||
const permissionKeywords = [
|
||||
'permission',
|
||||
'unauthorized',
|
||||
'not granted',
|
||||
'denied',
|
||||
'bluetooth is disabled',
|
||||
'enable it in settings',
|
||||
];
|
||||
const lowerMessage = errorMessage.toLowerCase();
|
||||
return permissionKeywords.some(keyword => lowerMessage.includes(keyword));
|
||||
};
|
||||
|
||||
const checkPermissions = useCallback(async (): Promise<boolean> => {
|
||||
try {
|
||||
setError(null);
|
||||
setPermissionError(false);
|
||||
const ready = await checkBLEReadiness(bleManagerInstance);
|
||||
return ready;
|
||||
} catch {
|
||||
const errorMsg = 'Failed to check Bluetooth permissions';
|
||||
setError(errorMsg);
|
||||
setPermissionError(true);
|
||||
return false;
|
||||
}
|
||||
}, [bleManagerInstance]);
|
||||
|
||||
const scanDevices = useCallback(async () => {
|
||||
try {
|
||||
setError(null);
|
||||
setPermissionError(false);
|
||||
setIsScanning(true);
|
||||
const devices = await bleManager.scanDevices();
|
||||
// Sort by RSSI (strongest first)
|
||||
const sorted = devices.sort((a, b) => b.rssi - a.rssi);
|
||||
setFoundDevices(sorted);
|
||||
} catch (err: any) {
|
||||
setError(err.message || 'Failed to scan for devices');
|
||||
const errorMsg = err.message || 'Failed to scan for devices';
|
||||
setError(errorMsg);
|
||||
setPermissionError(isPermissionError(errorMsg));
|
||||
throw err;
|
||||
} finally {
|
||||
setIsScanning(false);
|
||||
@ -57,16 +94,21 @@ export function BLEProvider({ children }: { children: ReactNode }) {
|
||||
const connectDevice = useCallback(async (deviceId: string): Promise<boolean> => {
|
||||
try {
|
||||
setError(null);
|
||||
setPermissionError(false);
|
||||
const success = await bleManager.connectDevice(deviceId);
|
||||
if (success) {
|
||||
setConnectedDevices(prev => new Set(prev).add(deviceId));
|
||||
} else {
|
||||
// Connection failed but no exception - set user-friendly error
|
||||
setError('Could not connect to sensor. Please move closer and try again.');
|
||||
const errorMsg = 'Could not connect to sensor. Please move closer and try again.';
|
||||
setError(errorMsg);
|
||||
setPermissionError(false);
|
||||
}
|
||||
return success;
|
||||
} catch (err: any) {
|
||||
setError(err.message || 'Failed to connect to device');
|
||||
const errorMsg = err.message || 'Failed to connect to device';
|
||||
setError(errorMsg);
|
||||
setPermissionError(isPermissionError(errorMsg));
|
||||
return false;
|
||||
}
|
||||
}, []);
|
||||
@ -139,6 +181,7 @@ export function BLEProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
const clearError = useCallback(() => {
|
||||
setError(null);
|
||||
setPermissionError(false);
|
||||
}, []);
|
||||
|
||||
const cleanupBLE = useCallback(async () => {
|
||||
@ -157,7 +200,7 @@ export function BLEProvider({ children }: { children: ReactNode }) {
|
||||
setConnectedDevices(new Set());
|
||||
setError(null);
|
||||
|
||||
} catch (err: any) {
|
||||
} catch {
|
||||
// Don't throw - we want to allow logout to proceed even if BLE cleanup fails
|
||||
}
|
||||
}, [isScanning, stopScan]);
|
||||
@ -177,6 +220,7 @@ export function BLEProvider({ children }: { children: ReactNode }) {
|
||||
connectedDevices,
|
||||
isBLEAvailable,
|
||||
error,
|
||||
permissionError,
|
||||
scanDevices,
|
||||
stopScan,
|
||||
connectDevice,
|
||||
@ -187,6 +231,7 @@ export function BLEProvider({ children }: { children: ReactNode }) {
|
||||
rebootDevice,
|
||||
cleanupBLE,
|
||||
clearError,
|
||||
checkPermissions,
|
||||
};
|
||||
|
||||
return <BLEContext.Provider value={value}>{children}</BLEContext.Provider>;
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
// Real BLE Manager для физических устройств
|
||||
|
||||
import { BleManager, Device, State } from 'react-native-ble-plx';
|
||||
import { PermissionsAndroid, Platform } from 'react-native';
|
||||
import { Platform } from 'react-native';
|
||||
import { IBLEManager, WPDevice, WiFiNetwork, WiFiStatus, BLE_CONFIG, BLE_COMMANDS } from './types';
|
||||
import { requestBLEPermissions, checkBluetoothEnabled } from './permissions';
|
||||
import base64 from 'react-native-base64';
|
||||
|
||||
export class RealBLEManager implements IBLEManager {
|
||||
@ -22,52 +23,17 @@ export class RealBLEManager implements IBLEManager {
|
||||
// Don't initialize BleManager here - use lazy initialization
|
||||
}
|
||||
|
||||
// Check and request permissions
|
||||
private async requestPermissions(): Promise<boolean> {
|
||||
if (Platform.OS === 'ios') {
|
||||
// iOS handles permissions automatically via Info.plist
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
if (Platform.Version >= 31) {
|
||||
// Android 12+
|
||||
const granted = await PermissionsAndroid.requestMultiple([
|
||||
PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN!,
|
||||
PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT!,
|
||||
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION!,
|
||||
]);
|
||||
|
||||
return Object.values(granted).every(
|
||||
status => status === PermissionsAndroid.RESULTS.GRANTED
|
||||
);
|
||||
} else {
|
||||
// Android < 12
|
||||
const granted = await PermissionsAndroid.request(
|
||||
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION!
|
||||
);
|
||||
return granted === PermissionsAndroid.RESULTS.GRANTED;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if Bluetooth is enabled
|
||||
private async isBluetoothEnabled(): Promise<boolean> {
|
||||
const state = await this.manager.state();
|
||||
return state === State.PoweredOn;
|
||||
}
|
||||
|
||||
async scanDevices(): Promise<WPDevice[]> {
|
||||
const hasPermission = await this.requestPermissions();
|
||||
if (!hasPermission) {
|
||||
throw new Error('Bluetooth permissions not granted');
|
||||
// Check permissions with graceful fallback
|
||||
const permissionStatus = await requestBLEPermissions();
|
||||
if (!permissionStatus.granted) {
|
||||
throw new Error(permissionStatus.error || 'Bluetooth permissions not granted');
|
||||
}
|
||||
|
||||
const isEnabled = await this.isBluetoothEnabled();
|
||||
if (!isEnabled) {
|
||||
throw new Error('Bluetooth is disabled. Please enable it in settings.');
|
||||
// Check Bluetooth state
|
||||
const bluetoothStatus = await checkBluetoothEnabled(this.manager);
|
||||
if (!bluetoothStatus.enabled) {
|
||||
throw new Error(bluetoothStatus.error || 'Bluetooth is disabled. Please enable it in settings.');
|
||||
}
|
||||
|
||||
const foundDevices = new Map<string, WPDevice>();
|
||||
@ -123,15 +89,15 @@ export class RealBLEManager implements IBLEManager {
|
||||
async connectDevice(deviceId: string): Promise<boolean> {
|
||||
try {
|
||||
// Step 0: Check permissions (required for Android 12+)
|
||||
const hasPermission = await this.requestPermissions();
|
||||
if (!hasPermission) {
|
||||
throw new Error('Bluetooth permissions not granted');
|
||||
const permissionStatus = await requestBLEPermissions();
|
||||
if (!permissionStatus.granted) {
|
||||
throw new Error(permissionStatus.error || 'Bluetooth permissions not granted');
|
||||
}
|
||||
|
||||
// Step 0.5: Check Bluetooth is enabled
|
||||
const isEnabled = await this.isBluetoothEnabled();
|
||||
if (!isEnabled) {
|
||||
throw new Error('Bluetooth is disabled. Please enable it in settings.');
|
||||
const bluetoothStatus = await checkBluetoothEnabled(this.manager);
|
||||
if (!bluetoothStatus.enabled) {
|
||||
throw new Error(bluetoothStatus.error || 'Bluetooth is disabled. Please enable it in settings.');
|
||||
}
|
||||
|
||||
// Check if already connected
|
||||
|
||||
@ -40,3 +40,6 @@ export const bleManager: IBLEManager = {
|
||||
|
||||
// Re-export types
|
||||
export * from './types';
|
||||
|
||||
// Re-export permission utilities
|
||||
export * from './permissions';
|
||||
|
||||
249
services/ble/permissions.ts
Normal file
249
services/ble/permissions.ts
Normal file
@ -0,0 +1,249 @@
|
||||
// BLE Permissions Helper
|
||||
// Handles Bluetooth permission requests with graceful fallback
|
||||
|
||||
import { PermissionsAndroid, Platform, Linking, Alert } from 'react-native';
|
||||
import { BleManager, State } from 'react-native-ble-plx';
|
||||
|
||||
export interface PermissionStatus {
|
||||
granted: boolean;
|
||||
canRequest: boolean; // false if user previously denied with "Don't ask again"
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface BluetoothStatus {
|
||||
enabled: boolean;
|
||||
canEnable: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check and request BLE permissions based on platform
|
||||
* Returns permission status with graceful fallback info
|
||||
*/
|
||||
export async function requestBLEPermissions(): Promise<PermissionStatus> {
|
||||
if (Platform.OS === 'ios') {
|
||||
// iOS handles permissions automatically via Info.plist
|
||||
// BLE permission dialog shows on first BLE operation
|
||||
return { granted: true, canRequest: true };
|
||||
}
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
try {
|
||||
if (Platform.Version >= 31) {
|
||||
// Android 12+ requires BLUETOOTH_SCAN and BLUETOOTH_CONNECT
|
||||
const results = await PermissionsAndroid.requestMultiple([
|
||||
PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN!,
|
||||
PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT!,
|
||||
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION!,
|
||||
]);
|
||||
|
||||
const allGranted = Object.values(results).every(
|
||||
status => status === PermissionsAndroid.RESULTS.GRANTED
|
||||
);
|
||||
|
||||
const anyNeverAskAgain = Object.values(results).some(
|
||||
status => status === PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN
|
||||
);
|
||||
|
||||
if (allGranted) {
|
||||
return { granted: true, canRequest: true };
|
||||
}
|
||||
|
||||
if (anyNeverAskAgain) {
|
||||
return {
|
||||
granted: false,
|
||||
canRequest: false,
|
||||
error: 'Bluetooth permissions were previously denied. Please enable them in Settings.',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
granted: false,
|
||||
canRequest: true,
|
||||
error: 'Bluetooth permissions are required to scan for sensors.',
|
||||
};
|
||||
} else {
|
||||
// Android < 12 requires ACCESS_FINE_LOCATION
|
||||
const result = await PermissionsAndroid.request(
|
||||
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION!
|
||||
);
|
||||
|
||||
if (result === PermissionsAndroid.RESULTS.GRANTED) {
|
||||
return { granted: true, canRequest: true };
|
||||
}
|
||||
|
||||
if (result === PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN) {
|
||||
return {
|
||||
granted: false,
|
||||
canRequest: false,
|
||||
error: 'Location permission was previously denied. Please enable it in Settings.',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
granted: false,
|
||||
canRequest: true,
|
||||
error: 'Location permission is required to scan for Bluetooth devices.',
|
||||
};
|
||||
}
|
||||
} catch (error: any) {
|
||||
return {
|
||||
granted: false,
|
||||
canRequest: false,
|
||||
error: `Failed to request permissions: ${error.message}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown platform
|
||||
return {
|
||||
granted: false,
|
||||
canRequest: false,
|
||||
error: 'Bluetooth permissions not supported on this platform',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check Bluetooth state (enabled/disabled)
|
||||
* Returns status with helpful error messages
|
||||
*/
|
||||
export async function checkBluetoothEnabled(manager: BleManager): Promise<BluetoothStatus> {
|
||||
try {
|
||||
const state = await manager.state();
|
||||
|
||||
switch (state) {
|
||||
case State.PoweredOn:
|
||||
return { enabled: true, canEnable: true };
|
||||
|
||||
case State.PoweredOff:
|
||||
return {
|
||||
enabled: false,
|
||||
canEnable: true,
|
||||
error: 'Bluetooth is turned off. Please enable it in your device settings.',
|
||||
};
|
||||
|
||||
case State.Unauthorized:
|
||||
return {
|
||||
enabled: false,
|
||||
canEnable: false,
|
||||
error: 'Bluetooth access is not authorized. Please enable permissions in Settings.',
|
||||
};
|
||||
|
||||
case State.Unsupported:
|
||||
return {
|
||||
enabled: false,
|
||||
canEnable: false,
|
||||
error: 'Bluetooth is not supported on this device.',
|
||||
};
|
||||
|
||||
case State.Resetting:
|
||||
return {
|
||||
enabled: false,
|
||||
canEnable: true,
|
||||
error: 'Bluetooth is resetting. Please wait a moment and try again.',
|
||||
};
|
||||
|
||||
case State.Unknown:
|
||||
default:
|
||||
return {
|
||||
enabled: false,
|
||||
canEnable: true,
|
||||
error: 'Bluetooth state is unknown. Please check your device settings.',
|
||||
};
|
||||
}
|
||||
} catch (error: any) {
|
||||
return {
|
||||
enabled: false,
|
||||
canEnable: false,
|
||||
error: `Failed to check Bluetooth state: ${error.message}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a user-friendly alert for permission/Bluetooth issues
|
||||
* Offers to open Settings if needed
|
||||
*/
|
||||
export function showPermissionAlert(
|
||||
permissionStatus: PermissionStatus,
|
||||
bluetoothStatus: BluetoothStatus
|
||||
): void {
|
||||
// Bluetooth disabled (can be fixed easily)
|
||||
if (!bluetoothStatus.enabled && bluetoothStatus.canEnable) {
|
||||
Alert.alert(
|
||||
'Bluetooth Required',
|
||||
bluetoothStatus.error || 'Please enable Bluetooth to scan for sensors.',
|
||||
[
|
||||
{ text: 'Cancel', style: 'cancel' },
|
||||
{
|
||||
text: 'Open Settings',
|
||||
onPress: () => {
|
||||
if (Platform.OS === 'ios') {
|
||||
Linking.openURL('App-Prefs:Bluetooth');
|
||||
} else {
|
||||
Linking.sendIntent('android.settings.BLUETOOTH_SETTINGS');
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Permission denied with "Never ask again"
|
||||
if (!permissionStatus.granted && !permissionStatus.canRequest) {
|
||||
Alert.alert(
|
||||
'Permissions Required',
|
||||
permissionStatus.error || 'Bluetooth permissions are required to scan for sensors. Please enable them in Settings.',
|
||||
[
|
||||
{ text: 'Cancel', style: 'cancel' },
|
||||
{
|
||||
text: 'Open Settings',
|
||||
onPress: () => {
|
||||
Linking.openSettings();
|
||||
},
|
||||
},
|
||||
]
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Permission denied (can retry)
|
||||
if (!permissionStatus.granted) {
|
||||
Alert.alert(
|
||||
'Permissions Required',
|
||||
permissionStatus.error || 'Bluetooth permissions are required to scan for sensors.',
|
||||
[{ text: 'OK' }]
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Bluetooth not supported or other unrecoverable error
|
||||
if (!bluetoothStatus.canEnable) {
|
||||
Alert.alert(
|
||||
'Bluetooth Unavailable',
|
||||
bluetoothStatus.error || 'Bluetooth is not available on this device.',
|
||||
[{ text: 'OK' }]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Comprehensive pre-scan check
|
||||
* Returns true if ready to scan, false otherwise (with user alert shown)
|
||||
*/
|
||||
export async function checkBLEReadiness(manager: BleManager): Promise<boolean> {
|
||||
// Step 1: Check permissions
|
||||
const permissionStatus = await requestBLEPermissions();
|
||||
|
||||
// Step 2: Check Bluetooth state
|
||||
const bluetoothStatus = await checkBluetoothEnabled(manager);
|
||||
|
||||
// Step 3: If not ready, show appropriate alert
|
||||
if (!permissionStatus.granted || !bluetoothStatus.enabled) {
|
||||
showPermissionAlert(permissionStatus, bluetoothStatus);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user