Voice Mode improvements
This commit is contained in:
parent
4d90f8611b
commit
d768294458
|
@ -433,11 +433,22 @@
|
||||||
"description": "Text displayed while loading voice permissions",
|
"description": "Text displayed while loading voice permissions",
|
||||||
"context": "Visible in the settings view"
|
"context": "Visible in the settings view"
|
||||||
},
|
},
|
||||||
|
"settingsVoiceTtsNotSupported": "Text-to-speech not supported",
|
||||||
|
"@settingsVoiceTtsNotSupported": {
|
||||||
|
"description": "Text displayed when text-to-speech is not supported",
|
||||||
|
"context": "Visible in the settings view"
|
||||||
|
},
|
||||||
|
"settingsVoiceTtsNotSupportedDescription": "Text-to-speech services are not supported for the selected language. Select a different language in the language drawer to reenable them.\nOther services like voice recognition and AI thinking will still work as usual, but interaction might not be as fluent.",
|
||||||
"settingsVoicePermissionNot": "Permissions not granted",
|
"settingsVoicePermissionNot": "Permissions not granted",
|
||||||
"@settingsVoicePermissionNot": {
|
"@settingsVoicePermissionNot": {
|
||||||
"description": "Text displayed when voice permissions are not granted",
|
"description": "Text displayed when voice permissions are not granted",
|
||||||
"context": "Visible in the settings view"
|
"context": "Visible in the settings view"
|
||||||
},
|
},
|
||||||
|
"settingsVoiceNotEnabled": "Voice mode not enabled",
|
||||||
|
"@settingsVoiceNotEnabled": {
|
||||||
|
"description": "Text displayed when voice mode is not enabled",
|
||||||
|
"context": "Visible in the settings view"
|
||||||
|
},
|
||||||
"settingsVoiceNotSupported": "Voice mode not supported",
|
"settingsVoiceNotSupported": "Voice mode not supported",
|
||||||
"@settingsVoiceNotSupported": {
|
"@settingsVoiceNotSupported": {
|
||||||
"description": "Text displayed when voice mode is not supported",
|
"description": "Text displayed when voice mode is not supported",
|
||||||
|
|
|
@ -85,7 +85,7 @@ class _ScreenVoiceState extends State<ScreenVoice> {
|
||||||
text = "";
|
text = "";
|
||||||
|
|
||||||
speech.listen(
|
speech.listen(
|
||||||
localeId: (prefs!.getString("voiceLanguage") ?? ""),
|
localeId: (prefs!.getString("voiceLanguage") ?? "en-US"),
|
||||||
listenOptions:
|
listenOptions:
|
||||||
stt.SpeechListenOptions(listenMode: stt.ListenMode.dictation),
|
stt.SpeechListenOptions(listenMode: stt.ListenMode.dictation),
|
||||||
onResult: (result) {
|
onResult: (result) {
|
||||||
|
@ -172,91 +172,93 @@ class _ScreenVoiceState extends State<ScreenVoice> {
|
||||||
lightHaptic();
|
lightHaptic();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (done &&
|
// var volume = await VolumeController().getVolume();
|
||||||
(await voice.getLanguages as List).contains(
|
// var voicesTmp1 = await voice.getLanguages;
|
||||||
(prefs!.getString("voiceLanguage") ?? "en_US")
|
// var voices = jsonEncode(voicesTmp1);
|
||||||
.replaceAll("_", "-"))) {
|
// var isVoiceAvailable = (await voice.isLanguageAvailable(
|
||||||
|
// (prefs!.getString("voiceLanguage") ?? "en_US")
|
||||||
|
// .replaceAll("_", "-")))
|
||||||
|
// .toString();
|
||||||
|
// var voices2Tmp1 = await speech.locales();
|
||||||
|
// var voices2Tmp2 = [];
|
||||||
|
// for (var voice in voices2Tmp1) {
|
||||||
|
// voices2Tmp2.add(voice.localeId.replaceAll("_", "-"));
|
||||||
|
// }
|
||||||
|
// var voices2 = jsonEncode(voices2Tmp2);
|
||||||
|
// await showDialog(
|
||||||
|
// // ignore: use_build_context_synchronously
|
||||||
|
// context: context,
|
||||||
|
// builder: (context) {
|
||||||
|
// return Dialog.fullscreen(
|
||||||
|
// child: ListView(children: [
|
||||||
|
// const Row(
|
||||||
|
// crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
// mainAxisSize: MainAxisSize.max,
|
||||||
|
// children: [
|
||||||
|
// Expanded(child: Divider(color: Colors.red)),
|
||||||
|
// SizedBox(width: 8),
|
||||||
|
// Text("START", style: TextStyle(color: Colors.red)),
|
||||||
|
// SizedBox(width: 8),
|
||||||
|
// Expanded(child: Divider(color: Colors.red))
|
||||||
|
// ]),
|
||||||
|
// Text((prefs!.getString("voiceLanguage") ?? "en_US")
|
||||||
|
// .replaceAll("_", "-")),
|
||||||
|
// const Divider(),
|
||||||
|
// Text(volume.toString()),
|
||||||
|
// const Divider(),
|
||||||
|
// Text(voices),
|
||||||
|
// const Divider(),
|
||||||
|
// Text(voicesTmp1
|
||||||
|
// .contains((prefs!.getString("voiceLanguage") ?? "en_US")
|
||||||
|
// .replaceAll("_", "-"))
|
||||||
|
// .toString()),
|
||||||
|
// const Divider(),
|
||||||
|
// Text(isVoiceAvailable),
|
||||||
|
// const Divider(),
|
||||||
|
// Text(voices2),
|
||||||
|
// const Divider(),
|
||||||
|
// Text(voices2Tmp2
|
||||||
|
// .contains((prefs!.getString("voiceLanguage") ?? "en_US")
|
||||||
|
// .replaceAll("_", "-"))
|
||||||
|
// .toString()),
|
||||||
|
// const Divider(),
|
||||||
|
// Text(speech.isAvailable.toString()),
|
||||||
|
// const Row(
|
||||||
|
// crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
// mainAxisSize: MainAxisSize.max,
|
||||||
|
// children: [
|
||||||
|
// Expanded(child: Divider(color: Colors.red)),
|
||||||
|
// SizedBox(width: 8),
|
||||||
|
// Text("END", style: TextStyle(color: Colors.red)),
|
||||||
|
// SizedBox(width: 8),
|
||||||
|
// Expanded(child: Divider(color: Colors.red))
|
||||||
|
// ])
|
||||||
|
// ]));
|
||||||
|
// });
|
||||||
|
|
||||||
|
if (done) {
|
||||||
aiThinking = false;
|
aiThinking = false;
|
||||||
heavyHaptic();
|
heavyHaptic();
|
||||||
voice.setLanguage((prefs!.getString("voiceLanguage") ?? "en_US")
|
|
||||||
.replaceAll("_", "-"));
|
|
||||||
voice.setSpeechRate(0.6);
|
|
||||||
voice.setCompletionHandler(() async {
|
|
||||||
speaking = false;
|
|
||||||
try {
|
|
||||||
setState(() {});
|
|
||||||
} catch (_) {}
|
|
||||||
process();
|
|
||||||
});
|
|
||||||
var tmp = aiText;
|
|
||||||
tmp.replaceAll("-", ".");
|
|
||||||
tmp.replaceAll("*", ".");
|
|
||||||
|
|
||||||
// var volume = await VolumeController().getVolume();
|
if ((await voice.getLanguages as List).contains(
|
||||||
// var voicesTmp1 = await voice.getLanguages;
|
(prefs!.getString("voiceLanguage") ?? "en_US")
|
||||||
// var voices = jsonEncode(voicesTmp1);
|
.replaceAll("_", "-"))) {
|
||||||
// var isVoiceAvailable = (await voice.isLanguageAvailable(
|
voice.setLanguage((prefs!.getString("voiceLanguage") ?? "en_US")
|
||||||
// (prefs!.getString("voiceLanguage") ?? "en_US")
|
.replaceAll("_", "-"));
|
||||||
// .replaceAll("_", "-")))
|
voice.setSpeechRate(0.6);
|
||||||
// .toString();
|
voice.setCompletionHandler(() async {
|
||||||
// var voices2Tmp1 = await speech.locales();
|
speaking = false;
|
||||||
// var voices2Tmp2 = [];
|
try {
|
||||||
// for (var voice in voices2Tmp1) {
|
setState(() {});
|
||||||
// voices2Tmp2.add(voice.localeId.replaceAll("_", "-"));
|
} catch (_) {}
|
||||||
// }
|
process();
|
||||||
// var voices2 = jsonEncode(voices2Tmp2);
|
});
|
||||||
// await showDialog(
|
var tmp = aiText;
|
||||||
// // ignore: use_build_context_synchronously
|
tmp.replaceAll("-", ".");
|
||||||
// context: context,
|
tmp.replaceAll("*", ".");
|
||||||
// builder: (context) {
|
|
||||||
// return Dialog.fullscreen(
|
|
||||||
// child: ListView(children: [
|
|
||||||
// const Row(
|
|
||||||
// crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
// mainAxisSize: MainAxisSize.max,
|
|
||||||
// children: [
|
|
||||||
// Expanded(child: Divider(color: Colors.red)),
|
|
||||||
// SizedBox(width: 8),
|
|
||||||
// Text("START", style: TextStyle(color: Colors.red)),
|
|
||||||
// SizedBox(width: 8),
|
|
||||||
// Expanded(child: Divider(color: Colors.red))
|
|
||||||
// ]),
|
|
||||||
// Text((prefs!.getString("voiceLanguage") ?? "en_US")
|
|
||||||
// .replaceAll("_", "-")),
|
|
||||||
// const Divider(),
|
|
||||||
// Text(volume.toString()),
|
|
||||||
// const Divider(),
|
|
||||||
// Text(voices),
|
|
||||||
// const Divider(),
|
|
||||||
// Text(voicesTmp1
|
|
||||||
// .contains((prefs!.getString("voiceLanguage") ?? "en_US")
|
|
||||||
// .replaceAll("_", "-"))
|
|
||||||
// .toString()),
|
|
||||||
// const Divider(),
|
|
||||||
// Text(isVoiceAvailable),
|
|
||||||
// const Divider(),
|
|
||||||
// Text(voices2),
|
|
||||||
// const Divider(),
|
|
||||||
// Text(voices2Tmp2
|
|
||||||
// .contains((prefs!.getString("voiceLanguage") ?? "en_US")
|
|
||||||
// .replaceAll("_", "-"))
|
|
||||||
// .toString()),
|
|
||||||
// const Divider(),
|
|
||||||
// Text(speech.isAvailable.toString()),
|
|
||||||
// const Row(
|
|
||||||
// crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
// mainAxisSize: MainAxisSize.max,
|
|
||||||
// children: [
|
|
||||||
// Expanded(child: Divider(color: Colors.red)),
|
|
||||||
// SizedBox(width: 8),
|
|
||||||
// Text("END", style: TextStyle(color: Colors.red)),
|
|
||||||
// SizedBox(width: 8),
|
|
||||||
// Expanded(child: Divider(color: Colors.red))
|
|
||||||
// ])
|
|
||||||
// ]));
|
|
||||||
// });
|
|
||||||
|
|
||||||
voice.speak(tmp);
|
voice.speak(tmp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
addToSystem: (prefs!.getBool("voiceLimitLanguage") ?? true)
|
addToSystem: (prefs!.getBool("voiceLimitLanguage") ?? true)
|
||||||
|
|
|
@ -27,20 +27,35 @@ class _ScreenSettingsVoiceState extends State<ScreenSettingsVoice> {
|
||||||
Iterable<String> languageOptionIds = [];
|
Iterable<String> languageOptionIds = [];
|
||||||
Iterable<String> languageOptions = [];
|
Iterable<String> languageOptions = [];
|
||||||
|
|
||||||
|
List voiceLanguageOptionsAvailable = [];
|
||||||
|
List voiceLanguageOptions = [];
|
||||||
|
|
||||||
|
bool dialogMustLoad = true;
|
||||||
|
|
||||||
|
void load() async {
|
||||||
|
var tmp = await speech.locales();
|
||||||
|
languageOptionIds = tmp.map((e) => e.localeId);
|
||||||
|
languageOptions = tmp.map((e) => e.name);
|
||||||
|
|
||||||
|
permissionRecord = await Permission.microphone.isGranted;
|
||||||
|
permissionBluetooth = await Permission.bluetoothConnect.isGranted;
|
||||||
|
permissionLoading = false;
|
||||||
|
|
||||||
|
voiceLanguageOptions = await voice.getLanguages as List;
|
||||||
|
|
||||||
|
for (int i = 0; i < languageOptionIds.length; i++) {
|
||||||
|
if (voiceLanguageOptions
|
||||||
|
.contains(languageOptionIds.elementAt(i).replaceAll("_", "-"))) {
|
||||||
|
voiceLanguageOptionsAvailable.add(languageOptionIds.elementAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
void load() async {
|
|
||||||
var tmp = await speech.locales();
|
|
||||||
languageOptionIds = tmp.map((e) => e.localeId);
|
|
||||||
languageOptions = tmp.map((e) => e.name);
|
|
||||||
|
|
||||||
permissionRecord = await Permission.microphone.isGranted;
|
|
||||||
permissionBluetooth = await Permission.bluetoothConnect.isGranted;
|
|
||||||
permissionLoading = false;
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
|
|
||||||
load();
|
load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,25 +71,48 @@ class _ScreenSettingsVoiceState extends State<ScreenSettingsVoice> {
|
||||||
child: Column(children: [
|
child: Column(children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ListView(children: [
|
child: ListView(children: [
|
||||||
// const SizedBox(height: 8),
|
(permissionLoading ||
|
||||||
((prefs!.getBool("voiceModeEnabled") ?? false) ||
|
|
||||||
permissionLoading ||
|
|
||||||
(permissionBluetooth &&
|
(permissionBluetooth &&
|
||||||
permissionRecord &&
|
permissionRecord &&
|
||||||
voiceSupported))
|
voiceSupported &&
|
||||||
|
voiceLanguageOptionsAvailable.contains(
|
||||||
|
(prefs!.getString("voiceLanguage") ??
|
||||||
|
"en_US"))))
|
||||||
? const SizedBox.shrink()
|
? const SizedBox.shrink()
|
||||||
: button(
|
: button(
|
||||||
permissionLoading
|
permissionLoading
|
||||||
? AppLocalizations.of(context)!
|
? AppLocalizations.of(context)!
|
||||||
.settingsVoicePermissionLoading
|
.settingsVoicePermissionLoading
|
||||||
: (permissionBluetooth && permissionRecord)
|
: (!voiceLanguageOptionsAvailable.contains(
|
||||||
|
(prefs!.getString(
|
||||||
|
"voiceLanguage") ??
|
||||||
|
"en_US")) &&
|
||||||
|
(prefs!.getBool("voiceModeEnabled") ??
|
||||||
|
false))
|
||||||
? AppLocalizations.of(context)!
|
? AppLocalizations.of(context)!
|
||||||
.settingsVoiceNotSupported
|
.settingsVoiceTtsNotSupported
|
||||||
: AppLocalizations.of(context)!
|
: !(permissionBluetooth && permissionRecord)
|
||||||
.settingsVoicePermissionNot,
|
? AppLocalizations.of(context)!
|
||||||
|
.settingsVoicePermissionNot
|
||||||
|
: !(prefs!.getBool(
|
||||||
|
"voiceModeEnabled") ??
|
||||||
|
false)
|
||||||
|
? AppLocalizations.of(context)!
|
||||||
|
.settingsVoiceNotEnabled
|
||||||
|
: AppLocalizations.of(context)!
|
||||||
|
.settingsVoiceNotSupported,
|
||||||
Icons.info_rounded, () {
|
Icons.info_rounded, () {
|
||||||
if (permissionLoading) return;
|
if (permissionLoading) return;
|
||||||
if (!(permissionBluetooth && permissionRecord)) {
|
if (!voiceLanguageOptions.contains(
|
||||||
|
(prefs!.getString("voiceLanguage") ??
|
||||||
|
"en_US"))) {
|
||||||
|
selectionHaptic();
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
|
content: Text(AppLocalizations.of(context)!
|
||||||
|
.settingsVoiceTtsNotSupportedDescription),
|
||||||
|
showCloseIcon: true));
|
||||||
|
} else if (!(permissionBluetooth &&
|
||||||
|
permissionRecord)) {
|
||||||
void load() async {
|
void load() async {
|
||||||
try {
|
try {
|
||||||
if (await Permission
|
if (await Permission
|
||||||
|
@ -135,33 +173,42 @@ class _ScreenSettingsVoiceState extends State<ScreenSettingsVoice> {
|
||||||
Icons.language_rounded, () {
|
Icons.language_rounded, () {
|
||||||
int usedIndex = -1;
|
int usedIndex = -1;
|
||||||
Function? setModalState;
|
Function? setModalState;
|
||||||
void load() async {
|
|
||||||
var tmp = await speech.locales();
|
|
||||||
languageOptionIds = tmp.map((e) => e.localeId);
|
|
||||||
languageOptions = tmp.map((e) => e.name);
|
|
||||||
|
|
||||||
if ((prefs!.getString("voiceLanguage") ?? "") != "") {
|
|
||||||
for (int i = 0; i < languageOptionIds.length; i++) {
|
|
||||||
if (languageOptionIds.elementAt(i) ==
|
|
||||||
(prefs!.getString("voiceLanguage") ?? "")) {
|
|
||||||
usedIndex = i;
|
|
||||||
setModalState!(() {});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
selectionHaptic();
|
selectionHaptic();
|
||||||
|
|
||||||
load();
|
|
||||||
|
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
builder:
|
builder:
|
||||||
(context) => StatefulBuilder(
|
(context) => StatefulBuilder(
|
||||||
builder: (context, setLocalState) {
|
builder: (context, setLocalState) {
|
||||||
setModalState = setLocalState;
|
setModalState = setLocalState;
|
||||||
|
|
||||||
|
void loadSelected() async {
|
||||||
|
if ((prefs!.getString("voiceLanguage") ??
|
||||||
|
"") !=
|
||||||
|
"") {
|
||||||
|
for (int i = 0;
|
||||||
|
i < languageOptionIds.length;
|
||||||
|
i++) {
|
||||||
|
if (languageOptionIds.elementAt(i) ==
|
||||||
|
(prefs!.getString(
|
||||||
|
"voiceLanguage") ??
|
||||||
|
"")) {
|
||||||
|
setModalState!(() {
|
||||||
|
usedIndex = i;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dialogMustLoad) {
|
||||||
|
load();
|
||||||
|
loadSelected();
|
||||||
|
dialogMustLoad = false;
|
||||||
|
}
|
||||||
|
|
||||||
return PopScope(
|
return PopScope(
|
||||||
onPopInvoked: (didPop) {
|
onPopInvoked: (didPop) {
|
||||||
if (usedIndex == -1) return;
|
if (usedIndex == -1) return;
|
||||||
|
@ -169,7 +216,9 @@ class _ScreenSettingsVoiceState extends State<ScreenSettingsVoice> {
|
||||||
"voiceLanguage",
|
"voiceLanguage",
|
||||||
languageOptionIds
|
languageOptionIds
|
||||||
.elementAt(usedIndex));
|
.elementAt(usedIndex));
|
||||||
setState(() {});
|
setState(() {
|
||||||
|
dialogMustLoad = true;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
|
@ -226,8 +275,8 @@ class _ScreenSettingsVoiceState extends State<ScreenSettingsVoice> {
|
||||||
avatar: (usedIndex ==
|
avatar: (usedIndex ==
|
||||||
index)
|
index)
|
||||||
? null
|
? null
|
||||||
: (languageOptionIds.elementAt(index).startsWith(AppLocalizations.of(context)!.localeName))
|
: (voiceLanguageOptionsAvailable.contains(languageOptionIds.elementAt(index)))
|
||||||
? const Icon(Icons.star_rounded)
|
? const Icon(Icons.spatial_tracking_rounded)
|
||||||
: null,
|
: null,
|
||||||
checkmarkColor: (usedIndex ==
|
checkmarkColor: (usedIndex ==
|
||||||
index)
|
index)
|
||||||
|
|
Loading…
Reference in New Issue