@@ -71,6 +71,7 @@ import TokenIcon, { clearImageCache } from "./src/components/TokenIcon";
7171import QRCode from "react-native-qrcode-svg" ;
7272import { CameraView , useCameraPermissions } from "expo-camera" ;
7373import TransportBLE from "@ledgerhq/react-native-hw-transport-ble" ;
74+ import { BleManager } from "react-native-ble-plx" ;
7475import AppSolana from "@ledgerhq/hw-app-solana" ;
7576import AsyncStorage from "@react-native-async-storage/async-storage" ;
7677import * as SecureStore from "expo-secure-store" ;
@@ -392,6 +393,7 @@ function AppContent() {
392393 const ledgerScanSubscriptionRef = useRef ( null ) ; // Store scan subscription for cleanup
393394 const ledgerCleaningRef = useRef ( false ) ; // Prevent concurrent cleanup
394395 const ledgerCleanedUpRef = useRef ( false ) ; // Track if cleanup has already been completed
396+ const bleManagerRef = useRef ( null ) ; // BLE Manager instance for Bluetooth control
395397 const sendAddressInputRef = useRef ( null ) ; // Ref for send address TextInput
396398
397399 // Send and Receive states
@@ -3188,6 +3190,78 @@ function AppContent() {
31883190 scanForLedger ( ) ;
31893191 } ;
31903192
3193+ // Bluetooth Power Cycle - Reset Bluetooth adapter to clear stale connections
3194+ const powerCycleBluetooth = async ( ) => {
3195+ console . log ( "🔄 Attempting to power cycle Bluetooth adapter..." ) ;
3196+
3197+ try {
3198+ // Initialize BLE Manager if not already done
3199+ if ( ! bleManagerRef . current ) {
3200+ bleManagerRef . current = new BleManager ( ) ;
3201+ console . log ( " ✓ BLE Manager initialized" ) ;
3202+ }
3203+
3204+ const manager = bleManagerRef . current ;
3205+
3206+ // Check current Bluetooth state
3207+ const state = await manager . state ( ) ;
3208+ console . log ( ` 📡 Current Bluetooth state: ${ state } ` ) ;
3209+
3210+ if ( state === "PoweredOn" ) {
3211+ // Try to disable Bluetooth (only works on Android <13)
3212+ try {
3213+ console . log ( " ⏸ Disabling Bluetooth..." ) ;
3214+ await manager . disable ( ) ;
3215+ console . log ( " ✓ Bluetooth disabled" ) ;
3216+
3217+ // Wait for it to fully power down
3218+ await new Promise ( ( resolve ) => setTimeout ( resolve , 1000 ) ) ;
3219+
3220+ // Re-enable Bluetooth
3221+ console . log ( " ▶ Re-enabling Bluetooth..." ) ;
3222+ await manager . enable ( ) ;
3223+ console . log ( " ✓ Bluetooth re-enabled" ) ;
3224+
3225+ // Wait for it to fully power up
3226+ await new Promise ( ( resolve ) => setTimeout ( resolve , 1500 ) ) ;
3227+
3228+ console . log ( "✅ Bluetooth power cycle complete!" ) ;
3229+ return true ;
3230+ } catch ( error ) {
3231+ // Disable/enable not supported (Android 13+)
3232+ if ( error . message && error . message . includes ( "not supported" ) ) {
3233+ console . log (
3234+ " ⚠ Bluetooth disable/enable not supported on this Android version"
3235+ ) ;
3236+ console . log (
3237+ " 💡 User must manually toggle Bluetooth in system settings"
3238+ ) ;
3239+
3240+ Toast . show ( {
3241+ type : "info" ,
3242+ text1 : "Bluetooth Reset Required" ,
3243+ text2 :
3244+ "Please toggle Bluetooth OFF then ON in your phone settings to clear stale connections" ,
3245+ position : "bottom" ,
3246+ visibilityTime : 5000 ,
3247+ } ) ;
3248+ return false ;
3249+ }
3250+ throw error ;
3251+ }
3252+ } else {
3253+ console . log (
3254+ ` ⚠ Bluetooth is not powered on (state: ${ state } ), skipping power cycle`
3255+ ) ;
3256+ return false ;
3257+ }
3258+ } catch ( error ) {
3259+ console . error ( "❌ Error during Bluetooth power cycle:" , error ) ;
3260+ console . error ( " Error message:" , error . message ) ;
3261+ return false ;
3262+ }
3263+ } ;
3264+
31913265 // Proper BLE cleanup function following best practices
31923266 const cleanupLedgerBLE = async ( ) => {
31933267 // Prevent double cleanup - check if already cleaned up OR currently cleaning
@@ -3536,7 +3610,21 @@ function AppContent() {
35363610 ledgerCleaningRef . current = false ;
35373611 ledgerCleanedUpRef . current = false ;
35383612
3539- // 4. Attempt to disconnect using device ID if supported
3613+ // 4. Power cycle Bluetooth on second retry to clear stale connections
3614+ // (Skip on first attempt to avoid unnecessary delay)
3615+ if ( retryCount === 1 ) {
3616+ console . log ( " 🔄 Second retry attempt - power cycling Bluetooth..." ) ;
3617+ const cycled = await powerCycleBluetooth ( ) ;
3618+ if ( cycled ) {
3619+ console . log ( " ✓ Bluetooth power cycle succeeded" ) ;
3620+ } else {
3621+ console . log (
3622+ " ⚠ Bluetooth power cycle not available (Android 13+)"
3623+ ) ;
3624+ }
3625+ }
3626+
3627+ // 5. Attempt to disconnect using device ID if supported
35403628 if ( TransportBLE . disconnectDevice ) {
35413629 await TransportBLE . disconnectDevice ( deviceId ) ;
35423630 console . log ( " ✓ TransportBLE.disconnectDevice succeeded" ) ;
0 commit comments