Compare commits
1 Commits
dev
...
accessibil
Author | SHA1 | Date | |
---|---|---|---|
|
d92767bda1 |
459
dialer/lib/features/accessibility/accessibility.dart
Normal file
459
dialer/lib/features/accessibility/accessibility.dart
Normal file
@ -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<AccessibilityPage> {
|
||||||
|
// 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<void> _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<void> _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<bool> 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,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ import 'package:dialer/features/settings/settings.dart';
|
|||||||
import '../../services/contact_service.dart';
|
import '../../services/contact_service.dart';
|
||||||
import 'package:dialer/features/voicemail/voicemail_page.dart';
|
import 'package:dialer/features/voicemail/voicemail_page.dart';
|
||||||
import '../contacts/widgets/contact_modal.dart';
|
import '../contacts/widgets/contact_modal.dart';
|
||||||
|
import 'package:dialer/features/accessibility/accessibility.dart'; // Add this import
|
||||||
|
|
||||||
|
|
||||||
class _MyHomePageState extends State<MyHomePage>
|
class _MyHomePageState extends State<MyHomePage>
|
||||||
@ -233,6 +234,10 @@ class _MyHomePageState extends State<MyHomePage>
|
|||||||
value: 'settings',
|
value: 'settings',
|
||||||
child: Text('Settings'),
|
child: Text('Settings'),
|
||||||
),
|
),
|
||||||
|
const PopupMenuItem<String>(
|
||||||
|
value: 'accessibility',
|
||||||
|
child: Text('Accessibility'),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
onSelected: (String value) {
|
onSelected: (String value) {
|
||||||
if (value == 'settings') {
|
if (value == 'settings') {
|
||||||
@ -241,6 +246,12 @@ class _MyHomePageState extends State<MyHomePage>
|
|||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => const SettingsPage()),
|
builder: (context) => const SettingsPage()),
|
||||||
);
|
);
|
||||||
|
} else if (value == 'accessibility') {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => const AccessibilityPage()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
Loading…
Reference in New Issue
Block a user