aboutsummaryrefslogtreecommitdiffstats
path: root/lib/presentation
diff options
context:
space:
mode:
Diffstat (limited to 'lib/presentation')
-rw-r--r--lib/presentation/common_widget/voice_assistant_button.dart214
-rw-r--r--lib/presentation/router/routes/routes.dart6
-rw-r--r--lib/presentation/screens/home/home.dart11
-rw-r--r--lib/presentation/screens/settings/settings_screens/date_time/date_time_screen.dart2
-rw-r--r--lib/presentation/screens/settings/settings_screens/units/units_screen.dart3
-rw-r--r--lib/presentation/screens/settings/settings_screens/version_info/version_info_screend.dart30
-rw-r--r--lib/presentation/screens/settings/settings_screens/voice_assistant/voice_assistant_screen.dart28
-rw-r--r--lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/stt_model/stt_model_screen.dart120
-rw-r--r--lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_content.dart251
-rw-r--r--lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_settings_list_tile.dart111
-rw-r--r--lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_tile.dart102
-rw-r--r--lib/presentation/screens/settings/widgets/settings_content.dart10
12 files changed, 883 insertions, 5 deletions
diff --git a/lib/presentation/common_widget/voice_assistant_button.dart b/lib/presentation/common_widget/voice_assistant_button.dart
new file mode 100644
index 0000000..2a82a0a
--- /dev/null
+++ b/lib/presentation/common_widget/voice_assistant_button.dart
@@ -0,0 +1,214 @@
+import 'package:flutter_ics_homescreen/export.dart';
+
+class VoiceAssistantButton extends ConsumerStatefulWidget {
+ const VoiceAssistantButton({super.key});
+
+ @override
+ ConsumerState<VoiceAssistantButton> createState() => _VoiceAssistantButtonState();
+}
+
+class _VoiceAssistantButtonState extends ConsumerState<VoiceAssistantButton> with SingleTickerProviderStateMixin {
+ bool _showOverlay = false;
+ late AnimationController _animationController;
+ late Animation<double> _pulseAnimation;
+ int overlayLock = 0;
+
+ @override
+ void initState() {
+ super.initState();
+ _animationController = AnimationController(
+ vsync: this,
+ duration: const Duration(milliseconds: 700),
+ )..stop(); // Stop the animation initially
+
+ _pulseAnimation = Tween<double>(begin: 1.0, end: 1.05).animate(
+ CurvedAnimation(parent: _animationController, curve: Curves.easeInOut),
+ );
+ }
+
+ @override
+ void dispose() {
+ _animationController.dispose();
+ super.dispose();
+ }
+
+ void _onTap() {
+ ref.read(voiceAssistantStateProvider.notifier).updateCommandResponse("");
+ ref.read(voiceAssistantStateProvider.notifier).updateCommand("");
+ bool state = ref.read(voiceAssistantStateProvider.notifier).toggleButtonPressed();
+ if(state){
+ var voiceAgentClient = ref.read(voiceAgentClientProvider);
+ voiceAgentClient.startVoiceAssistant();
+ }
+ }
+
+ void _showAssistantPopup(BuildContext context) {
+ showModalBottomSheet(
+ context: context,
+ isScrollControlled: true,
+ backgroundColor: Colors.transparent,
+ builder: (context) {
+ return Consumer(
+ builder: (context, ref, child) {
+ final String? command = ref.watch(voiceAssistantStateProvider.select((value) => value.command));
+ final String? commandResponse = ref.watch(voiceAssistantStateProvider.select((value) => value.commandResponse));
+ final bool isRecording = ref.watch(voiceAssistantStateProvider.select((value)=>value.isRecording));
+ final bool isProcessing = ref.watch(voiceAssistantStateProvider.select((value)=>value.isCommandProcessing));
+
+ if (isRecording) {
+ _animationController.repeat(reverse: true);
+ } else {
+ _animationController.stop();
+ }
+
+ return Container(
+ height: MediaQuery.of(context).size.height * 0.35,
+ decoration: const BoxDecoration(
+ image: DecorationImage(
+ image: AssetImage('assets/VoiceAssistantBottomSheetBg.png'),
+ fit: BoxFit.cover,
+ ),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.fromLTRB(30, 0, 40, 0),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.end,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ if(!isRecording && !isProcessing)
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16.0),
+ child: Text(
+ command ?? "No Command Detected",
+ textAlign: TextAlign.center,
+ style: const TextStyle(
+ color: Colors.white70,
+ fontSize: 43,
+ fontWeight: FontWeight.w400,
+ ),
+ ),
+ ),
+ SizedBox(
+ height: MediaQuery.of(context).size.height * 0.03
+ ),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16.0),
+ child: Text(
+ commandResponse ?? "No Response",
+ textAlign: TextAlign.center,
+ style: const TextStyle(
+ color: Color.fromRGBO(41, 95, 248, 1),
+ fontSize: 43,
+ fontWeight: FontWeight.w800,
+ ),
+ ),
+ ),
+
+ SizedBox(
+ height: MediaQuery.of(context).size.height * 0.02,
+ ),
+ ],
+ ),
+
+ if(isRecording)
+ Column(
+ children: [
+ const Text("Listening...",
+ style: TextStyle(
+ color: Colors.white70,
+ fontSize: 43,
+ fontWeight: FontWeight.w400,
+ ),
+ ),
+ SizedBox(
+ height: MediaQuery.of(context).size.height*0.02,
+ ),
+ ScaleTransition(
+ scale: _pulseAnimation, // Apply the pulse animation here
+ child: SvgPicture.asset(
+ 'assets/VoiceControlButton.svg',
+ fit: BoxFit.cover,
+ semanticsLabel: 'Voice Assistant',
+ ),
+ ),
+ ],
+ ),
+
+ if(!isRecording && isProcessing)
+ Column(
+ children: [
+ const Text("Processing...",
+ style: TextStyle(
+ color: Colors.white70,
+ fontSize: 43,
+ fontWeight: FontWeight.w400,
+ ),
+ ),
+ SizedBox(
+ height: MediaQuery.of(context).size.height*0.05,
+ ),
+ Lottie.asset(
+ 'animations/LoadingAnimation.json',
+ fit: BoxFit.cover,
+ repeat: true,
+ ),
+ ],
+ ),
+ SizedBox(
+ height: MediaQuery.of(context).size.height * 0.035,
+ ),
+ ],
+ ),
+ ),
+ );
+ },
+ );
+ },
+ ).whenComplete(() {
+ ref.read(voiceAssistantStateProvider.notifier).updateCommandResponse(null);
+ ref.read(voiceAssistantStateProvider.notifier).updateCommand(null);
+ ref.read(voiceAssistantStateProvider.notifier).toggleShowOverlay(false);
+ overlayLock = 0;
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ _showOverlay = ref.watch(voiceAssistantStateProvider.select((value) => value.showOverLay));
+
+ if(_showOverlay){
+ WidgetsBinding.instance!.addPostFrameCallback((_) {
+ if(overlayLock == 0){
+ overlayLock = 1;
+ _showAssistantPopup(context);
+ }
+ });
+ }
+ else if(overlayLock == 1){
+ overlayLock = 0;
+ Navigator.of(context).pop();
+ }
+
+ String svgPath = ref.watch(voiceAssistantStateProvider.select((value) => value.buttonPressed))
+ ? 'assets/VoiceAssistantActive.svg'
+ : 'assets/VoiceAssistantEnabled.svg';
+
+ return Padding(
+ padding: const EdgeInsets.only(left: 8),
+ child: GestureDetector(
+ onTap: _onTap,
+ child: Container(
+ padding: EdgeInsets.zero,
+ child: SvgPicture.asset(
+ svgPath,
+ fit: BoxFit.cover,
+ semanticsLabel: 'Voice Assistant',
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/router/routes/routes.dart b/lib/presentation/router/routes/routes.dart
index 328d495..24eab3a 100644
--- a/lib/presentation/router/routes/routes.dart
+++ b/lib/presentation/router/routes/routes.dart
@@ -3,6 +3,8 @@ import 'package:flutter_ics_homescreen/presentation/screens/settings/settings_sc
import 'package:flutter_ics_homescreen/presentation/screens/settings/settings_screens/date_time/time/time_screen.dart';
import '../../../../export.dart';
+import '../../screens/settings/settings_screens/voice_assistant/voice_assistant_screen.dart';
+import '../../screens/settings/settings_screens/voice_assistant/widgets/stt_model/stt_model_screen.dart';
List<Page<dynamic>> onGenerateAppViewPages(
AppState state,
@@ -57,5 +59,9 @@ List<Page<dynamic>> onGenerateAppViewPages(
return [TimePage.page()];
case AppState.year:
return [SelectYearPage.page()];
+ case AppState.voiceAssistant:
+ return [VoiceAssistantPage.page()];
+ case AppState.sttModel:
+ return [STTModelPage.page()];
}
}
diff --git a/lib/presentation/screens/home/home.dart b/lib/presentation/screens/home/home.dart
index 0ee52ac..6e3e119 100644
--- a/lib/presentation/screens/home/home.dart
+++ b/lib/presentation/screens/home/home.dart
@@ -1,4 +1,6 @@
import 'package:flutter_ics_homescreen/export.dart';
+
+import '../../common_widget/voice_assistant_button.dart';
// import 'package:media_kit_video/media_kit_video.dart';
final bkgImageProvider = Provider((ref) {
@@ -76,6 +78,15 @@ class HomeScreenState extends ConsumerState<HomeScreen> {
height: 500,
child: const VolumeFanControl()),
),
+ // Voice Assistant Button
+ if (appState != AppState.splash && ref.watch(voiceAssistantStateProvider.select((value)=>value.isVoiceAssistantEnable)))
+ Positioned(
+ top: MediaQuery.of(context).size.height * 0.82,
+ child: Container(
+ padding: const EdgeInsets.only(left: 8),
+ child: const VoiceAssistantButton()
+ ),
+ ),
],
),
bottomNavigationBar:
diff --git a/lib/presentation/screens/settings/settings_screens/date_time/date_time_screen.dart b/lib/presentation/screens/settings/settings_screens/date_time/date_time_screen.dart
index caf56a1..acc1541 100644
--- a/lib/presentation/screens/settings/settings_screens/date_time/date_time_screen.dart
+++ b/lib/presentation/screens/settings/settings_screens/date_time/date_time_screen.dart
@@ -16,7 +16,7 @@ class DateTimePage extends ConsumerWidget {
body: Column(
children: [
CommonTitle(
- title: 'Date & Time',
+ title: 'Date & Time',
hasBackButton: true,
onPressed: () {
context.flow<AppState>().update((state) => AppState.settings);
diff --git a/lib/presentation/screens/settings/settings_screens/units/units_screen.dart b/lib/presentation/screens/settings/settings_screens/units/units_screen.dart
index a49546f..ba84471 100644
--- a/lib/presentation/screens/settings/settings_screens/units/units_screen.dart
+++ b/lib/presentation/screens/settings/settings_screens/units/units_screen.dart
@@ -100,7 +100,6 @@ class UnitsTileState extends ConsumerState<UnitsTile> {
children: [
Container(
margin: const EdgeInsets.symmetric(vertical: 8),
- padding: const EdgeInsets.symmetric(vertical: 15),
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
@@ -111,7 +110,7 @@ class UnitsTileState extends ConsumerState<UnitsTile> {
//color: Color(0xFF0D113F),
child: ListTile(
contentPadding:
- const EdgeInsets.symmetric(vertical: 17, horizontal: 24),
+ const EdgeInsets.symmetric(vertical: 32, horizontal: 24),
leading: widget.icon != null
? Icon(
widget.icon,
diff --git a/lib/presentation/screens/settings/settings_screens/version_info/version_info_screend.dart b/lib/presentation/screens/settings/settings_screens/version_info/version_info_screend.dart
index fce1837..c5e571d 100644
--- a/lib/presentation/screens/settings/settings_screens/version_info/version_info_screend.dart
+++ b/lib/presentation/screens/settings/settings_screens/version_info/version_info_screend.dart
@@ -1,6 +1,32 @@
import 'package:flutter_ics_homescreen/export.dart';
class VersionInfoPage extends ConsumerWidget {
+ static String aglVersionFilePath = '/etc/os-release';
+ static String kernelVersionFilePath = '/proc/version';
+
+ static String aglVersion() {
+ try {
+ final file = File(aglVersionFilePath);
+ final data = file.readAsStringSync().split("\n");
+ if (!data[1].contains('Automotive Grade Linux')) {
+ throw 'Non-AGL distribution';
+ }
+ return "AGL: " + data[2].split('"')[1];
+ } catch (_) {
+ return '<AGL version not found>';
+ }
+ }
+
+ static String kernelVersion() {
+ try {
+ final file = File(kernelVersionFilePath);
+ final data = file.readAsStringSync().split(" ");
+ return "Kernel: " + data[2];
+ } catch (_) {
+ return '<Kernel version not found>';
+ }
+ }
+
const VersionInfoPage({super.key});
static Page<void> page() =>
@@ -43,7 +69,7 @@ class VersionInfoPage extends ConsumerWidget {
child: ListTile(
contentPadding: const EdgeInsets.only(top: 50, left: 25),
leading: Text(
- aglVeriosn,
+ aglVersion(),
style: Theme.of(context).textTheme.titleMedium,
),
),
@@ -64,7 +90,7 @@ class VersionInfoPage extends ConsumerWidget {
contentPadding: const EdgeInsets.only(top: 50, left: 25),
leading: Text(
- kernelVeriosn,
+ kernelVersion(),
style: Theme.of(context).textTheme.titleMedium,
),
),
diff --git a/lib/presentation/screens/settings/settings_screens/voice_assistant/voice_assistant_screen.dart b/lib/presentation/screens/settings/settings_screens/voice_assistant/voice_assistant_screen.dart
new file mode 100644
index 0000000..e1f38ae
--- /dev/null
+++ b/lib/presentation/screens/settings/settings_screens/voice_assistant/voice_assistant_screen.dart
@@ -0,0 +1,28 @@
+
+import 'package:flutter_ics_homescreen/export.dart';
+import 'widgets/voice_assistant_content.dart';
+
+class VoiceAssistantPage extends ConsumerWidget{
+ const VoiceAssistantPage({super.key});
+
+ static Page<void> page() => const MaterialPage<void>(child: VoiceAssistantPage());
+ @override
+ Widget build(BuildContext context,WidgetRef ref) {
+
+ return Scaffold(
+ body: Column(
+ children: [
+ CommonTitle(
+ title: 'Voice Assistant',
+ hasBackButton: true,
+ onPressed: () {
+ ref.read(appProvider.notifier).back();
+ },
+ ),
+ Expanded(child: VoiceAssistantContent()),
+ ],
+ ),
+ );
+ }
+}
+
diff --git a/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/stt_model/stt_model_screen.dart b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/stt_model/stt_model_screen.dart
new file mode 100644
index 0000000..614763d
--- /dev/null
+++ b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/stt_model/stt_model_screen.dart
@@ -0,0 +1,120 @@
+import 'package:flutter_ics_homescreen/export.dart';
+
+import '../../../../../../../data/models/voice_assistant_state.dart';
+
+class STTModelPage extends ConsumerWidget {
+ const STTModelPage({super.key});
+
+ static Page<void> page() =>
+ const MaterialPage<void>(child: STTModelPage());
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final SttModel sttModel = ref.watch(voiceAssistantStateProvider.select((value) => value.sttModel));
+
+ return Scaffold(
+ body: Column(
+ children: [
+ CommonTitle(
+ title: 'Speech to Text Model',
+ hasBackButton: true,
+ onPressed: () {
+ context.flow<AppState>().update((state) => AppState.voiceAssistant);
+ },
+ ),
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 144),
+ child: ListView(
+ children: [
+ Container(
+ height: 130,
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ stops: sttModel == SttModel.whisper
+ ? [0, 0.01, 0.8]
+ : [0.1, 1],
+ colors: sttModel == SttModel.whisper
+ ? <Color>[
+ Colors.white,
+ Colors.blue,
+ const Color.fromARGB(16, 41, 98, 255)
+ ]
+ : <Color>[Colors.black, Colors.black12]),
+ ),
+ child: ListTile(
+ minVerticalPadding: 0.0,
+ contentPadding: const EdgeInsets.symmetric(
+ horizontal: 16.0, vertical: 40.0),
+ leading: Text(
+ 'Whisper AI',
+ style: Theme.of(context).textTheme.titleMedium,
+ ),
+ trailing: sttModel == SttModel.whisper
+ ? const Icon(
+ Icons.done,
+ color: AGLDemoColors.periwinkleColor,
+ size: 48,
+ )
+ : null,
+ onTap: () {
+ ref
+ .read(voiceAssistantStateProvider.notifier)
+ .updateSttModel(SttModel.whisper);
+ }),
+ ),
+ const SizedBox(
+ height: 8,
+ ),
+ Container(
+ height: 130,
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ stops: sttModel == SttModel.vosk
+ ? [0, 0.01, 0.8]
+ : [0.1, 1],
+ colors: sttModel == SttModel.vosk
+ ? <Color>[
+ Colors.white,
+ Colors.blue,
+ const Color.fromARGB(16, 41, 98, 255)
+ ]
+ : <Color>[Colors.black, Colors.black12]),
+ ),
+ child: ListTile(
+ minVerticalPadding: 0.0,
+ contentPadding: const EdgeInsets.symmetric(
+ horizontal: 16.0, vertical: 40.0),
+ leading: Text(
+ 'Vosk',
+ style: Theme.of(context).textTheme.titleMedium,
+ ),
+ //title: Text(widget.title),
+ //enabled: isSwitchOn,
+ trailing: sttModel == SttModel.vosk
+ ? const Icon(
+ Icons.done,
+ color: AGLDemoColors.periwinkleColor,
+ size: 48,
+ )
+ : null,
+
+ onTap: () {
+ ref
+ .read(voiceAssistantStateProvider.notifier)
+ .updateSttModel(SttModel.vosk);
+ },
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_content.dart b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_content.dart
new file mode 100644
index 0000000..924a219
--- /dev/null
+++ b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_content.dart
@@ -0,0 +1,251 @@
+import 'package:flutter_ics_homescreen/export.dart';
+
+import 'package:flutter_ics_homescreen/export.dart';
+import 'package:flutter_ics_homescreen/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_tile.dart';
+
+import '../../../../../../core/utils/helpers.dart';
+import '../../../../../../data/models/voice_assistant_state.dart';
+
+@immutable
+class VoiceAssistantContent extends ConsumerWidget {
+ VoiceAssistantContent({Key? key}) : super(key: key);
+ bool isWakeWordMode = false;
+ bool isVoiceAssistantOverlay = false;
+ bool isOnlineMode = false;
+ SttModel sttModel = SttModel.whisper;
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ isWakeWordMode =
+ ref.watch(voiceAssistantStateProvider.select((value) => value.isWakeWordMode));
+ isVoiceAssistantOverlay =
+ ref.watch(voiceAssistantStateProvider.select((value) => value.voiceAssistantOverlay));
+ isOnlineMode =
+ ref.watch(voiceAssistantStateProvider.select((value) => value.isOnlineMode));
+ sttModel =
+ ref.watch(voiceAssistantStateProvider.select((value) => value.sttModel));
+
+ final wakeWordCallback = () {
+ bool status = ref.read(voiceAssistantStateProvider.notifier).toggleWakeWordMode();
+ if(status){
+ var voiceAgentClient = ref.read(voiceAgentClientProvider);
+ voiceAgentClient.startWakeWordDetection();
+ }
+ };
+
+ final voiceAssistantOverlayCallback = () {
+ ref.read(voiceAssistantStateProvider.notifier).toggleVoiceAssistantOverlay();
+ };
+
+ final onlineModeCallback = () {
+ ref.read(voiceAssistantStateProvider.notifier).toggleOnlineMode();
+ };
+
+
+ return Column(
+ children: [
+ Expanded(
+ child: ListView(
+ padding: const EdgeInsets.symmetric(vertical: 50, horizontal: 144),
+ children: [
+ VoiceAssistantTile(
+ icon: Icons.insert_comment_outlined,
+ title: "Voice Assistant Overlay",
+ hasSwitch: true,
+ voidCallback: voiceAssistantOverlayCallback,
+ isSwitchOn: isVoiceAssistantOverlay
+ ),
+ if(ref.watch(voiceAssistantStateProvider.select((value) => value.isOnlineModeAvailable)))
+ VoiceAssistantTile(
+ icon: Icons.cloud_circle,
+ title: "Online Mode",
+ hasSwitch: true,
+ voidCallback: onlineModeCallback,
+ isSwitchOn: isOnlineMode
+ ),
+ VoiceAssistantTile(
+ icon: Icons.mic_none_outlined,
+ title: "Wake Word Mode",
+ hasSwitch: true,
+ voidCallback: wakeWordCallback,
+ isSwitchOn: isWakeWordMode
+ ),
+ if(ref.watch(voiceAssistantStateProvider.select((value) => value.isWakeWordMode)))
+ WakeWordTile(),
+ SttTile(
+ title: " Speech To Text",
+ sttName: sttModel==SttModel.whisper ? "Whisper AI" : "Vosk",
+ hasSwich: true,
+ voidCallback: () async {
+ context
+ .flow<AppState>()
+ .update((next) => AppState.sttModel);
+ }),
+ ],
+ )
+ ),
+ ],
+ );
+ }
+}
+
+class SttTile extends ConsumerStatefulWidget {
+ final IconData? icon;
+ final String title;
+ final String sttName;
+ final bool hasSwich;
+ final VoidCallback voidCallback;
+ final String? image;
+ const SttTile({
+ Key? key,
+ this.icon,
+ required this.title,
+ required this.sttName,
+ required this.hasSwich,
+ required this.voidCallback,
+ this.image,
+ }) : super(key: key);
+
+ @override
+ SttTileState createState() => SttTileState();
+}
+
+class SttTileState extends ConsumerState<SttTile> {
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ children: [
+ Container(
+ margin: const EdgeInsets.symmetric(vertical: 8),
+ padding: const EdgeInsets.symmetric(vertical: 15),
+ decoration: const BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ stops: [0.3, 1],
+ colors: <Color>[Colors.black, Colors.black12]),
+ ),
+ //color: Color(0xFF0D113F),
+ child: ListTile(
+ contentPadding:
+ const EdgeInsets.symmetric(vertical: 17, horizontal: 24),
+ leading: Icon(
+ Icons.transcribe_outlined,
+ color: AGLDemoColors.periwinkleColor,
+ size: 48,
+ ),
+ title: Text(
+ widget.title,
+ style: TextStyle(
+ color: AGLDemoColors.periwinkleColor,
+ shadows: [
+ Helpers.dropShadowRegular,
+ ],
+ fontSize: 40),
+ ),
+ trailing: Row(
+ mainAxisSize: MainAxisSize.min,
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ Text(
+ widget.sttName,
+ style: TextStyle(
+ color: AGLDemoColors.periwinkleColor,
+ shadows: [
+ Helpers.dropShadowRegular,
+ ],
+ fontSize: 40,
+ ),
+ ),
+ const SizedBox(
+ width: 24,
+ ),
+ const Icon(
+ Icons.arrow_forward_ios,
+ color: AGLDemoColors.periwinkleColor,
+ size: 48,
+ ),
+ ],
+ ),
+ onTap: widget.voidCallback,
+ ),
+ ),
+ const SizedBox(
+ height: 8,
+ )
+ ],
+ );
+ }
+}
+
+
+
+class WakeWordTile extends ConsumerStatefulWidget {
+ const WakeWordTile({Key? key}) : super(key: key);
+
+ @override
+ WakeWordTileState createState() => WakeWordTileState();
+}
+
+class WakeWordTileState extends ConsumerState<WakeWordTile> {
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ children: [
+ Container(
+ margin: const EdgeInsets.symmetric(vertical: 8),
+ padding: const EdgeInsets.symmetric(vertical: 15),
+ decoration: const BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ stops: [0.3, 1],
+ colors: <Color>[Colors.black, Colors.black12]),
+ ),
+ //color: Color(0xFF0D113F),
+ child: ListTile(
+ contentPadding:
+ const EdgeInsets.symmetric(vertical: 17, horizontal: 24),
+ leading: Icon(
+ Icons.mic_none_outlined,
+ color: AGLDemoColors.periwinkleColor,
+ size: 48,
+ ),
+ title: Text(
+ "Wake Word",
+ style: TextStyle(
+ color: AGLDemoColors.periwinkleColor,
+ shadows: [
+ Helpers.dropShadowRegular,
+ ],
+ fontSize: 40),
+ ),
+ trailing: Row(
+ mainAxisSize: MainAxisSize.min,
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ Text(
+ ref.watch(voiceAssistantStateProvider.select((value) => value.wakeWord)) ?? "Not Set",
+ style: TextStyle(
+ color: AGLDemoColors.periwinkleColor,
+ shadows: [
+ Helpers.dropShadowRegular,
+ ],
+ fontSize: 40,
+ ),
+ ),
+ const SizedBox(
+ width: 50,
+ ),
+
+ ],
+ ),
+ ),
+ ),
+ const SizedBox(
+ height: 8,
+ )
+ ],
+ );
+ }
+}
diff --git a/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_settings_list_tile.dart b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_settings_list_tile.dart
new file mode 100644
index 0000000..ee0365a
--- /dev/null
+++ b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_settings_list_tile.dart
@@ -0,0 +1,111 @@
+import 'package:flutter_ics_homescreen/export.dart';
+import 'package:protos/val_api.dart';
+
+class VoiceAssistantSettingsTile extends ConsumerStatefulWidget {
+ final IconData icon;
+ final String title;
+ final bool hasSwich;
+ final VoidCallback voidCallback;
+ const VoiceAssistantSettingsTile({
+ Key? key,
+ required this.icon,
+ required this.title,
+ required this.hasSwich,
+ required this.voidCallback,
+ }) : super(key: key);
+
+ @override
+ VoiceAssistantSettingsTileState createState() => VoiceAssistantSettingsTileState();
+}
+
+class VoiceAssistantSettingsTileState extends ConsumerState<VoiceAssistantSettingsTile> {
+ bool isSwitchOn = true;
+ @override
+ Widget build(BuildContext context) {
+ isSwitchOn = ref.watch(voiceAssistantStateProvider.select((voiceAssistant) => voiceAssistant.isVoiceAssistantEnable));
+ return Column(
+ children: [
+ GestureDetector(
+ onTap: isSwitchOn ? widget.voidCallback : () {},
+ child: Container(
+ height: 130,
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ stops: isSwitchOn ? [0.3, 1] : [0.8, 1],
+ colors: isSwitchOn
+ ? <Color>[Colors.black, Colors.black12]
+ : <Color>[
+ const Color.fromARGB(50, 0, 0, 0),
+ Colors.transparent
+ ],
+ ),
+ ),
+ child: Card(
+ color: Colors.transparent,
+ elevation: 5,
+ child: Padding(
+ padding:
+ const EdgeInsets.symmetric(vertical: 0, horizontal: 24),
+ child: Row(
+ children: [
+ Icon(
+ widget.icon,
+ color: AGLDemoColors.periwinkleColor,
+ size: 48,
+ ),
+ const SizedBox(width: 24),
+ Expanded(
+ child: Text(
+ widget.title,
+ style: const TextStyle(fontSize: 40),
+ ),
+ ),
+ widget.hasSwich
+ ? Container(
+ width: 126,
+ height: 80,
+ decoration: const ShapeDecoration(
+ color:
+ AGLDemoColors.gradientBackgroundDarkColor,
+ shape: StadiumBorder(
+ side: BorderSide(
+ color: Color(0xFF5477D4),
+ width: 4,
+ )),
+ ),
+ child: FittedBox(
+ fit: BoxFit.fill,
+ child: Switch(
+ value: isSwitchOn,
+ onChanged: (bool value) async {
+ var voiceAgentClient = ref.read(voiceAgentClientProvider);
+ ServiceStatus status = await voiceAgentClient.checkServiceStatus();
+ ref.read(voiceAssistantStateProvider.notifier).toggleVoiceAssistant(status);
+ setState(() {
+ isSwitchOn = value;
+ });
+ // This is called when the user toggles the switch.
+ },
+ inactiveTrackColor: Colors.transparent,
+ activeTrackColor: Colors.transparent,
+ thumbColor:
+ MaterialStateProperty.all<Color>(
+ AGLDemoColors.periwinkleColor)),
+ ),
+ )
+ : const SizedBox(),
+ ],
+ ),
+ ),
+ )
+ ),
+ ),
+ const SizedBox(
+ height: 8,
+ )
+ ],
+ );
+ }
+}
diff --git a/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_tile.dart b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_tile.dart
new file mode 100644
index 0000000..d4bdd48
--- /dev/null
+++ b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_tile.dart
@@ -0,0 +1,102 @@
+import 'package:flutter_ics_homescreen/export.dart';
+
+class VoiceAssistantTile extends ConsumerStatefulWidget {
+ final IconData icon;
+ final String title;
+ final bool hasSwitch;
+ final VoidCallback voidCallback;
+ final bool isSwitchOn;
+ const VoiceAssistantTile({super.key, required this.icon, required this.title, required this.hasSwitch, required this.voidCallback,required this.isSwitchOn});
+
+ @override
+ ConsumerState<VoiceAssistantTile> createState() => _VoiceAssistantTileState();
+}
+
+class _VoiceAssistantTileState extends ConsumerState<VoiceAssistantTile> {
+ bool isSwitchOn = true;
+ @override
+ Widget build(BuildContext context) {
+ isSwitchOn = widget.isSwitchOn;
+ return Column(
+ children: [
+ Container(
+ height: 130,
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ stops: isSwitchOn ? [0.3, 1] : [0.8, 1],
+ colors: isSwitchOn
+ ? <Color>[Colors.black, Colors.black12]
+ : <Color>[
+ const Color.fromARGB(50, 0, 0, 0),
+ Colors.transparent
+ ],
+ ),
+ ),
+ child: Card(
+
+ color: Colors.transparent,
+ elevation: 5,
+ child: Padding(
+ padding:
+ const EdgeInsets.symmetric(vertical: 0, horizontal: 24),
+ child: Row(
+ children: [
+ Icon(
+ widget.icon,
+ color: AGLDemoColors.periwinkleColor,
+ size: 48,
+ ),
+ const SizedBox(width: 24),
+ Expanded(
+ child: Text(
+ widget.title,
+ style: const TextStyle(fontSize: 40),
+ ),
+ ),
+ widget.hasSwitch
+ ? Container(
+ width: 126,
+ height: 80,
+ decoration: const ShapeDecoration(
+ color:
+ AGLDemoColors.gradientBackgroundDarkColor,
+ shape: StadiumBorder(
+ side: BorderSide(
+ color: Color(0xFF5477D4),
+ width: 4,
+ )),
+ ),
+ child: FittedBox(
+ fit: BoxFit.fill,
+ child: Switch(
+ value: isSwitchOn,
+ onChanged: (bool value) {
+ setState(() {
+ isSwitchOn = value;
+ });
+ widget.voidCallback();
+ },
+ inactiveTrackColor: Colors.transparent,
+ activeTrackColor: Colors.transparent,
+ thumbColor:
+ MaterialStateProperty.all<Color>(
+ AGLDemoColors.periwinkleColor)),
+ ),
+ )
+ : const SizedBox(),
+ ],
+ ),
+ ),
+ )
+ ),
+ const SizedBox(
+ height: 14,
+ )
+ ],
+ );
+ }
+}
+
+
diff --git a/lib/presentation/screens/settings/widgets/settings_content.dart b/lib/presentation/screens/settings/widgets/settings_content.dart
index 6d0df50..b43c07b 100644
--- a/lib/presentation/screens/settings/widgets/settings_content.dart
+++ b/lib/presentation/screens/settings/widgets/settings_content.dart
@@ -1,6 +1,7 @@
import 'package:flutter_ics_homescreen/export.dart';
import '../../../custom_icons/custom_icons.dart';
+import '../settings_screens/voice_assistant/widgets/voice_assistant_settings_list_tile.dart';
class Settings extends ConsumerWidget {
const Settings({
@@ -55,6 +56,15 @@ class Settings extends ConsumerWidget {
voidCallback: () {
ref.read(appProvider.notifier).update(AppState.audioSettings);
}),
+ if(ref.watch(appConfigProvider).enableVoiceAssistant)
+ VoiceAssistantSettingsTile(
+ icon: Icons.keyboard_voice_outlined,
+ title: "Voice Assistant",
+ hasSwich: true,
+ voidCallback: (){
+ ref.read(appProvider.notifier).update(AppState.voiceAssistant);
+ }
+ ),
SettingsTile(
icon: Icons.person_2_outlined,
title: 'Profiles',