diff --git a/docs/session-logs/2026-10-17_bt-gps-prompts.md b/docs/session-logs/2026-10-17_bt-gps-prompts.md new file mode 100644 index 0000000..6167196 --- /dev/null +++ b/docs/session-logs/2026-10-17_bt-gps-prompts.md @@ -0,0 +1,30 @@ +# Сессия 2026-10-17 (2) - попап "включи BT/гео" при старте записи + 2 настройки "не спрашивать" + +## Задача +Пользователь забывает включать Bluetooth и GPS. Нужно: при старте записи, если выключено - +предложить включить; дать отказаться; 2 отдельные настройки "не спрашивать". + +Факт-чек: телефон С Google-сервисами (ранее ошибочно решил, что без). Тихо включать BT/гео +приложение на target 36 не может (ОС), поэтому - попап + действие: BT через системный диалог +включения, гео через экран настроек геолокации. + +## Сделано +- Rust `read_state_file(name)` (allowlist "location_state.json") - читает ответ Kotlin из + native_root. Зарегистрирован. +- Kotlin MainActivity: команды `check_location` (пишет `location_state.json` {enabled} через + LocationManager.isLocationEnabled) и `open_location_settings` (ACTION_LOCATION_SOURCE_SETTINGS). +- `gps.js` `GPS.isLocationEnabled()` - дёргает check_location, читает state-файл; true/false или + null (неизвестно - тогда не спрашивать). +- `main.js` `ensureSystemReady(needBt, needGps)` - перед startRecording: BT off -> + appConfirm "Включить?" -> `ble_request_enable`; гео off -> appConfirm "Открыть настройки?" -> + `open_location_settings`. Гейтится настройками `baldav_dontask_bt` / `baldav_dontask_gps`. + Вызов в кнопке записи: needBt = есть привязанный датчик, needGps = не зарядка. +- Настройки (раздел Пульсометр): 2 чекбокса "Не спрашивать про Bluetooth" / "...геолокацию". + +Ограничение (честно): авто-ВЫКЛ BT/гео после тренировки приложение сделать не может (ОС). +Тихое включение - тоже; поэтому попап + один тап. + +## Проверка +Синтаксис JS ок. На телефоне: выключить BT и гео, старт записи трека -> два попапа по очереди +(Включить BT / Открыть настройки гео); отказ -> запись идёт как есть; включить чекбоксы "не +спрашивать" -> попапы не показываются. diff --git a/frontend/js/gps.js b/frontend/js/gps.js index 81641f6..e86c11e 100644 --- a/frontend/js/gps.js +++ b/frontend/js/gps.js @@ -822,6 +822,19 @@ GPS.requestBackgroundLocation = async function() { } catch (e) { console.log("[GPS] reqbgloc:", e); } }; +// Is the OS location toggle ON (not just the permission)? Kotlin checks LocationManager and +// writes location_state.json; we read it back. Returns true/false, or null if undeterminable +// (desktop / no answer) — callers must not prompt on null. +GPS.isLocationEnabled = async function() { + try { + await window.__TAURI__.core.invoke("gps_service_command", { cmd: "check_location" }); + await new Promise(r => setTimeout(r, 300)); + let raw = await window.__TAURI__.core.invoke("read_state_file", { name: "location_state.json" }); + if (!raw) return null; + return !!JSON.parse(raw).enabled; + } catch { return null; } +}; + // BLE HR monitor — tied to GPS session (start→stop), but NOT paused when GPS pauses. // HR keeps measuring even when rider is stationary. GPS._startHrMonitor = async function() { diff --git a/frontend/js/main.js b/frontend/js/main.js index caca390..2e0d2b8 100644 --- a/frontend/js/main.js +++ b/frontend/js/main.js @@ -1182,6 +1182,26 @@ function setBtnProgress(el, pct) { el.style.setProperty("--pct", Math.max(0, Math.min(100, Math.round(pct))) + "%"); } +// Before a recording, nudge the user to turn on what it needs: Bluetooth (for the HR strap) and +// location (for the track). Can't silently toggle them on modern Android, so prompt + act: BT via +// the system enable dialog, location via the location-settings screen. Declining is fine, and the +// two "Не спрашивать" settings suppress each prompt. Never prompts when the state is unknown. +async function ensureSystemReady(needBt, needGps) { + let inv = window.__TAURI__.core.invoke; + if (needBt && localStorage.getItem("baldav_dontask_bt") !== "1") { + let bt = "on"; try { bt = await inv("ble_check_bluetooth"); } catch {} + if (bt === "off" && await appConfirm("Для пульсометра нужен Bluetooth. Включить?", "Включить")) { + try { await inv("ble_request_enable"); } catch {} + } + } + if (needGps && localStorage.getItem("baldav_dontask_gps") !== "1") { + let locOn = await GPS.isLocationEnabled(); + if (locOn === false && await appConfirm("Для записи трека нужна геолокация. Открыть настройки, чтобы включить?", "Открыть")) { + try { await inv("gps_service_command", { cmd: "open_location_settings" }); } catch {} + } + } +} + async function reopenDetail(sessionId) { let count = 0; while (winStack.length > 0) { @@ -2846,6 +2866,10 @@ async function renderGpsScreen(container) { GPS.recording.gearCount = bike.gear_count; } } + // Nudge BT (for the strap) / location (for the track) on if off. Non-blocking to the user's + // choice: they can decline, and the two "Не спрашивать" settings suppress each prompt. + await ensureSystemReady(!!state.profile?.ble_device_addr, type !== "workout"); + // Workout (HR-only): pass the profile so finish can compute kcal from HR (Keytel). let hrProfile = type === "workout" ? { weightKg: wkg, @@ -7330,6 +7354,16 @@ async function renderSettings(container) {
+ +