diff --git a/workout-logger/lib/main.dart b/workout-logger/lib/main.dart index 253e741..2460957 100644 --- a/workout-logger/lib/main.dart +++ b/workout-logger/lib/main.dart @@ -18,7 +18,6 @@ import 'services/interfaces/ml_service_interface.dart'; import 'services/interfaces/health_connect_service_interface.dart'; import 'services/workout_provider.dart'; import 'services/settings_provider.dart'; -import 'services/api_service.dart'; import 'services/managers/program_manager.dart'; import 'services/managers/history_manager.dart'; import 'services/managers/health_sync_manager.dart'; @@ -101,8 +100,6 @@ class WorkoutLoggerApp extends StatelessWidget { Provider.value(value: _mlService), // IHealthConnectService stays in tree for ProfileScreen permission flow Provider.value(value: _healthConnectService), - // Provide the ApiService singleton via DI - Provider.value(value: ApiService()), // ProgramManager passed to tree directly ChangeNotifierProvider.value(value: _programManager), // SettingsProvider for user preferences (weight unit, increments) @@ -173,7 +170,6 @@ class _AppInitializerState extends State { final settings = context.read(); final historyManager = context.read(); final prManager = context.read(); - final api = context.read(); final gemini = context.read(); final readiness = context.read(); @@ -200,17 +196,6 @@ 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 — 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( - (Object e) => debugPrint('Failed to report usage: $e'), - ); - } - if (!mounted) return; setState(() { _initialized = true; diff --git a/workout-logger/lib/screens/profile_screen.dart b/workout-logger/lib/screens/profile_screen.dart index e7432fe..21e2910 100644 --- a/workout-logger/lib/screens/profile_screen.dart +++ b/workout-logger/lib/screens/profile_screen.dart @@ -362,8 +362,6 @@ class _ProfileScreenState extends State const SizedBox(height: AppSpacing.md), const AiSettingsSection(), const SizedBox(height: AppSpacing.md), - PrivacySection(settings: settings), - const SizedBox(height: AppSpacing.md), AboutSection(appVersion: _appVersion), const SizedBox(height: AppSpacing.xxl), ]), diff --git a/workout-logger/lib/screens/settings_screen.dart b/workout-logger/lib/screens/settings_screen.dart deleted file mode 100644 index ca5e33a..0000000 --- a/workout-logger/lib/screens/settings_screen.dart +++ /dev/null @@ -1,594 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:file_picker/file_picker.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:share_plus/share_plus.dart'; -import 'package:intl/intl.dart'; -import '../services/workout_provider.dart'; -import '../services/settings_provider.dart'; -import '../services/api_service.dart'; -import '../theme/app_theme.dart'; - -class SettingsScreen extends StatefulWidget { - const SettingsScreen({super.key}); - - @override - State createState() => _SettingsScreenState(); -} - -class _SettingsScreenState extends State { - bool _isBackingUp = false; - bool _isExporting = false; - bool _isImporting = false; - - // ==================== Feedback helpers ==================== - - void _showErrorSnackBar(String message) { - if (!mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(message), - backgroundColor: AppTheme.error, - ), - ); - } - - void _showSuccessSnackBar(String message) { - if (!mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(message), - backgroundColor: AppTheme.success, - ), - ); - } - - // ==================== Remote Backup ==================== - - Future _performBackup() async { - setState(() => _isBackingUp = true); - - try { - final provider = context.read(); - final jsonString = await provider.exportAllData(); - final data = jsonDecode(jsonString) as Map; - - if (!mounted) return; - final api = context.read(); - await api.trackEvent('backup_triggered').catchError((_) => null); - final success = await api.backupData(data); - - if (success) { - _showSuccessSnackBar('Backup successful!'); - } else { - _showErrorSnackBar('Backup failed. Please try again.'); - } - } catch (e, stackTrace) { - debugPrint('Backup error: $e\n$stackTrace'); - _showErrorSnackBar('Something went wrong. Please try again.'); - } finally { - if (mounted) { - setState(() => _isBackingUp = false); - } - } - } - - // ==================== Local Export ==================== - - Future _exportToFile() async { - setState(() => _isExporting = true); - - try { - final provider = context.read(); - final jsonString = await provider.exportAllData(); - - final tempDir = await getTemporaryDirectory(); - final dateStr = DateFormat('yyyy-MM-dd_HHmmss').format(DateTime.now()); - final fileName = 'repforge_backup_$dateStr.json'; - final file = File('${tempDir.path}/$fileName'); - await file.writeAsString(jsonString); - - final result = await SharePlus.instance.share( - ShareParams( - files: [XFile(file.path)], - subject: 'RepForge Backup', - ), - ); - - if (result.status == ShareResultStatus.success || - result.status == ShareResultStatus.dismissed) { - _showSuccessSnackBar('Backup file exported successfully!'); - } - } catch (e, stackTrace) { - debugPrint('Export error: $e\n$stackTrace'); - _showErrorSnackBar('Export failed. Please try again.'); - } finally { - if (mounted) { - setState(() => _isExporting = false); - } - } - } - - // ==================== Local Import ==================== - - Future _importFromFile() async { - final provider = context.read(); - // Show confirmation dialog - final confirmed = await showDialog( - context: context, - builder: (context) => AlertDialog( - backgroundColor: AppTheme.cardColor, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), - title: const Text( - 'Import Backup', - style: TextStyle(color: AppTheme.textPrimary), - ), - content: const Text( - 'This will merge the backup data with your existing data. ' - 'Existing workouts, routines, and targets will be kept. ' - 'New items from the backup will be added.\n\n' - 'Select a .json backup file to continue.', - style: TextStyle(color: AppTheme.textSecondary), - ), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context, false), - child: const Text( - 'Cancel', - style: TextStyle(color: AppTheme.textSecondary), - ), - ), - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: AppTheme.primaryColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - onPressed: () => Navigator.pop(context, true), - child: const Text('Choose File'), - ), - ], - ), - ); - - if (confirmed != true) return; - - setState(() => _isImporting = true); - - try { - final result = await FilePicker.pickFiles( - type: FileType.custom, - allowedExtensions: ['json'], - ); - - if (result == null || result.files.single.path == null) { - if (mounted) setState(() => _isImporting = false); - return; - } - - final file = File(result.files.single.path!); - final jsonString = await file.readAsString(); - if (!mounted) return; - - // Basic validation: ensure it's valid JSON with expected keys - final data = jsonDecode(jsonString) as Map; - if (!data.containsKey('sessions') && !data.containsKey('routines')) { - _showErrorSnackBar('Invalid backup file. Expected a RepForge backup.'); - return; - } - - await provider.importData(jsonString); - if (!mounted) return; - - final itemCount = (data['sessions'] as List?)?.length ?? 0; - final routineCount = (data['routines'] as List?)?.length ?? 0; - - _showSuccessSnackBar( - 'Import complete! Processed $itemCount sessions, $routineCount routines.', - ); - } catch (e, stackTrace) { - debugPrint('Import error: $e\n$stackTrace'); - _showErrorSnackBar( - 'Import failed. Make sure you selected a valid backup file.', - ); - } finally { - if (mounted) { - setState(() => _isImporting = false); - } - } - } - - // ==================== Build ==================== - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('Settings'), - backgroundColor: AppTheme.surfaceColor, - elevation: 0, - ), - body: ListView( - padding: const EdgeInsets.all(16), - children: [ - _buildSectionHeader('Preferences'), - const SizedBox(height: 16), - _buildPreferencesCard(), - const SizedBox(height: 24), - _buildSectionHeader('Data Management'), - const SizedBox(height: 16), - _buildLocalBackupCard(), - const SizedBox(height: 16), - _buildBackupCard(), - ], - ), - ); - } - - Widget _buildPreferencesCard() { - final settings = context.watch(); - - return Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: AppTheme.cardColor, - borderRadius: BorderRadius.circular(12), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: AppTheme.primaryColor.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(8), - ), - child: const Icon( - Icons.tune_rounded, - color: AppTheme.primaryColor, - ), - ), - const SizedBox(width: 16), - const Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Units & Increments', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: AppTheme.textPrimary, - ), - ), - Text( - 'Customize weight display and input steps', - style: TextStyle( - fontSize: 12, - color: AppTheme.textSecondary, - ), - ), - ], - ), - ), - ], - ), - const SizedBox(height: 20), - // Weight unit toggle - const Text( - 'Weight Unit', - style: TextStyle( - color: AppTheme.textSecondary, - fontSize: 13, - fontWeight: FontWeight.w500, - ), - ), - const SizedBox(height: 8), - Row( - children: [ - Expanded( - child: _UnitButton( - label: 'kg', - selected: settings.weightUnit == WeightUnit.kg, - onTap: () => settings.setWeightUnit(WeightUnit.kg), - ), - ), - const SizedBox(width: 8), - Expanded( - child: _UnitButton( - label: 'lbs', - selected: settings.weightUnit == WeightUnit.lbs, - onTap: () => settings.setWeightUnit(WeightUnit.lbs), - ), - ), - ], - ), - const SizedBox(height: 16), - // Weight increment selector - const Text( - 'Weight Increment', - style: TextStyle( - color: AppTheme.textSecondary, - fontSize: 13, - fontWeight: FontWeight.w500, - ), - ), - const SizedBox(height: 8), - Wrap( - spacing: 8, - children: settings.availableIncrements.map((inc) { - final selected = settings.weightIncrement == inc; - return ChoiceChip( - label: Text( - inc == inc.truncateToDouble() - ? '${inc.toStringAsFixed(0)} ${settings.unitLabel}' - : '${inc.toStringAsFixed(2).replaceAll(RegExp(r'0+$'), '')} ${settings.unitLabel}', - ), - selected: selected, - onSelected: (_) => settings.setWeightIncrement(inc), - selectedColor: AppTheme.primaryColor.withValues(alpha: 0.3), - backgroundColor: AppTheme.surfaceColor, - labelStyle: TextStyle( - color: selected ? AppTheme.primaryColor : AppTheme.textSecondary, - fontWeight: selected ? FontWeight.bold : FontWeight.normal, - ), - side: BorderSide( - color: selected - ? AppTheme.primaryColor - : AppTheme.surfaceColor, - ), - ); - }).toList(), - ), - ], - ), - ); - } - - Widget _buildSectionHeader(String title) { - return Text( - title, - style: Theme.of(context).textTheme.titleMedium?.copyWith( - color: AppTheme.primaryColor, - fontWeight: FontWeight.bold, - ), - ); - } - - Widget _buildLocalBackupCard() { - return Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: AppTheme.cardColor, - borderRadius: BorderRadius.circular(12), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: AppTheme.secondaryColor.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(8), - ), - child: const Icon( - Icons.save_alt_rounded, - color: AppTheme.secondaryColor, - ), - ), - const SizedBox(width: 16), - const Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Local Backup', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: AppTheme.textPrimary, - ), - ), - Text( - 'Export or import a backup file to transfer data between devices', - style: TextStyle( - fontSize: 12, - color: AppTheme.textSecondary, - ), - ), - ], - ), - ), - ], - ), - const SizedBox(height: 16), - Row( - children: [ - Expanded( - child: ElevatedButton.icon( - onPressed: _isExporting ? null : _exportToFile, - icon: _isExporting - ? const SizedBox( - height: 18, - width: 18, - child: CircularProgressIndicator( - strokeWidth: 2, - valueColor: AlwaysStoppedAnimation( - Colors.white, - ), - ), - ) - : const Icon(Icons.upload_file_rounded, size: 20), - label: Text(_isExporting ? 'Exporting...' : 'Export'), - style: ElevatedButton.styleFrom( - backgroundColor: AppTheme.secondaryColor, - foregroundColor: AppTheme.backgroundColor, - padding: const EdgeInsets.symmetric(vertical: 12), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - ), - ), - const SizedBox(width: 12), - Expanded( - child: OutlinedButton.icon( - onPressed: _isImporting ? null : _importFromFile, - icon: _isImporting - ? const SizedBox( - height: 18, - width: 18, - child: CircularProgressIndicator( - strokeWidth: 2, - valueColor: AlwaysStoppedAnimation( - AppTheme.secondaryColor, - ), - ), - ) - : const Icon(Icons.download_rounded, size: 20), - label: Text(_isImporting ? 'Importing...' : 'Import'), - style: OutlinedButton.styleFrom( - foregroundColor: AppTheme.secondaryColor, - side: const BorderSide(color: AppTheme.secondaryColor), - padding: const EdgeInsets.symmetric(vertical: 12), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - ), - ), - ], - ), - ], - ), - ); - } - - Widget _buildBackupCard() { - return Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: AppTheme.cardColor, - borderRadius: BorderRadius.circular(12), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: AppTheme.primaryColor.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(8), - ), - child: const Icon( - Icons.cloud_upload_outlined, - color: AppTheme.primaryColor, - ), - ), - const SizedBox(width: 16), - const Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Remote Backup', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: AppTheme.textPrimary, - ), - ), - Text( - 'Securely backup your workout data to the cloud', - style: TextStyle( - fontSize: 12, - color: AppTheme.textSecondary, - ), - ), - ], - ), - ), - ], - ), - const SizedBox(height: 16), - SizedBox( - width: double.infinity, - child: ElevatedButton( - onPressed: _isBackingUp ? null : _performBackup, - - style: ElevatedButton.styleFrom( - backgroundColor: AppTheme.primaryColor, - padding: const EdgeInsets.symmetric(vertical: 12), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - child: _isBackingUp - ? const SizedBox( - height: 20, - width: 20, - child: CircularProgressIndicator( - strokeWidth: 2, - valueColor: AlwaysStoppedAnimation(Colors.white), - ), - ) - : const Text('Backup Now'), - ), - ), - ], - ), - ); - } -} - -class _UnitButton extends StatelessWidget { - final String label; - final bool selected; - final VoidCallback onTap; - - const _UnitButton({ - required this.label, - required this.selected, - required this.onTap, - }); - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: onTap, - child: AnimatedContainer( - duration: const Duration(milliseconds: 150), - padding: const EdgeInsets.symmetric(vertical: 10), - decoration: BoxDecoration( - color: selected - ? AppTheme.primaryColor.withValues(alpha: 0.2) - : AppTheme.surfaceColor, - borderRadius: BorderRadius.circular(8), - border: Border.all( - color: selected ? AppTheme.primaryColor : AppTheme.surfaceColor, - width: 1.5, - ), - ), - child: Center( - child: Text( - label, - style: TextStyle( - color: selected ? AppTheme.primaryColor : AppTheme.textSecondary, - fontWeight: selected ? FontWeight.bold : FontWeight.normal, - fontSize: 15, - ), - ), - ), - ), - ); - } -} diff --git a/workout-logger/lib/screens/widgets/profile_sections.dart b/workout-logger/lib/screens/widgets/profile_sections.dart index 19b30c3..3abd5f6 100644 --- a/workout-logger/lib/screens/widgets/profile_sections.dart +++ b/workout-logger/lib/screens/widgets/profile_sections.dart @@ -397,62 +397,6 @@ class DataManagementSection extends StatelessWidget { } } -// ── Privacy section ──────────────────────────────────────────────────────────── -class PrivacySection extends StatelessWidget { - const PrivacySection({super.key, required this.settings}); - - final SettingsProvider settings; - - static const _color = AppColors.textSoft; - - @override - Widget build(BuildContext context) { - return _ProfileSection( - icon: Icons.privacy_tip_outlined, - iconColor: _color, - title: 'Privacy', - subtitle: 'Control anonymous usage data', - child: Column( - children: [ - Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Share anonymous usage data', - style: TextStyle(fontFamily: 'Geist', - color: AppColors.textPrimary, - fontSize: 14, - fontWeight: FontWeight.w500, - ), - ), - const SizedBox(height: 2), - Text( - 'Off by default. Install ID, platform, and workout counts — no personal data', - style: TextStyle(fontFamily: 'Geist', - color: AppColors.textMuted, - fontSize: 12, - ), - ), - ], - ), - ), - Switch( - value: settings.analyticsEnabled, - onChanged: (v) => settings.setAnalyticsEnabled(v), - activeThumbColor: _color, - activeTrackColor: _color.withValues(alpha: 0.35), - ), - ], - ), - ], - ), - ); - } -} - // ── About section ───────────────────────────────────────────────────────────── class AboutSection extends StatefulWidget { const AboutSection({super.key, required this.appVersion}); diff --git a/workout-logger/lib/services/api_service.dart b/workout-logger/lib/services/api_service.dart deleted file mode 100644 index 4b93765..0000000 --- a/workout-logger/lib/services/api_service.dart +++ /dev/null @@ -1,173 +0,0 @@ -import 'dart:convert'; -import 'dart:async'; -import 'package:http/http.dart' as http; -import 'package:flutter/foundation.dart'; -import 'package:hive/hive.dart'; -import 'package:uuid/uuid.dart'; - -// Conditional import: uses dart:io on native, stub on web. -import 'platform_stub.dart' if (dart.library.io) 'platform_io.dart'; - -/// Singleton service for communicating with the RepForge analytics backend. -class ApiService { - static const String _baseUrl = String.fromEnvironment( - 'API_URL', - defaultValue: 'https://workout-logger-production-1e93.up.railway.app', - ); - - static final ApiService _instance = ApiService._internal(); - - factory ApiService() => _instance; - ApiService._internal(); - - http.Client _client = http.Client(); - - /// Replace the HTTP client for testing only. - @visibleForTesting - static void setTestClient(http.Client client) { - _instance._client = client; - } - - String? _cachedAppId; - - /// Returns a stable installation ID (UUID v4) persisted in Hive. - Future get userAppId async { - if (_cachedAppId != null) return _cachedAppId!; - // Defensively open the box if it's not already open - final Box box; - if (Hive.isBoxOpen('settings')) { - box = Hive.box('settings'); - } else { - box = await Hive.openBox('settings'); - } - var id = box.get('user_app_id'); - if (id == null) { - id = const Uuid().v4(); - await box.put('user_app_id', id); - } - _cachedAppId = id; - return id; - } - - String get _platform { - if (kIsWeb) return 'web'; - return getPlatformName(); - } - - // ───────── ingest helpers ───────── - - Future sendHeartbeat() async { - try { - final id = await userAppId; - final body = { - 'user_app_id': id, - 'platform': _platform, - 'timestamp': DateTime.now().toUtc().toIso8601String(), - }; - final res = await _client - .post( - Uri.parse('$_baseUrl/heartbeat'), - headers: {'Content-Type': 'application/json'}, - body: jsonEncode(body), - ) - .timeout(const Duration(seconds: 10)); - if (res.statusCode != 200) { - debugPrint('Heartbeat failed: ${res.body}'); - } - } on TimeoutException { - debugPrint('Heartbeat timeout after 10 seconds'); - } catch (e) { - debugPrint('Heartbeat error: $e'); - } - } - - Future trackEvent( - String event, { - Map? metadata, - }) async { - try { - final id = await userAppId; - final body = { - 'user_app_id': id, - 'event': event, - 'platform': _platform, - 'timestamp': DateTime.now().toUtc().toIso8601String(), - 'metadata': ?metadata, - }; - final res = await _client - .post( - Uri.parse('$_baseUrl/event'), - headers: {'Content-Type': 'application/json'}, - body: jsonEncode(body), - ) - .timeout(const Duration(seconds: 10)); - if (res.statusCode != 200) { - debugPrint('Event tracking failed: ${res.body}'); - } - } on TimeoutException { - debugPrint('Event tracking timeout after 10 seconds'); - } catch (e) { - debugPrint('Event tracking error: $e'); - } - } - - Future reportUsage(Map stats) async { - try { - final id = await userAppId; - final payload = { - 'user_app_id': id, - 'total_workouts': stats['totalWorkouts'], - 'weekly_workouts': stats['weeklyWorkouts'], - 'weekly_volume': stats['weeklyVolume'], - 'exercises_this_week': stats['exercisesThisWeek'], - 'platform': _platform, - 'report_date': DateTime.now().toUtc().toIso8601String(), - }; - - final response = await _client - .post( - Uri.parse('$_baseUrl/report'), - headers: {'Content-Type': 'application/json'}, - body: jsonEncode(payload), - ) - .timeout(const Duration(seconds: 10)); - - if (response.statusCode != 200) { - debugPrint('Failed to report usage: ${response.body}'); - } - } on TimeoutException { - debugPrint('Usage report timeout after 10 seconds'); - } catch (e) { - debugPrint('Error reporting usage: $e'); - } - } - - Future backupData(Map data) async { - try { - final id = await userAppId; - - final payload = {'user_app_id': id, ...data}; - - final response = await _client - .post( - Uri.parse('$_baseUrl/backup'), - headers: {'Content-Type': 'application/json'}, - body: jsonEncode(payload), - ) - .timeout(const Duration(minutes: 2)); - - if (response.statusCode == 200) { - return true; - } else { - debugPrint('Failed to backup data: ${response.body}'); - return false; - } - } on TimeoutException { - debugPrint('Backup upload timeout after 2 minutes'); - return false; - } catch (e) { - debugPrint('Error backing up data: $e'); - return false; - } - } -} diff --git a/workout-logger/lib/services/settings_provider.dart b/workout-logger/lib/services/settings_provider.dart index f882b7a..164df92 100644 --- a/workout-logger/lib/services/settings_provider.dart +++ b/workout-logger/lib/services/settings_provider.dart @@ -20,7 +20,6 @@ class SettingsProvider extends ChangeNotifier { String _weeklyInsights = ''; DateTime? _weeklyInsightsDate; bool _showAdvancedMetrics = false; - bool _analyticsEnabled = false; WeightUnit get weightUnit => _weightUnit; double get weightIncrement => _weightIncrement; @@ -35,13 +34,6 @@ class SettingsProvider extends ChangeNotifier { DateTime? get weeklyInsightsDate => _weeklyInsightsDate; bool get showAdvancedMetrics => _showAdvancedMetrics; - /// Whether automatic telemetry (heartbeat/app-open/usage-report) may - /// 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); Future init() async { @@ -68,9 +60,6 @@ class SettingsProvider extends ChangeNotifier { _weeklyInsightsDate = dateStr != null ? DateTime.tryParse(dateStr) : null; final advMetrics = await _storage.getSetting('showAdvancedMetrics'); _showAdvancedMetrics = advMetrics == 'true'; - - final analytics = await _storage.getSetting('analyticsEnabled'); - _analyticsEnabled = analytics == 'true'; } Future setUserName(String name) async { @@ -141,12 +130,6 @@ class SettingsProvider extends ChangeNotifier { notifyListeners(); } - Future setAnalyticsEnabled(bool value) async { - _analyticsEnabled = value; - await _storage.saveSetting('analyticsEnabled', value.toString()); - notifyListeners(); - } - Future saveWeeklyInsights(String insights) async { _weeklyInsights = insights; _weeklyInsightsDate = DateTime.now();