From ef7bcd524889cac2875f408939b55401f945f654 Mon Sep 17 00:00:00 2001 From: Devasy Patel <110348311+Devasy23@users.noreply.github.com> Date: Sat, 22 Aug 2026 00:05:24 +0530 Subject: [PATCH] fix: default analytics off for all installs, not just F-Droid Second round of F-Droid review (MR 40630): the installer-identity check (PackageInfo.installerStore == 'org.fdroid.fdroid') only catches the official F-Droid client. Other F-Droid clients, sideloads, or installs where installer info can't be read all fell through with analyticsEnabled defaulting to true, so telemetry still fired on first launch for those cases. Drop the installer-detection entirely and just default analyticsEnabled to false for every install. Telemetry now requires an explicit opt-in via the Privacy toggle regardless of install source, which is simpler, removes the fragile detection, and still needs no build-recipe changes (same binary everywhere, reproducible builds unaffected). Co-Authored-By: Claude Sonnet 5 --- workout-logger/lib/main.dart | 8 ++--- .../lib/screens/widgets/profile_sections.dart | 12 ++----- .../lib/services/settings_provider.dart | 31 +++++-------------- 3 files changed, 14 insertions(+), 37 deletions(-) diff --git a/workout-logger/lib/main.dart b/workout-logger/lib/main.dart index f1cefb3..253e741 100644 --- a/workout-logger/lib/main.dart +++ b/workout-logger/lib/main.dart @@ -200,10 +200,10 @@ class _AppInitializerState extends State { // so the opt-in flag is loaded; never blocks or fails app init. readiness.refresh(); - // Fire-and-forget analytics in background — never fires for F-Droid - // installs, and honors the user's Settings toggle otherwise. Must - // run after settings.init() so both are loaded. - if (settings.telemetryAllowed) { + // Fire-and-forget analytics in background — off by default, only + // fires once the user opts in via the Settings toggle. Must run + // after settings.init() so the flag is loaded. + if (settings.analyticsEnabled) { api.sendHeartbeat(); api.trackEvent('app_open'); provider.getQuickStats().then((stats) => api.reportUsage(stats)).catchError( diff --git a/workout-logger/lib/screens/widgets/profile_sections.dart b/workout-logger/lib/screens/widgets/profile_sections.dart index 313c176..19b30c3 100644 --- a/workout-logger/lib/screens/widgets/profile_sections.dart +++ b/workout-logger/lib/screens/widgets/profile_sections.dart @@ -407,8 +407,6 @@ class PrivacySection extends StatelessWidget { @override Widget build(BuildContext context) { - final isFdroid = settings.isFdroidInstall; - final enabled = settings.telemetryAllowed; return _ProfileSection( icon: Icons.privacy_tip_outlined, iconColor: _color, @@ -432,9 +430,7 @@ class PrivacySection extends StatelessWidget { ), const SizedBox(height: 2), Text( - isFdroid - ? 'Always off for F-Droid installs' - : 'Install ID, platform, and workout counts — no personal data', + 'Off by default. Install ID, platform, and workout counts — no personal data', style: TextStyle(fontFamily: 'Geist', color: AppColors.textMuted, fontSize: 12, @@ -444,10 +440,8 @@ class PrivacySection extends StatelessWidget { ), ), Switch( - value: enabled, - onChanged: isFdroid - ? null - : (v) => settings.setAnalyticsEnabled(v), + value: settings.analyticsEnabled, + onChanged: (v) => settings.setAnalyticsEnabled(v), activeThumbColor: _color, activeTrackColor: _color.withValues(alpha: 0.35), ), diff --git a/workout-logger/lib/services/settings_provider.dart b/workout-logger/lib/services/settings_provider.dart index fbd9b8d..f882b7a 100644 --- a/workout-logger/lib/services/settings_provider.dart +++ b/workout-logger/lib/services/settings_provider.dart @@ -20,8 +20,7 @@ class SettingsProvider extends ChangeNotifier { String _weeklyInsights = ''; DateTime? _weeklyInsightsDate; bool _showAdvancedMetrics = false; - bool _analyticsEnabled = true; - bool _isFdroidInstall = false; + bool _analyticsEnabled = false; WeightUnit get weightUnit => _weightUnit; double get weightIncrement => _weightIncrement; @@ -36,21 +35,12 @@ class SettingsProvider extends ChangeNotifier { DateTime? get weeklyInsightsDate => _weeklyInsightsDate; bool get showAdvancedMetrics => _showAdvancedMetrics; - /// Whether this install came from the F-Droid client (detected at runtime - /// via the Android installer package name — never baked in at compile - /// time, since a compile-time difference between the F-Droid build and - /// the GitHub release binary would break F-Droid's byte-for-byte - /// reproducible-build verification against `Binaries:` in fdroiddata). - bool get isFdroidInstall => _isFdroidInstall; - - /// User's analytics preference, as stored. F-Droid installs are always - /// telemetry-free regardless of this value — see [telemetryAllowed]. - bool get analyticsEnabled => _analyticsEnabled; - /// Whether automatic telemetry (heartbeat/app-open/usage-report) may - /// fire. False for F-Droid installs unconditionally; otherwise follows - /// the user's setting. - bool get telemetryAllowed => _analyticsEnabled && !_isFdroidInstall; + /// fire. Off by default for every install — installer identity (F-Droid + /// vs. sideload vs. Play) isn't a reliable signal, since there are + /// multiple F-Droid client apps and installer info can be unreadable. + /// Users opt in via the Privacy toggle in Settings. + bool get analyticsEnabled => _analyticsEnabled; SettingsProvider(this._storage); @@ -80,14 +70,7 @@ class SettingsProvider extends ChangeNotifier { _showAdvancedMetrics = advMetrics == 'true'; final analytics = await _storage.getSetting('analyticsEnabled'); - _analyticsEnabled = analytics != 'false'; - - try { - final info = await PackageInfo.fromPlatform(); - _isFdroidInstall = info.installerStore == 'org.fdroid.fdroid'; - } catch (_) { - _isFdroidInstall = false; - } + _analyticsEnabled = analytics == 'true'; } Future setUserName(String name) async {