From 20de20c6ae921aea6f813c82c3e8eb35f9d5329f Mon Sep 17 00:00:00 2001 From: sonnemaf Date: Thu, 7 Jan 2021 15:12:42 +0100 Subject: [PATCH 1/6] VoiceCommand trigger added to Microsoft.Toolkit.Uwp.UI.Behaviors --- .../Microsoft.Toolkit.Uwp.SampleApp.csproj | 12 ++ .../Package.appxmanifest | 1 + .../SamplePages/VoiceCommands/ClickAction.cs | 23 +++ .../VoiceCommands/SelectListBoxItemAction.cs | 52 +++++++ .../VoiceCommands/VoiceCommands.png | Bin 0 -> 3825 bytes .../VoiceCommands/VoiceCommandsPage.xaml | 143 ++++++++++++++++++ .../VoiceCommands/VoiceCommandsPage.xaml.cs | 72 +++++++++ .../VoiceCommands/VoiceCommandsPageCode.bind | 60 ++++++++ .../VoiceCommands/VoiceCommandsPageXaml.bind | 142 +++++++++++++++++ .../SamplePages/samples.json | 17 ++- .../VoiceCommands/ISpeechRecognitionResult.cs | 9 ++ .../VoiceCommands/ISpeechRecognizer.cs | 10 ++ .../VoiceCommands/RecognizedEventArgs.cs | 16 ++ .../VoiceCommands/SpeechRecognitionResult.cs | 15 ++ .../VoiceCommands/VoiceCommandTrigger.cs | 110 ++++++++++++++ .../WindowsMediaSpeechRecognizer.cs | 75 +++++++++ 16 files changed, 754 insertions(+), 3 deletions(-) create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/ClickAction.cs create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/SelectListBoxItemAction.cs create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommands.png create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml.cs create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageCode.bind create mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageXaml.bind create mode 100644 Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognitionResult.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognizer.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/RecognizedEventArgs.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/SpeechRecognitionResult.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/VoiceCommandTrigger.cs create mode 100644 Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/WindowsMediaSpeechRecognizer.cs diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj index 6a2d9935b6e..18ec78ab161 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj +++ b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj @@ -359,6 +359,7 @@ + @@ -547,6 +548,11 @@ IsNullOrEmptyStateTriggerPage.xaml + + + VoiceCommandsPage.xaml + + WrapLayoutPage.xaml @@ -587,6 +593,8 @@ + + @@ -1330,6 +1338,10 @@ Designer MSBuild:Compile + + MSBuild:Compile + Designer + Designer MSBuild:Compile diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Package.appxmanifest b/Microsoft.Toolkit.Uwp.SampleApp/Package.appxmanifest index c9118ddbe4f..5cea81f4dcb 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Package.appxmanifest +++ b/Microsoft.Toolkit.Uwp.SampleApp/Package.appxmanifest @@ -48,5 +48,6 @@ + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/ClickAction.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/ClickAction.cs new file mode 100644 index 00000000000..5bc826a3a94 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/ClickAction.cs @@ -0,0 +1,23 @@ +using Microsoft.Xaml.Interactivity; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Automation.Peers; +using Windows.UI.Xaml.Automation.Provider; +using Windows.UI.Xaml.Controls; + +namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages +{ + public class ClickAction : DependencyObject, IAction + { + public object Execute(object sender, object parameter) + { + if (sender is Button btn && btn.IsEnabled) + { + var peer = new ButtonAutomationPeer(btn); + var invokeProv = peer.GetPattern(PatternInterface.Invoke) as IInvokeProvider; + invokeProv?.Invoke(); + } + + return null; + } + } +} diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/SelectListBoxItemAction.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/SelectListBoxItemAction.cs new file mode 100644 index 00000000000..4f11a72da13 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/SelectListBoxItemAction.cs @@ -0,0 +1,52 @@ +using Microsoft.Xaml.Interactivity; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages +{ + public class SelectListBoxItemAction : DependencyObject, IAction + { + public Direction MoveDirection + { + get => (Direction)GetValue(MoveDirectionProperty); + set => SetValue(MoveDirectionProperty, value); + } + + public static readonly DependencyProperty MoveDirectionProperty = DependencyProperty.Register(nameof(MoveDirection), typeof(Direction), typeof(SelectListBoxItemAction), new PropertyMetadata(default(Direction))); + + public object Execute(object sender, object parameter) + { + if (sender is ListBox lb) + { + var selectedIndex = lb.SelectedIndex; + switch (MoveDirection) + { + case Direction.Up when selectedIndex > 0: + lb.SelectedIndex--; + break; + case Direction.Down when selectedIndex < lb.Items.Count - 1: + lb.SelectedIndex++; + break; + case Direction.First when lb.Items.Count > 0: + lb.SelectedIndex = 0; + break; + case Direction.Last when lb.Items.Count > 0: + lb.SelectedIndex = lb.Items.Count - 1; + break; + default: + break; + } + } + + return null; + } + + public enum Direction + { + Up, + Down, + First, + Last + } + } +} diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommands.png b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommands.png new file mode 100644 index 0000000000000000000000000000000000000000..06aa81ad292070aca666f2112c9094b1710b0a61 GIT binary patch literal 3825 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 WlP04s^1r7b&~-~Y3kv*J{C@!G*7qL( literal 0 HcmV?d00001 diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml new file mode 100644 index 00000000000..196ea937fab --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml.cs new file mode 100644 index 00000000000..ffe662ecf46 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml.cs @@ -0,0 +1,72 @@ +using System; +using Microsoft.Toolkit.Uwp.UI.Behaviors; +using Windows.UI.Popups; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 + +namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class VoiceCommandsPage : Page + { + public VoiceCommandsPage() + { + this.InitializeComponent(); + this.Loaded += this.VoiceCommandsPage_Loaded; + } + + private async void VoiceCommandsPage_Loaded(object sender, RoutedEventArgs e) + { + if (VoiceCommandTrigger.SpeechRecognizer is null) + { + VoiceCommandTrigger.SpeechRecognizer = await WindowsMediaSpeechRecognizer.CreateAsync(Window.Current); + } + } + + private void ButtonAdd_Click(object sender, RoutedEventArgs e) + { + MoveItem(listBoxAvailable, listBoxSelected); + } + + private void MoveItem(ListBox from, ListBox to) + { + var selectedIndex = from.SelectedIndex; + var selectedItem = from.SelectedItem; + if (selectedIndex > -1) + { + from.Items.RemoveAt(selectedIndex); + from.SelectedIndex = Math.Min(selectedIndex, from.Items.Count - 1); + to.Items.Add(selectedItem); + to.SelectedIndex = to.Items.Count - 1; + } + } + + private void ButtonDelete_Click(object sender, RoutedEventArgs e) + { + MoveItem(listBoxSelected, listBoxAvailable); + } + + + private void ButtonAppend_Click(object sender, RoutedEventArgs e) + { + if (!string.IsNullOrWhiteSpace(textBoxExtraItem.Text)) + { + listBoxSelected.Items.Add(new ListBoxItem + { + Content = textBoxExtraItem.Text + }); + listBoxSelected.SelectedIndex = listBoxSelected.Items.Count - 1; + textBoxExtraItem.Text = string.Empty; + } + } + + public void WhatCanISay() + { + _ = new MessageDialog("TODO: What can I Say").ShowAsync(); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageCode.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageCode.bind new file mode 100644 index 00000000000..0b321ec9514 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageCode.bind @@ -0,0 +1,60 @@ +// C# + +public sealed partial class VoiceCommandsPage : Page +{ + public VoiceCommandsPage() + { + this.InitializeComponent(); + this.Loaded += this.VoiceCommandsPage_Loaded; + } + + private async void VoiceCommandsPage_Loaded(object sender, RoutedEventArgs e) + { + if (VoiceCommandTrigger.SpeechRecognizer is null) + { + VoiceCommandTrigger.SpeechRecognizer = await WindowsMediaSpeechRecognizer.CreateAsync(Window.Current); + } + } + + private void ButtonAdd_Click(object sender, RoutedEventArgs e) + { + MoveItem(listBoxAvailable, listBoxSelected); + } + + private void MoveItem(ListBox from, ListBox to) + { + var selectedIndex = from.SelectedIndex; + var selectedItem = from.SelectedItem; + if (selectedIndex > -1) + { + from.Items.RemoveAt(selectedIndex); + from.SelectedIndex = Math.Min(selectedIndex, from.Items.Count - 1); + to.Items.Add(selectedItem); + to.SelectedIndex = to.Items.Count - 1; + } + } + + private void ButtonDelete_Click(object sender, RoutedEventArgs e) + { + MoveItem(listBoxSelected, listBoxAvailable); + } + + + private void ButtonAppend_Click(object sender, RoutedEventArgs e) + { + if (!string.IsNullOrWhiteSpace(textBoxExtraItem.Text)) + { + listBoxSelected.Items.Add(new ListBoxItem + { + Content = textBoxExtraItem.Text + }); + listBoxSelected.SelectedIndex = listBoxSelected.Items.Count - 1; + textBoxExtraItem.Text = string.Empty; + } + } + + public void WhatCanISay() + { + _ = new MessageDialog("TODO: What can I Say").ShowAsync(); + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageXaml.bind new file mode 100644 index 00000000000..dea9e91a633 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageXaml.bind @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json index b7d7a495ffd..2bc6470cc2e 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json @@ -859,7 +859,7 @@ { "Name": "ViewportBehavior", "Type": "ViewportBehaviorPage", - "Subcategory": "Systems", + "Subcategory": "Behaviors", "About": "Behavior for listening element enter or exit viewport", "CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI/Behaviors", "CodeFile": "ViewportBehaviorCode.bind", @@ -870,7 +870,7 @@ { "Name": "AutoFocusBehavior", "Type": "AutoFocusBehaviorPage", - "Subcategory": "Systems", + "Subcategory": "Behaviors", "About": "Behavior to automatically set the focus on a control when it loads", "CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI/Behaviors/", "XamlCodeFile": "AutoFocusBehaviorXaml.bind", @@ -880,13 +880,24 @@ { "Name": "FocusBehavior", "Type": "FocusBehaviorPage", - "Subcategory": "Systems", + "Subcategory": "Behaviors", "About": "Behavior to automatically set the focus on the first control which accepts it", "CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI/Behaviors/", "CodeFile": "FocusBehaviorXaml.bind", "Icon": "/Assets/Helpers.png", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/behaviors/FocusBehavior.md" }, + { + "Name": "VoiceCommands", + "Type": "VoiceCommandsPage", + "Subcategory": "Behaviors", + "About": "Behavior to trigger Actions using a voice command", + "CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI/Behaviors/", + "CodeFile": "VoiceCommandsPageCode.bind", + "XamlCodeFile": "VoiceCommandsPageXaml.bind", + "Icon": "/SamplePages/VoiceCommands/VoiceCommands.png", + "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/behaviors/FocusBehavior.md" + }, { "Name": "Markdown Parser", "Type": "MarkdownParserPage", diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognitionResult.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognitionResult.cs new file mode 100644 index 00000000000..18b548b426d --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognitionResult.cs @@ -0,0 +1,9 @@ +namespace Microsoft.Toolkit.Uwp.UI.Behaviors +{ + public interface ISpeechRecognitionResult + { + string Text { get; } + double RawConfidence { get; } + } + +} diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognizer.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognizer.cs new file mode 100644 index 00000000000..4511cf1fe18 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognizer.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.Toolkit.Uwp.UI.Behaviors +{ + public interface ISpeechRecognizer { + event RecognizedEventHandler Recognized; + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/RecognizedEventArgs.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/RecognizedEventArgs.cs new file mode 100644 index 00000000000..d6aac626601 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/RecognizedEventArgs.cs @@ -0,0 +1,16 @@ +using System; + +namespace Microsoft.Toolkit.Uwp.UI.Behaviors +{ + public class RecognizedEventArgs : EventArgs + { + public ISpeechRecognitionResult Result { get; } + + public RecognizedEventArgs(ISpeechRecognitionResult propertyParameter) + { + this.Result = propertyParameter; + } + } + + public delegate void RecognizedEventHandler(ISpeechRecognizer sender, RecognizedEventArgs e); +} diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/SpeechRecognitionResult.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/SpeechRecognitionResult.cs new file mode 100644 index 00000000000..618d5c5f6e8 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/SpeechRecognitionResult.cs @@ -0,0 +1,15 @@ +namespace Microsoft.Toolkit.Uwp.UI.Behaviors +{ + public class SpeechRecognitionResult : ISpeechRecognitionResult + { + public string Text { get; } + + public double RawConfidence { get; } + + public SpeechRecognitionResult(string text, double rawConfidence) + { + Text = text; + RawConfidence = rawConfidence; + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/VoiceCommandTrigger.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/VoiceCommandTrigger.cs new file mode 100644 index 00000000000..c75f0552732 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/VoiceCommandTrigger.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.Xaml.Interactivity; +using Windows.UI.Core; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Behaviors +{ + public class VoiceCommandTrigger : Trigger + { + private static readonly Dictionary> _triggers = new Dictionary>(StringComparer.InvariantCultureIgnoreCase); + private static ISpeechRecognizer _speechRecognizer; + + public static ISpeechRecognizer SpeechRecognizer + { + get => _speechRecognizer; + set + { + if (value != _speechRecognizer) + { + if (_speechRecognizer is object) + { + _speechRecognizer.Recognized -= SpeechRecognizer_Recognized; + } + _speechRecognizer = value; + if (_speechRecognizer is object) + { + _speechRecognizer.Recognized += SpeechRecognizer_Recognized; + } + } + } + } + + private static void SpeechRecognizer_Recognized(ISpeechRecognizer sender, RecognizedEventArgs e) + { + Debug.WriteLine(e.Result.Text); + if (_triggers.TryGetValue(e.Result.Text, out var list)) + { + foreach (var trigger in list) + { + _ = trigger.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => + { + Interaction.ExecuteActions(trigger.AssociatedObject, trigger.Actions, e); + }); + } + } + } + + public string Text + { + get => (string)GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + public static readonly DependencyProperty TextProperty = DependencyProperty.Register(nameof(Text), typeof(string), typeof(VoiceCommandTrigger), new PropertyMetadata(default(string), OnTextPropertyChanged)); + + private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is VoiceCommandTrigger source) + { + var newValue = (string)e.NewValue; + var oldValue = (string)e.OldValue; + if (!string.IsNullOrEmpty(oldValue)) source.Remove(oldValue); + if (!string.IsNullOrEmpty(newValue)) source.Add(newValue); + } + } + + + protected override void OnDetaching() + { + Remove(Text); + base.OnDetaching(); + } + + private void Add(string text) + { + foreach (var item in text.Split('|')) + { + if (_triggers.TryGetValue(item, out var list)) + { + list.Add(this); + } + else + { + list = new List() + { + this + }; + _triggers[item] = list; + } + } + } + + private void Remove(string text) + { + foreach (var item in text.Split('|')) + { + if (_triggers.TryGetValue(item, out var list)) + { + list.Remove(this); + if (list.Count == 0) + { + _triggers.Remove(item); + } + } + } + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/WindowsMediaSpeechRecognizer.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/WindowsMediaSpeechRecognizer.cs new file mode 100644 index 00000000000..0bc6ff590a0 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/WindowsMediaSpeechRecognizer.cs @@ -0,0 +1,75 @@ +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using Windows.Globalization; +using Windows.Media.SpeechRecognition; +using Windows.UI.Core; + +namespace Microsoft.Toolkit.Uwp.UI.Behaviors +{ + public class WindowsMediaSpeechRecognizer : ISpeechRecognizer + { + private readonly SpeechRecognizer _speechRecognizer; + + private WindowsMediaSpeechRecognizer(SpeechRecognizer speechRecognizer) + { + _speechRecognizer = speechRecognizer; + } + + public static async Task CreateAsync(Windows.UI.Xaml.Window window) + { + SpeechRecognizer sr = null; + foreach (var item in Windows.System.UserProfile.GlobalizationPreferences.Languages) + { + var language = new Language(item); + try + { + sr = new SpeechRecognizer(language); + break; + } + catch (Exception ex) + { + Debug.WriteLine(ex); + } + } + + if (sr is null) + { + sr = new SpeechRecognizer(); + } + + var d = new WindowsMediaSpeechRecognizer(sr); + + Debug.WriteLine($"SpeechRecognizer Language: { sr.CurrentLanguage.DisplayName}"); + + sr.ContinuousRecognitionSession.AutoStopSilenceTimeout = TimeSpan.MaxValue; + await sr.CompileConstraintsAsync(); + sr.ContinuousRecognitionSession.ResultGenerated += d.ContinuousRecognitionSession_ResultGenerated; + await sr.ContinuousRecognitionSession.StartAsync(); + + window.Activated += d.Window_Activated; + + return d; + } + + private void Window_Activated(object sender, Windows.UI.Core.WindowActivatedEventArgs e) + { + if (e.WindowActivationState == CoreWindowActivationState.CodeActivated && _speechRecognizer.State == SpeechRecognizerState.Idle) + { + _ = _speechRecognizer.ContinuousRecognitionSession.StartAsync(); + } + } + + private void ContinuousRecognitionSession_ResultGenerated(SpeechContinuousRecognitionSession sender, SpeechContinuousRecognitionResultGeneratedEventArgs args) + { + OnRecognized(new RecognizedEventArgs(new SpeechRecognitionResult(args.Result.Text, args.Result.RawConfidence))); + } + + public event RecognizedEventHandler Recognized; + + protected virtual void OnRecognized(RecognizedEventArgs args) + { + Recognized?.Invoke(this, args); + } + } +} From ded0b25ec705ae4e3489ddc0ebd911c3e65e3596 Mon Sep 17 00:00:00 2001 From: sonnemaf Date: Mon, 11 Jan 2021 09:43:13 +0100 Subject: [PATCH 2/6] IsEnabled property added --- .../VoiceCommands/VoiceCommandsPage.xaml | 47 ++++++++++++---- .../VoiceCommands/VoiceCommandsPage.xaml.cs | 9 ++++ .../VoiceCommands/VoiceCommandsPageCode.bind | 11 +++- .../VoiceCommands/VoiceCommandsPageXaml.bind | 54 ++++++++++++++----- .../VoiceCommands/VoiceCommandTrigger.cs | 15 +++++- 5 files changed, 112 insertions(+), 24 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml index 196ea937fab..487496e35ff 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml @@ -15,7 +15,8 @@ RowSpacing="12"> - + @@ -41,16 +42,20 @@ Grid.RowSpan="3" SelectedIndex="0"> - + - + - + - + @@ -73,6 +78,7 @@ Content="> Add >"> @@ -87,16 +93,20 @@ Grid.RowSpan="2" Grid.Column="2"> - + - + - + - + @@ -111,6 +121,7 @@ Content="< Delete <"> @@ -139,5 +150,23 @@ + + + + + + + + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml.cs index ffe662ecf46..f74c9badc29 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml.cs @@ -68,5 +68,14 @@ public void WhatCanISay() { _ = new MessageDialog("TODO: What can I Say").ShowAsync(); } + + private void ToggleListning_Toggled(object sender, RoutedEventArgs e) + { + if (triggerListning is object) + { + triggerListning.Text = toggleListning.IsOn ? "Voice Off" : "Voice On"; + actionListning.Value = !toggleListning.IsOn; + } + } } } diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageCode.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageCode.bind index 0b321ec9514..3d839d151c4 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageCode.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageCode.bind @@ -1,5 +1,3 @@ -// C# - public sealed partial class VoiceCommandsPage : Page { public VoiceCommandsPage() @@ -57,4 +55,13 @@ public sealed partial class VoiceCommandsPage : Page { _ = new MessageDialog("TODO: What can I Say").ShowAsync(); } + + private void ToggleListning_Toggled(object sender, RoutedEventArgs e) + { + if (triggerListning is object) + { + triggerListning.Text = toggleListning.IsOn ? "Voice Off" : "Voice On"; + actionListning.Value = !toggleListning.IsOn; + } + } } \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageXaml.bind index dea9e91a633..a8ed0294455 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageXaml.bind +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageXaml.bind @@ -1,4 +1,4 @@ - + - + @@ -40,16 +42,20 @@ Grid.RowSpan="3" SelectedIndex="0"> - + - + - + - + @@ -72,7 +78,8 @@ Content="> Add >"> + IsEnabled="{x:Bind toggleListning.IsOn, Mode=OneWay}" + Text="Add|at"> @@ -86,16 +93,20 @@ Grid.RowSpan="2" Grid.Column="2"> - + - + - + - + @@ -110,7 +121,8 @@ Content="< Delete <"> + IsEnabled="{x:Bind toggleListning.IsOn, Mode=OneWay}" + Text="Delete"> @@ -138,5 +150,23 @@ + + + + + + + + diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/VoiceCommandTrigger.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/VoiceCommandTrigger.cs index c75f0552732..752cd1aa285 100644 --- a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/VoiceCommandTrigger.cs +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/VoiceCommandTrigger.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using Microsoft.Xaml.Interactivity; +using Windows.Gaming.Input.ForceFeedback; using Windows.UI.Core; using Windows.UI.Xaml; @@ -41,7 +43,10 @@ private static void SpeechRecognizer_Recognized(ISpeechRecognizer sender, Recogn { _ = trigger.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { - Interaction.ExecuteActions(trigger.AssociatedObject, trigger.Actions, e); + if (trigger.IsEnabled) + { + Interaction.ExecuteActions(trigger.AssociatedObject, trigger.Actions, e); + } }); } } @@ -55,6 +60,14 @@ public string Text public static readonly DependencyProperty TextProperty = DependencyProperty.Register(nameof(Text), typeof(string), typeof(VoiceCommandTrigger), new PropertyMetadata(default(string), OnTextPropertyChanged)); + public bool IsEnabled + { + get => (bool)GetValue(IsEnabledProperty); + set => SetValue(IsEnabledProperty, value); + } + + public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.Register(nameof(IsEnabled), typeof(bool), typeof(VoiceCommandTrigger), new PropertyMetadata(true)); + private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is VoiceCommandTrigger source) From a084f678e77cb6488ebae2f8a41254405ae3a506 Mon Sep 17 00:00:00 2001 From: sonnemaf Date: Tue, 12 Jan 2021 16:36:45 +0100 Subject: [PATCH 3/6] Error handling added for when the Microphone is not allowed to be accessed --- .../WindowsMediaSpeechRecognizer.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/WindowsMediaSpeechRecognizer.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/WindowsMediaSpeechRecognizer.cs index 0bc6ff590a0..46f269e757e 100644 --- a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/WindowsMediaSpeechRecognizer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/WindowsMediaSpeechRecognizer.cs @@ -39,17 +39,21 @@ public static async Task CreateAsync(Windows.UI.Xa } var d = new WindowsMediaSpeechRecognizer(sr); - - Debug.WriteLine($"SpeechRecognizer Language: { sr.CurrentLanguage.DisplayName}"); + Debug.WriteLine($"SpeechRecognizer Language: {sr.CurrentLanguage.DisplayName}"); sr.ContinuousRecognitionSession.AutoStopSilenceTimeout = TimeSpan.MaxValue; await sr.CompileConstraintsAsync(); sr.ContinuousRecognitionSession.ResultGenerated += d.ContinuousRecognitionSession_ResultGenerated; - await sr.ContinuousRecognitionSession.StartAsync(); - - window.Activated += d.Window_Activated; - - return d; + try + { + await sr.ContinuousRecognitionSession.StartAsync(); + window.Activated += d.Window_Activated; + return d; + } + catch + { + return null; + } } private void Window_Activated(object sender, Windows.UI.Core.WindowActivatedEventArgs e) From 92c270196b50ad8e5319d8327c1d1167568a7755 Mon Sep 17 00:00:00 2001 From: sonnemaf Date: Wed, 20 Jan 2021 20:05:22 +0100 Subject: [PATCH 4/6] Xml comments added --- .../VoiceCommands/ISpeechRecognitionResult.cs | 11 +++++- .../VoiceCommands/ISpeechRecognizer.cs | 15 ++++---- .../VoiceCommands/RecognizedEventArgs.cs | 19 ++++++++-- .../VoiceCommands/SpeechRecognitionResult.cs | 14 ++++++++ .../VoiceCommands/VoiceCommandTrigger.cs | 35 +++++++++++++++++-- .../WindowsMediaSpeechRecognizer.cs | 18 +++++++++- 6 files changed, 99 insertions(+), 13 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognitionResult.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognitionResult.cs index 18b548b426d..8dd7b2d1621 100644 --- a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognitionResult.cs +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognitionResult.cs @@ -1,9 +1,18 @@ namespace Microsoft.Toolkit.Uwp.UI.Behaviors { + /// + /// Service used to report a recognized speech result + /// public interface ISpeechRecognitionResult { + /// + /// Gets the Text of the recognized speech + /// string Text { get; } + + /// + /// Gets the RawConfidence of the recognized speech + /// double RawConfidence { get; } } - } diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognizer.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognizer.cs index 4511cf1fe18..023ff7c7672 100644 --- a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognizer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognizer.cs @@ -1,10 +1,13 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Microsoft.Toolkit.Uwp.UI.Behaviors +namespace Microsoft.Toolkit.Uwp.UI.Behaviors { - public interface ISpeechRecognizer { + /// + /// Service used to recongize speech + /// + public interface ISpeechRecognizer + { + /// + /// Occurs when a speech is recognized + /// event RecognizedEventHandler Recognized; } } diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/RecognizedEventArgs.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/RecognizedEventArgs.cs index d6aac626601..a76931c841e 100644 --- a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/RecognizedEventArgs.cs +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/RecognizedEventArgs.cs @@ -2,15 +2,30 @@ namespace Microsoft.Toolkit.Uwp.UI.Behaviors { + /// + /// used to report the recognized speech result. + /// public class RecognizedEventArgs : EventArgs { + /// + /// Gets the result of a recognized speech + /// public ISpeechRecognitionResult Result { get; } - public RecognizedEventArgs(ISpeechRecognitionResult propertyParameter) + /// + /// Initializes a new instance of the class. + /// + /// The result which is speech recognized + public RecognizedEventArgs(ISpeechRecognitionResult result) { - this.Result = propertyParameter; + this.Result = result; } } + /// + /// The Delegate for a Recognized Event. + /// + /// Sender ThemeListener + /// The event arguments. public delegate void RecognizedEventHandler(ISpeechRecognizer sender, RecognizedEventArgs e); } diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/SpeechRecognitionResult.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/SpeechRecognitionResult.cs index 618d5c5f6e8..a657f2dc57c 100644 --- a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/SpeechRecognitionResult.cs +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/SpeechRecognitionResult.cs @@ -1,11 +1,25 @@ namespace Microsoft.Toolkit.Uwp.UI.Behaviors { + /// + /// This class reports a recognized speech + /// public class SpeechRecognitionResult : ISpeechRecognitionResult { + /// + /// Gets the Text of the recognized speech + /// public string Text { get; } + /// + /// Gets the RawConfidence of the recognized speech + /// public double RawConfidence { get; } + /// + /// Initializes a new instance of the class. + /// + /// the speech recognized text + /// the rawConfidence of recognized speech public SpeechRecognitionResult(string text, double rawConfidence) { Text = text; diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/VoiceCommandTrigger.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/VoiceCommandTrigger.cs index 752cd1aa285..1e5609b61d6 100644 --- a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/VoiceCommandTrigger.cs +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/VoiceCommandTrigger.cs @@ -9,11 +9,17 @@ namespace Microsoft.Toolkit.Uwp.UI.Behaviors { + /// + /// A behavior that listens spoken voice commands and executes its actions when that event is fired. + /// public class VoiceCommandTrigger : Trigger { private static readonly Dictionary> _triggers = new Dictionary>(StringComparer.InvariantCultureIgnoreCase); private static ISpeechRecognizer _speechRecognizer; + /// + /// Gets or sets the SpeechRecognizer object used to recongize the voice commands + /// public static ISpeechRecognizer SpeechRecognizer { get => _speechRecognizer; @@ -25,6 +31,7 @@ public static ISpeechRecognizer SpeechRecognizer { _speechRecognizer.Recognized -= SpeechRecognizer_Recognized; } + _speechRecognizer = value; if (_speechRecognizer is object) { @@ -52,20 +59,35 @@ private static void SpeechRecognizer_Recognized(ISpeechRecognizer sender, Recogn } } + /// + /// Gets or sets the spoken Text to trigger the Actions + /// + /// + /// Use the | to seperate alternative texts + /// public string Text { get => (string)GetValue(TextProperty); set => SetValue(TextProperty, value); } + /// + /// Identifies the dependency property. + /// public static readonly DependencyProperty TextProperty = DependencyProperty.Register(nameof(Text), typeof(string), typeof(VoiceCommandTrigger), new PropertyMetadata(default(string), OnTextPropertyChanged)); + /// + /// Gets or sets a value indicating whether the VoiceCommand is enabled. Default is true + /// public bool IsEnabled { get => (bool)GetValue(IsEnabledProperty); set => SetValue(IsEnabledProperty, value); } + /// + /// Identifies the dependency property. + /// public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.Register(nameof(IsEnabled), typeof(bool), typeof(VoiceCommandTrigger), new PropertyMetadata(true)); private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) @@ -74,12 +96,19 @@ private static void OnTextPropertyChanged(DependencyObject d, DependencyProperty { var newValue = (string)e.NewValue; var oldValue = (string)e.OldValue; - if (!string.IsNullOrEmpty(oldValue)) source.Remove(oldValue); - if (!string.IsNullOrEmpty(newValue)) source.Add(newValue); + if (!string.IsNullOrEmpty(oldValue)) + { + source.Remove(oldValue); + } + + if (!string.IsNullOrEmpty(newValue)) + { + source.Add(newValue); + } } } - + /// protected override void OnDetaching() { Remove(Text); diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/WindowsMediaSpeechRecognizer.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/WindowsMediaSpeechRecognizer.cs index 46f269e757e..e98a5e00c2e 100644 --- a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/WindowsMediaSpeechRecognizer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/WindowsMediaSpeechRecognizer.cs @@ -4,9 +4,13 @@ using Windows.Globalization; using Windows.Media.SpeechRecognition; using Windows.UI.Core; +using Windows.UI.Xaml; namespace Microsoft.Toolkit.Uwp.UI.Behaviors { + /// + /// This class used the Windows to recognize speech for the + /// public class WindowsMediaSpeechRecognizer : ISpeechRecognizer { private readonly SpeechRecognizer _speechRecognizer; @@ -16,7 +20,12 @@ private WindowsMediaSpeechRecognizer(SpeechRecognizer speechRecognizer) _speechRecognizer = speechRecognizer; } - public static async Task CreateAsync(Windows.UI.Xaml.Window window) + /// + /// Creates a which can be used for a + /// + /// The is used for triggering the Activated event. This will restart the speech recongition. + /// The created + public static async Task CreateAsync(Window window) { SpeechRecognizer sr = null; foreach (var item in Windows.System.UserProfile.GlobalizationPreferences.Languages) @@ -69,8 +78,15 @@ private void ContinuousRecognitionSession_ResultGenerated(SpeechContinuousRecogn OnRecognized(new RecognizedEventArgs(new SpeechRecognitionResult(args.Result.Text, args.Result.RawConfidence))); } + /// + /// Occurs when a speech is recognized + /// public event RecognizedEventHandler Recognized; + /// + /// Called when speech is recognized + /// + /// used to report the protected virtual void OnRecognized(RecognizedEventArgs args) { Recognized?.Invoke(this, args); From 04fe7a3a1e9da3285c0d9d5bc4444e04ba5bd769 Mon Sep 17 00:00:00 2001 From: sonnemaf Date: Thu, 21 Jan 2021 16:03:34 +0100 Subject: [PATCH 5/6] What can I say button added' and XAML and Code bind pages removed --- .../Microsoft.Toolkit.Uwp.SampleApp.csproj | 2 - .../VoiceCommands/VoiceCommandsPage.xaml | 23 ++- .../VoiceCommands/VoiceCommandsPage.xaml.cs | 18 +- .../VoiceCommands/VoiceCommandsPageCode.bind | 67 ------- .../VoiceCommands/VoiceCommandsPageXaml.bind | 172 ------------------ .../SamplePages/samples.json | 2 - 6 files changed, 29 insertions(+), 255 deletions(-) delete mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageCode.bind delete mode 100644 Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageXaml.bind diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj index 317657a6561..8ae3ddd01cc 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj +++ b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj @@ -590,8 +590,6 @@ - - diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml index 487496e35ff..fad852ff5ac 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml @@ -14,14 +14,6 @@ ColumnSpacing="12" RowSpacing="12"> - - - - - - @@ -151,6 +143,21 @@ + + @@ -50,7 +48,6 @@ private void ButtonDelete_Click(object sender, RoutedEventArgs e) MoveItem(listBoxSelected, listBoxAvailable); } - private void ButtonAppend_Click(object sender, RoutedEventArgs e) { if (!string.IsNullOrWhiteSpace(textBoxExtraItem.Text)) @@ -66,7 +63,20 @@ private void ButtonAppend_Click(object sender, RoutedEventArgs e) public void WhatCanISay() { - _ = new MessageDialog("TODO: What can I Say").ShowAsync(); + string content = "You can speak the following voice commands: \r\n" + + "- What can I say\r\n" + + "- Help \r\n" + + "- Add\r\n" + + "- Delete\r\n" + + "- Move avaiable up\r\n" + + "- Move avaiable down\r\n" + + "- Move avaiable to First\r\n" + + "- Move avaiable to Last\r\n" + + "- Move selected up\r\n" + + "- Move selected down\r\n" + + "- Move selected to First\r\n" + + "- Move selected to Last"; + _ = new MessageDialog(content, "What can I Say").ShowAsync(); } private void ToggleListning_Toggled(object sender, RoutedEventArgs e) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageCode.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageCode.bind deleted file mode 100644 index 3d839d151c4..00000000000 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageCode.bind +++ /dev/null @@ -1,67 +0,0 @@ -public sealed partial class VoiceCommandsPage : Page -{ - public VoiceCommandsPage() - { - this.InitializeComponent(); - this.Loaded += this.VoiceCommandsPage_Loaded; - } - - private async void VoiceCommandsPage_Loaded(object sender, RoutedEventArgs e) - { - if (VoiceCommandTrigger.SpeechRecognizer is null) - { - VoiceCommandTrigger.SpeechRecognizer = await WindowsMediaSpeechRecognizer.CreateAsync(Window.Current); - } - } - - private void ButtonAdd_Click(object sender, RoutedEventArgs e) - { - MoveItem(listBoxAvailable, listBoxSelected); - } - - private void MoveItem(ListBox from, ListBox to) - { - var selectedIndex = from.SelectedIndex; - var selectedItem = from.SelectedItem; - if (selectedIndex > -1) - { - from.Items.RemoveAt(selectedIndex); - from.SelectedIndex = Math.Min(selectedIndex, from.Items.Count - 1); - to.Items.Add(selectedItem); - to.SelectedIndex = to.Items.Count - 1; - } - } - - private void ButtonDelete_Click(object sender, RoutedEventArgs e) - { - MoveItem(listBoxSelected, listBoxAvailable); - } - - - private void ButtonAppend_Click(object sender, RoutedEventArgs e) - { - if (!string.IsNullOrWhiteSpace(textBoxExtraItem.Text)) - { - listBoxSelected.Items.Add(new ListBoxItem - { - Content = textBoxExtraItem.Text - }); - listBoxSelected.SelectedIndex = listBoxSelected.Items.Count - 1; - textBoxExtraItem.Text = string.Empty; - } - } - - public void WhatCanISay() - { - _ = new MessageDialog("TODO: What can I Say").ShowAsync(); - } - - private void ToggleListning_Toggled(object sender, RoutedEventArgs e) - { - if (triggerListning is object) - { - triggerListning.Text = toggleListning.IsOn ? "Voice Off" : "Voice On"; - actionListning.Value = !toggleListning.IsOn; - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageXaml.bind deleted file mode 100644 index a8ed0294455..00000000000 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPageXaml.bind +++ /dev/null @@ -1,172 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json index 2bc6470cc2e..c1108be09af 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json @@ -893,8 +893,6 @@ "Subcategory": "Behaviors", "About": "Behavior to trigger Actions using a voice command", "CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI/Behaviors/", - "CodeFile": "VoiceCommandsPageCode.bind", - "XamlCodeFile": "VoiceCommandsPageXaml.bind", "Icon": "/SamplePages/VoiceCommands/VoiceCommands.png", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/behaviors/FocusBehavior.md" }, From b469849bdb50e159363ccbd330e22abcbcd11b26 Mon Sep 17 00:00:00 2001 From: sonnemaf Date: Thu, 21 Jan 2021 16:09:20 +0100 Subject: [PATCH 6/6] Updated headers --- .../SamplePages/VoiceCommands/ClickAction.cs | 6 +++++- .../SamplePages/VoiceCommands/SelectListBoxItemAction.cs | 6 +++++- .../SamplePages/VoiceCommands/VoiceCommandsPage.xaml.cs | 6 +++++- .../VoiceCommands/ISpeechRecognitionResult.cs | 6 +++++- .../VoiceCommands/ISpeechRecognizer.cs | 6 +++++- .../VoiceCommands/RecognizedEventArgs.cs | 6 +++++- .../VoiceCommands/SpeechRecognitionResult.cs | 6 +++++- .../VoiceCommands/VoiceCommandTrigger.cs | 6 +++++- .../VoiceCommands/WindowsMediaSpeechRecognizer.cs | 6 +++++- 9 files changed, 45 insertions(+), 9 deletions(-) diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/ClickAction.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/ClickAction.cs index 5bc826a3a94..15f40edab1a 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/ClickAction.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/ClickAction.cs @@ -1,4 +1,8 @@ -using Microsoft.Xaml.Interactivity; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Xaml.Interactivity; using Windows.UI.Xaml; using Windows.UI.Xaml.Automation.Peers; using Windows.UI.Xaml.Automation.Provider; diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/SelectListBoxItemAction.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/SelectListBoxItemAction.cs index 4f11a72da13..7e016bab64d 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/SelectListBoxItemAction.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/SelectListBoxItemAction.cs @@ -1,4 +1,8 @@ -using Microsoft.Xaml.Interactivity; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Xaml.Interactivity; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml.cs index cdce8d7a362..a1b03843ea1 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/VoiceCommands/VoiceCommandsPage.xaml.cs @@ -1,4 +1,8 @@ -using System; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; using Microsoft.Toolkit.Uwp.UI.Behaviors; using Windows.UI.Popups; using Windows.UI.Xaml; diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognitionResult.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognitionResult.cs index 8dd7b2d1621..ddfe3d18c06 100644 --- a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognitionResult.cs +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognitionResult.cs @@ -1,4 +1,8 @@ -namespace Microsoft.Toolkit.Uwp.UI.Behaviors +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Toolkit.Uwp.UI.Behaviors { /// /// Service used to report a recognized speech result diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognizer.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognizer.cs index 023ff7c7672..ecaf111225f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognizer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/ISpeechRecognizer.cs @@ -1,4 +1,8 @@ -namespace Microsoft.Toolkit.Uwp.UI.Behaviors +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Toolkit.Uwp.UI.Behaviors { /// /// Service used to recongize speech diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/RecognizedEventArgs.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/RecognizedEventArgs.cs index a76931c841e..c8a995ff810 100644 --- a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/RecognizedEventArgs.cs +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/RecognizedEventArgs.cs @@ -1,4 +1,8 @@ -using System; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; namespace Microsoft.Toolkit.Uwp.UI.Behaviors { diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/SpeechRecognitionResult.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/SpeechRecognitionResult.cs index a657f2dc57c..358a1553679 100644 --- a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/SpeechRecognitionResult.cs +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/SpeechRecognitionResult.cs @@ -1,4 +1,8 @@ -namespace Microsoft.Toolkit.Uwp.UI.Behaviors +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Toolkit.Uwp.UI.Behaviors { /// /// This class reports a recognized speech diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/VoiceCommandTrigger.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/VoiceCommandTrigger.cs index 1e5609b61d6..44f116eee94 100644 --- a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/VoiceCommandTrigger.cs +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/VoiceCommandTrigger.cs @@ -1,4 +1,8 @@ -using System; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/WindowsMediaSpeechRecognizer.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/WindowsMediaSpeechRecognizer.cs index e98a5e00c2e..c982cb18d4f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/WindowsMediaSpeechRecognizer.cs +++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/VoiceCommands/WindowsMediaSpeechRecognizer.cs @@ -1,4 +1,8 @@ -using System; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; using System.Diagnostics; using System.Threading.Tasks; using Windows.Globalization;