diff --git a/apps/android/ChangeLog b/apps/android/ChangeLog index db5c0b057..de70af79f 100644 --- a/apps/android/ChangeLog +++ b/apps/android/ChangeLog @@ -20,3 +20,4 @@ 0.19: Add automatic translation for a couple of strings. 0.20: Fix wrong event used for forwarded GPS data from Gadgetbridge and add mapper to map longitude value correctly. 0.21: Fix broken 'Messages' button in menu +0.22: Handle connection events for GPS forwarding from phone diff --git a/apps/android/boot.js b/apps/android/boot.js index 88c45a0c5..0db1b48f4 100644 --- a/apps/android/boot.js +++ b/apps/android/boot.js @@ -141,7 +141,7 @@ Bangle.emit('GPS', event); }, "is_gps_active": function() { - gbSend({ t: "gps_power", status: Bangle._PWR && Bangle._PWR.GPS && Bangle._PWR.GPS.length>0 }); + gbSend({ t: "gps_power", status: Bangle.isGPSOn() }); } }; var h = HANDLERS[event.t]; @@ -178,7 +178,7 @@ },options.timeout||30000)}; }); return promise; - } + }; // Battery monitor function sendBattery() { gbSend({ t: "status", bat: E.getBattery(), chg: Bangle.isCharging()?1:0 }); } @@ -207,13 +207,39 @@ }; // GPS overwrite logic if (settings.overwriteGps) { // if the overwrite option is set../ - // Save current logic - const originalSetGpsPower = Bangle.setGPSPower; + const origSetGPSPower = Bangle.setGPSPower; + // migrate all GPS clients to the other variant on connection events + let handleConnection = (state) => { + if (Bangle.isGPSOn()){ + let orig = Bangle._PWR.GPS; + delete Bangle._PWR.GPS; + origSetGPSPower(state); + Bangle._PWR.GPS = orig; + } + }; + NRF.on('connect', ()=>{handleConnection(0);}); + NRF.on('disconnect', ()=>{handleConnection(1);}); + + // Work around Serial1 for GPS not working when connected to something + let serialTimeout; + let wrap = function(f){ + return (s)=>{ + if (serialTimeout) clearTimeout(serialTimeout); + handleConnection(1); + f(s); + serialTimeout = setTimeout(()=>{ + serialTimeout = undefined; + if (NRF.getSecurityStatus().connected) handleConnection(0); + }, 10000); + }; + }; + Serial1.println = wrap(Serial1.println); + Serial1.write = wrap(Serial1.write); + // Replace set GPS power logic to suppress activation of gps (and instead request it from the phone) Bangle.setGPSPower = (isOn, appID) => { - // if not connected, use old logic - if (!NRF.getSecurityStatus().connected) return originalSetGpsPower(isOn, appID); - // Emulate old GPS power logic + // if not connected use internal GPS power function + if (!NRF.getSecurityStatus().connected) return origSetGPSPower(isOn, appID); if (!Bangle._PWR) Bangle._PWR={}; if (!Bangle._PWR.GPS) Bangle._PWR.GPS=[]; if (!appID) appID="?"; @@ -222,11 +248,15 @@ let pwr = Bangle._PWR.GPS.length>0; gbSend({ t: "gps_power", status: pwr }); return pwr; - } - // Replace check if the GPS is on to check the _PWR variable + }; + // Allow checking for GPS via GadgetBridge Bangle.isGPSOn = () => { - return Bangle._PWR && Bangle._PWR.GPS && Bangle._PWR.GPS.length>0; - } + return !!(Bangle._PWR && Bangle._PWR.GPS && Bangle._PWR.GPS.length>0); + }; + // stop GPS on boot if not activated + setTimeout(()=>{ + if (!Bangle.isGPSOn()) gbSend({ t: "gps_power", status: false }); + },3000); } // remove settings object so it's not taking up RAM diff --git a/apps/android/metadata.json b/apps/android/metadata.json index 63fd7759a..94c24afd5 100644 --- a/apps/android/metadata.json +++ b/apps/android/metadata.json @@ -2,7 +2,7 @@ "id": "android", "name": "Android Integration", "shortName": "Android", - "version": "0.21", + "version": "0.22", "description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.", "icon": "app.png", "tags": "tool,system,messages,notifications,gadgetbridge", diff --git a/apps/android/test.js b/apps/android/test.js new file mode 100644 index 000000000..88a7c0566 --- /dev/null +++ b/apps/android/test.js @@ -0,0 +1,126 @@ +let result = true; + +function assertTrue(condition, text) { + if (!condition) { + result = false; + print("FAILURE: " + text); + } else print("OK: " + text); +} + +function assertFalse(condition, text) { + assertTrue(!condition, text); +} + +function assertUndefinedOrEmpty(array, text) { + assertTrue(!array || array.length == 0, text); +} + +function assertNotEmpty(array, text) { + assertTrue(array && array.length > 0, text); +} + +let internalOn = () => { + return getPinMode((process.env.HWVERSION==2)?D30:D26) == "input"; +}; + +let sec = { + connected: false +}; + +NRF.getSecurityStatus = () => sec; + +setTimeout(() => { + // add an empty starting point to make the asserts work + Bangle._PWR={}; + + print("Not connected, should use internal GPS"); + assertTrue(!NRF.getSecurityStatus().connected, "Not connected"); + + assertUndefinedOrEmpty(Bangle._PWR.GPS, "No GPS"); + assertFalse(Bangle.isGPSOn(), "isGPSOn"); + + assertTrue(Bangle.setGPSPower(1, "test"), "Switch GPS on"); + + assertNotEmpty(Bangle._PWR.GPS, "GPS"); + assertTrue(Bangle.isGPSOn(), "isGPSOn"); + assertTrue(internalOn(), "Internal GPS on"); + + assertFalse(Bangle.setGPSPower(0, "test"), "Switch GPS off"); + + assertUndefinedOrEmpty(Bangle._PWR.GPS, "No GPS"); + assertFalse(Bangle.isGPSOn(), "isGPSOn"); + assertFalse(internalOn(), "Internal GPS off"); + + print("Connected, should use GB GPS"); + sec.connected = true; + + assertTrue(NRF.getSecurityStatus().connected, "Connected"); + + assertUndefinedOrEmpty(Bangle._PWR.GPS, "No GPS"); + assertFalse(Bangle.isGPSOn(), "isGPSOn"); + assertFalse(internalOn(), "Internal GPS off"); + + assertTrue(Bangle.setGPSPower(1, "test"), "Switch GPS on"); + + assertNotEmpty(Bangle._PWR.GPS, "GPS"); + assertTrue(Bangle.isGPSOn(), "isGPSOn"); + assertFalse(internalOn(), "Internal GPS off"); + + assertFalse(Bangle.setGPSPower(0, "test"), "Switch GPS off"); + + assertUndefinedOrEmpty(Bangle._PWR.GPS, "No GPS"); + assertFalse(Bangle.isGPSOn(), "isGPSOn"); + assertFalse(internalOn(), "Internal GPS off"); + + print("Connected, then reconnect cycle"); + sec.connected = true; + + assertTrue(NRF.getSecurityStatus().connected, "Connected"); + + assertUndefinedOrEmpty(Bangle._PWR.GPS, "No GPS"); + assertFalse(Bangle.isGPSOn(), "isGPSOn"); + assertFalse(internalOn(), "Internal GPS off"); + + assertTrue(Bangle.setGPSPower(1, "test"), "Switch GPS on"); + + assertNotEmpty(Bangle._PWR.GPS, "GPS"); + assertTrue(Bangle.isGPSOn(), "isGPSOn"); + assertFalse(internalOn(), "Internal GPS off"); + + NRF.emit("disconnect", {}); + print("disconnect"); + sec.connected = false; + + setTimeout(() => { + + assertNotEmpty(Bangle._PWR.GPS, "GPS"); + assertTrue(Bangle.isGPSOn(), "isGPSOn"); + assertTrue(internalOn(), "Internal GPS on"); + + print("connect"); + sec.connected = true; + NRF.emit("connect", {}); + + setTimeout(() => { + assertNotEmpty(Bangle._PWR.GPS, "GPS"); + assertTrue(Bangle.isGPSOn(), "isGPSOn"); + assertFalse(internalOn(), "Internal GPS off"); + + assertFalse(Bangle.setGPSPower(0, "test"), "Switch GPS off"); + + assertUndefinedOrEmpty(Bangle._PWR.GPS, "No GPS"); + assertFalse(Bangle.isGPSOn(), "isGPSOn"); + assertFalse(internalOn(), "Internal GPS off"); + + setTimeout(() => { + print("Test disconnect without gps on"); + + assertUndefinedOrEmpty(Bangle._PWR.GPS, "No GPS"); + assertFalse(Bangle.isGPSOn(), "isGPSOn"); + assertFalse(internalOn(), "Internal GPS off"); + + print("Result Overall is " + (result ? "OK" : "FAIL")); + }, 0); + }, 0); + }, 0); +}, 5000); \ No newline at end of file