fix(fitness): request ACTIVITY_RECOGNITION for cadence
Android step detector silently returns no events on API 29+ when ACTIVITY_RECOGNITION is ungranted, so cadence was always absent from recorded tracks. Declare the permission, request it at GPS start, guard sensor registration and retry it from MainActivity.onRequestPermissionsResult when the user grants mid-session, and toast a hint if they deny.
This commit is contained in:
@@ -111,6 +111,10 @@ const translations: Translations = {
|
||||
elevation_loss: { en: 'Loss', de: 'Abstieg' },
|
||||
cadence: { en: 'Cadence', de: 'Kadenz' },
|
||||
cadence_unit: { en: 'spm', de: 'spm' },
|
||||
cadence_permission_missing: {
|
||||
en: 'Cadence disabled — grant Activity Recognition in system settings',
|
||||
de: 'Kadenz deaktiviert — Aktivitätserkennung in den Einstellungen erlauben'
|
||||
},
|
||||
personal_records: { en: 'Personal Records', de: 'Persönliche Rekorde' },
|
||||
delete_session_confirm: { en: 'Delete this workout session?', de: 'Dieses Training löschen?' },
|
||||
remove_gps_confirm: { en: 'Remove GPS track from this exercise?', de: 'GPS-Track von dieser Übung entfernen?' },
|
||||
|
||||
@@ -79,6 +79,7 @@ interface AndroidBridge {
|
||||
pauseTracking(): void;
|
||||
resumeTracking(): void;
|
||||
getIntervalState(): string;
|
||||
hasActivityRecognitionPermission?: () => boolean;
|
||||
}
|
||||
|
||||
function checkTauri(): boolean {
|
||||
@@ -294,6 +295,13 @@ export function createGpsTracker() {
|
||||
}
|
||||
}
|
||||
|
||||
function cadenceAvailable(): boolean {
|
||||
const bridge = getAndroidBridge();
|
||||
// No bridge (e.g. browser) or older build lacking the check: assume ok, don't nag.
|
||||
if (!bridge || typeof bridge.hasActivityRecognitionPermission !== 'function') return true;
|
||||
try { return bridge.hasActivityRecognitionPermission(); } catch { return true; }
|
||||
}
|
||||
|
||||
return {
|
||||
get track() { return track; },
|
||||
get isTracking() { return isTracking; },
|
||||
@@ -304,6 +312,7 @@ export function createGpsTracker() {
|
||||
get available() { return checkTauri(); },
|
||||
get debug() { return _debugMsg; },
|
||||
get intervalState() { return _intervalState; },
|
||||
cadenceAvailable,
|
||||
start,
|
||||
stop,
|
||||
reset,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { Trash2, Play, Pause, Trophy, Clock, Dumbbell, Route, RefreshCw, Check, ChevronUp, ChevronDown, Flame, MapPin, Volume2, X, Timer, Plus, GripVertical, Repeat } from '@lucide/svelte';
|
||||
import { detectFitnessLang, fitnessSlugs, t } from '$lib/js/fitnessI18n';
|
||||
import { confirm } from '$lib/js/confirmDialog.svelte';
|
||||
import { toast } from '$lib/js/toast.svelte';
|
||||
|
||||
const lang = $derived(detectFitnessLang($page.url.pathname));
|
||||
const sl = $derived(fitnessSlugs(lang));
|
||||
@@ -359,6 +360,20 @@
|
||||
}
|
||||
}
|
||||
|
||||
let _cadenceWarned = false;
|
||||
/** After the user resolves the permission dialog, check if cadence will work.
|
||||
* We wait a few seconds to let the system dialog settle before toasting. */
|
||||
function maybeWarnCadence() {
|
||||
if (_cadenceWarned) return;
|
||||
setTimeout(() => {
|
||||
if (_cadenceWarned) return;
|
||||
if (!gps.cadenceAvailable()) {
|
||||
_cadenceWarned = true;
|
||||
toast.info(t('cadence_permission_missing', lang));
|
||||
}
|
||||
}, 4000);
|
||||
}
|
||||
|
||||
let gpsToggling = $state(false);
|
||||
async function toggleGps() {
|
||||
if (gpsToggling) return;
|
||||
@@ -369,6 +384,7 @@
|
||||
useGps = true;
|
||||
} else {
|
||||
useGps = await gps.start(getVoiceGuidanceConfig());
|
||||
if (useGps) maybeWarnCadence();
|
||||
}
|
||||
} else {
|
||||
await gps.stop();
|
||||
@@ -471,6 +487,7 @@
|
||||
if (workout.mode === 'gps' && !gpsStarted && !gps.isTracking) {
|
||||
_prestartGps = true;
|
||||
gps.start(undefined, true);
|
||||
maybeWarnCadence();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -510,6 +527,7 @@
|
||||
const exercise = getExerciseById(exerciseId);
|
||||
if (exercise?.bodyPart === 'cardio' && gps.available && !useGps && !gps.isTracking) {
|
||||
useGps = await gps.start(getVoiceGuidanceConfig());
|
||||
if (useGps) maybeWarnCadence();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -529,6 +547,7 @@
|
||||
gpsStarted = true;
|
||||
useGps = true;
|
||||
workout.resumeTimer();
|
||||
maybeWarnCadence();
|
||||
}
|
||||
} finally {
|
||||
gpsStarting = false;
|
||||
|
||||
Reference in New Issue
Block a user