diff --git a/dialer/lib/features/accessibility/accessibility.dart b/dialer/lib/features/accessibility/accessibility.dart new file mode 100644 index 0000000..a9fe9c9 --- /dev/null +++ b/dialer/lib/features/accessibility/accessibility.dart @@ -0,0 +1,459 @@ +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class AccessibilityPage extends StatefulWidget { + const AccessibilityPage({Key? key}) : super(key: key); + + @override + _AccessibilityPageState createState() => _AccessibilityPageState(); +} + +class _AccessibilityPageState extends State { + // Font size setting + double _fontSize = 1.0; + + // Toggle settings + bool _highContrastMode = false; + bool _reduceAnimations = false; + bool _enableVibration = true; + bool _enableScreenReader = false; + bool _useSimpleLayout = false; + bool _autoAnswerCalls = false; + + // Call display timeout + int _callDisplayTimeout = 30; // seconds + + bool _isLoading = true; + + @override + void initState() { + super.initState(); + _loadSettings(); + } + + Future _loadSettings() async { + final prefs = await SharedPreferences.getInstance(); + + setState(() { + _fontSize = prefs.getDouble('accessibility_font_size') ?? 1.0; + _highContrastMode = prefs.getBool('accessibility_high_contrast') ?? false; + _reduceAnimations = prefs.getBool('accessibility_reduce_animations') ?? false; + _enableVibration = prefs.getBool('accessibility_vibration') ?? true; + _enableScreenReader = prefs.getBool('accessibility_screen_reader') ?? false; + _useSimpleLayout = prefs.getBool('accessibility_simple_layout') ?? false; + _autoAnswerCalls = prefs.getBool('accessibility_auto_answer') ?? false; + _callDisplayTimeout = prefs.getInt('accessibility_call_timeout') ?? 30; + _isLoading = false; + }); + } + + Future _saveSettings() async { + final prefs = await SharedPreferences.getInstance(); + + await prefs.setDouble('accessibility_font_size', _fontSize); + await prefs.setBool('accessibility_high_contrast', _highContrastMode); + await prefs.setBool('accessibility_reduce_animations', _reduceAnimations); + await prefs.setBool('accessibility_vibration', _enableVibration); + await prefs.setBool('accessibility_screen_reader', _enableScreenReader); + await prefs.setBool('accessibility_simple_layout', _useSimpleLayout); + await prefs.setBool('accessibility_auto_answer', _autoAnswerCalls); + await prefs.setInt('accessibility_call_timeout', _callDisplayTimeout); + + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Accessibility settings saved')), + ); + } + + String _getFontSizeLabel() { + if (_fontSize <= 0.8) return 'Small'; + if (_fontSize <= 1.0) return 'Default'; + if (_fontSize <= 1.2) return 'Large'; + if (_fontSize <= 1.4) return 'Extra Large'; + return 'Maximum'; + } + + String _getCallTimeoutLabel() { + if (_callDisplayTimeout <= 15) return '15 seconds'; + if (_callDisplayTimeout <= 30) return '30 seconds'; + if (_callDisplayTimeout <= 45) return '45 seconds'; + if (_callDisplayTimeout <= 60) return '1 minute'; + return '${_callDisplayTimeout} seconds'; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.black, + appBar: AppBar( + title: const Text('Accessibility'), + backgroundColor: Colors.black, + elevation: 0, + actions: [ + TextButton( + onPressed: _saveSettings, + child: const Text('Save', style: TextStyle(color: Colors.blue)), + ), + ], + ), + body: _isLoading + ? const Center(child: CircularProgressIndicator()) + : SingleChildScrollView( + padding: const EdgeInsets.all(20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Header with icon + Center( + child: Column( + children: [ + Container( + width: 80, + height: 80, + decoration: BoxDecoration( + color: Colors.blue, + borderRadius: BorderRadius.circular(20), + ), + child: const Icon( + Icons.accessibility_new, + size: 50, + color: Colors.white, + ), + ), + const SizedBox(height: 16), + const Text( + 'Accessibility Settings', + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + const SizedBox(height: 8), + Text( + 'Customize your experience', + style: TextStyle( + color: Colors.grey[400], + fontSize: 14, + ), + ), + ], + ), + ), + + const SizedBox(height: 32), + + // Text Size Settings + const _SectionHeader(title: 'Display Settings'), + _ContentCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + 'Text Size', + style: TextStyle(color: Colors.white, fontSize: 16), + ), + Text( + _getFontSizeLabel(), + style: TextStyle(color: Colors.blue, fontSize: 16), + ), + ], + ), + const SizedBox(height: 8), + Slider( + value: _fontSize, + min: 0.8, + max: 1.6, + divisions: 4, + label: _getFontSizeLabel(), + onChanged: (value) { + setState(() { + _fontSize = value; + }); + }, + ), + const SizedBox(height: 16), + _ToggleOption( + title: 'High Contrast Mode', + subtitle: 'Increase color contrast for better visibility', + value: _highContrastMode, + onChanged: (value) { + setState(() { + _highContrastMode = value; + }); + }, + ), + _ToggleOption( + title: 'Reduce Animations', + subtitle: 'Minimize motion effects throughout the app', + value: _reduceAnimations, + onChanged: (value) { + setState(() { + _reduceAnimations = value; + }); + }, + ), + _ToggleOption( + title: 'Simple Layout', + subtitle: 'Use a simplified interface with larger touch targets', + value: _useSimpleLayout, + onChanged: (value) { + setState(() { + _useSimpleLayout = value; + }); + }, + ), + ], + ), + ), + + const SizedBox(height: 24), + + // Assistive Technology + const _SectionHeader(title: 'Assistive Technology'), + _ContentCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _ToggleOption( + title: 'Enable Screen Reader', + subtitle: 'Works with TalkBack and VoiceOver', + value: _enableScreenReader, + onChanged: (value) { + setState(() { + _enableScreenReader = value; + }); + }, + ), + _ToggleOption( + title: 'Haptic Feedback', + subtitle: 'Vibration feedback for interactions', + value: _enableVibration, + onChanged: (value) { + setState(() { + _enableVibration = value; + }); + }, + ), + ], + ), + ), + + const SizedBox(height: 24), + + // Call Settings + const _SectionHeader(title: 'Call Settings'), + _ContentCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _ToggleOption( + title: 'Auto-Answer Calls', + subtitle: 'Answer incoming calls automatically after timeout', + value: _autoAnswerCalls, + onChanged: (value) { + setState(() { + _autoAnswerCalls = value; + }); + }, + ), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + 'Call Display Timeout', + style: TextStyle(color: Colors.white, fontSize: 16), + ), + Text( + _getCallTimeoutLabel(), + style: TextStyle(color: Colors.blue, fontSize: 16), + ), + ], + ), + const SizedBox(height: 8), + Slider( + value: _callDisplayTimeout.toDouble(), + min: 15, + max: 60, + divisions: 3, + label: _getCallTimeoutLabel(), + onChanged: (value) { + setState(() { + _callDisplayTimeout = value.toInt(); + }); + }, + ), + ], + ), + ), + + const SizedBox(height: 24), + + // Additional Resources + const _SectionHeader(title: 'Additional Resources'), + _ContentCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _LinkButton( + icon: Icons.help_outline, + text: 'Accessibility Help', + onTap: () { + // Add navigation or link to help page + }, + ), + const SizedBox(height: 12), + _LinkButton( + icon: Icons.feedback_outlined, + text: 'Send Accessibility Feedback', + onTap: () { + // Add feedback functionality + }, + ), + ], + ), + ), + + const SizedBox(height: 40), + ], + ), + ), + ); + } +} + +class _SectionHeader extends StatelessWidget { + final String title; + + const _SectionHeader({Key? key, required this.title}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Text( + title, + style: const TextStyle( + color: Colors.blue, + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ); + } +} + +class _ContentCard extends StatelessWidget { + final Widget child; + + const _ContentCard({Key? key, required this.child}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.grey[900], + borderRadius: BorderRadius.circular(12), + ), + child: child, + ); + } +} + +class _ToggleOption extends StatelessWidget { + final String title; + final String subtitle; + final bool value; + final ValueChanged onChanged; + + const _ToggleOption({ + Key? key, + required this.title, + required this.subtitle, + required this.value, + required this.onChanged, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: const TextStyle( + color: Colors.white, + fontSize: 16, + ), + ), + const SizedBox(height: 4), + Text( + subtitle, + style: const TextStyle( + color: Colors.white70, + fontSize: 14, + ), + ), + ], + ), + ), + Switch( + value: value, + onChanged: onChanged, + activeColor: Colors.blue, + ), + ], + ), + ); + } +} + +class _LinkButton extends StatelessWidget { + final IconData icon; + final String text; + final VoidCallback onTap; + + const _LinkButton({ + Key? key, + required this.icon, + required this.text, + required this.onTap, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(8), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row( + children: [ + Icon(icon, color: Colors.blue), + const SizedBox(width: 16), + Text( + text, + style: const TextStyle(color: Colors.white), + ), + const Spacer(), + const Icon( + Icons.arrow_forward_ios, + color: Colors.white70, + size: 16, + ), + ], + ), + ), + ); + } +} diff --git a/dialer/lib/features/home/home_page.dart b/dialer/lib/features/home/home_page.dart index 65adaa8..7322791 100644 --- a/dialer/lib/features/home/home_page.dart +++ b/dialer/lib/features/home/home_page.dart @@ -9,6 +9,7 @@ import 'package:dialer/features/settings/settings.dart'; import '../../services/contact_service.dart'; import 'package:dialer/features/voicemail/voicemail_page.dart'; import '../contacts/widgets/contact_modal.dart'; +import 'package:dialer/features/accessibility/accessibility.dart'; // Add this import class _MyHomePageState extends State @@ -233,6 +234,10 @@ class _MyHomePageState extends State value: 'settings', child: Text('Settings'), ), + const PopupMenuItem( + value: 'accessibility', + child: Text('Accessibility'), + ), ], onSelected: (String value) { if (value == 'settings') { @@ -241,6 +246,12 @@ class _MyHomePageState extends State MaterialPageRoute( builder: (context) => const SettingsPage()), ); + } else if (value == 'accessibility') { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const AccessibilityPage()), + ); } }, ),