protected override void Arrange() { AudioService = new Mock<IAudioService>(); CalibrationService = new Mock<ICalibrationService>(); CapturingStateManager = new Mock<ICapturingStateManager>(); DictionaryService = new Mock<IDictionaryService>(); InputService = new Mock<IInputService>(); KeyboardOutputService = new Mock<IKeyboardOutputService>(); KeyStateService = new Mock<IKeyStateService>(); KeyStateService.Setup(s => s.KeyDownStates).Returns(new NotifyingConcurrentDictionary<KeyValue, KeyDownStates>()); KeyStateService.Setup(s => s.KeySelectionProgress).Returns(new NotifyingConcurrentDictionary<KeyValue, double>()); LastMouseActionStateManager = new Mock<ILastMouseActionStateManager>(); MainWindowManipulationService = new Mock<IWindowManipulationService>(); MouseOutputService = new Mock<IMouseOutputService>(); SuggestionService = new Mock<ISuggestionStateService>(); ErrorNotifyingServices = new List<INotifyErrors>(); if(ShouldConstruct) { MainViewModel = new MainViewModel(AudioService.Object, CalibrationService.Object, DictionaryService.Object, KeyStateService.Object, SuggestionService.Object, CapturingStateManager.Object, LastMouseActionStateManager.Object, InputService.Object, KeyboardOutputService.Object, MouseOutputService.Object, MainWindowManipulationService.Object, ErrorNotifyingServices); MainViewModel.KeySelection += (s, e) => IsKeySelectionEventHandlerCalled = true; MainViewModel.PointSelection += (s, e) => IsPointSelectionEventHandlerCalled = true; } }
protected override void Act() { MainViewModel = new MainViewModel(AudioService.Object, CalibrationService.Object, DictionaryService.Object, KeyStateService.Object, SuggestionService.Object, CapturingStateManager.Object, LastMouseActionStateManager.Object, InputService.Object, KeyboardOutputService.Object, MouseOutputService.Object, MainWindowManipulationService.Object, ErrorNotifyingServices); }
private static async Task<bool> CheckForUpdates(IInputService inputService, IAudioService audioService, MainViewModel mainViewModel) { var taskCompletionSource = new TaskCompletionSource<bool>(); //Used to make this method awaitable on the InteractionRequest callback if (Settings.Default.CheckForUpdates) { Log.InfoFormat("Checking GitHub for updates (repo owner:'{0}', repo name:'{1}').", GitHubRepoOwner, GitHubRepoName); new ObservableGitHubClient(new ProductHeaderValue("OptiKey")).Release .GetAll(GitHubRepoOwner, GitHubRepoName) .Where(release => !release.Prerelease) .Take(1) .ObserveOnDispatcher() .Subscribe(release => { var currentVersion = new Version(DiagnosticInfo.AssemblyVersion); //Convert from string //Discard revision (4th number) as my GitHub releases are tagged with "vMAJOR.MINOR.PATCH" currentVersion = new Version(currentVersion.Major, currentVersion.Minor, currentVersion.Build); if (!string.IsNullOrEmpty(release.TagName)) { var tagNameWithoutLetters = new string(release.TagName.ToCharArray().Where(c => !char.IsLetter(c)).ToArray()); var latestAvailableVersion = new Version(tagNameWithoutLetters); if (latestAvailableVersion > currentVersion) { Log.InfoFormat("An update is available. Current version is {0}. Latest version on GitHub repo is {1}", currentVersion, latestAvailableVersion); inputService.RequestSuspend(); audioService.PlaySound(Settings.Default.InfoSoundFile, Settings.Default.InfoSoundVolume); mainViewModel.RaiseToastNotification(OptiKey.Properties.Resources.UPDATE_AVAILABLE, string.Format(OptiKey.Properties.Resources.URL_DOWNLOAD_PROMPT, release.TagName), NotificationTypes.Normal, () => { inputService.RequestResume(); taskCompletionSource.SetResult(true); }); return; } } Log.Info("No update found."); }, exception => Log.WarnFormat("Error when checking for updates. Exception message:{0}", exception.Message)); } else { taskCompletionSource.SetResult(false); } return await taskCompletionSource.Task; }
private static async Task<bool> ShowSplashScreen(IInputService inputService, IAudioService audioService, MainViewModel mainViewModel) { var taskCompletionSource = new TaskCompletionSource<bool>(); //Used to make this method awaitable on the InteractionRequest callback if (Settings.Default.ShowSplashScreen) { Log.Info("Showing splash screen."); var message = new StringBuilder(); message.AppendLine(string.Format(OptiKey.Properties.Resources.VERSION_DESCRIPTION, DiagnosticInfo.AssemblyVersion)); message.AppendLine(string.Format(OptiKey.Properties.Resources.KEYBOARD_AND_DICTIONARY_LANGUAGE_DESCRIPTION, Settings.Default.KeyboardAndDictionaryLanguage.ToDescription())); message.AppendLine(string.Format(OptiKey.Properties.Resources.UI_LANGUAGE_DESCRIPTION, Settings.Default.UiLanguage.ToDescription())); message.AppendLine(string.Format(OptiKey.Properties.Resources.POINTING_SOURCE_DESCRIPTION, Settings.Default.PointsSource.ToDescription())); var keySelectionSb = new StringBuilder(); keySelectionSb.Append(Settings.Default.KeySelectionTriggerSource.ToDescription()); switch (Settings.Default.KeySelectionTriggerSource) { case TriggerSources.Fixations: keySelectionSb.Append(string.Format(OptiKey.Properties.Resources.DURATION_FORMAT, Settings.Default.KeySelectionTriggerFixationDefaultCompleteTime.TotalMilliseconds)); break; case TriggerSources.KeyboardKeyDownsUps: keySelectionSb.Append(string.Format(" ({0})", Settings.Default.KeySelectionTriggerKeyboardKeyDownUpKey)); break; case TriggerSources.MouseButtonDownUps: keySelectionSb.Append(string.Format(" ({0})", Settings.Default.KeySelectionTriggerMouseDownUpButton)); break; } message.AppendLine(string.Format(OptiKey.Properties.Resources.KEY_SELECTION_TRIGGER_DESCRIPTION, keySelectionSb)); var pointSelectionSb = new StringBuilder(); pointSelectionSb.Append(Settings.Default.PointSelectionTriggerSource.ToDescription()); switch (Settings.Default.PointSelectionTriggerSource) { case TriggerSources.Fixations: pointSelectionSb.Append(string.Format(OptiKey.Properties.Resources.DURATION_FORMAT, Settings.Default.PointSelectionTriggerFixationCompleteTime.TotalMilliseconds)); break; case TriggerSources.KeyboardKeyDownsUps: pointSelectionSb.Append(string.Format(" ({0})", Settings.Default.PointSelectionTriggerKeyboardKeyDownUpKey)); break; case TriggerSources.MouseButtonDownUps: pointSelectionSb.Append(string.Format(" ({0})", Settings.Default.PointSelectionTriggerMouseDownUpButton)); break; } message.AppendLine(string.Format(OptiKey.Properties.Resources.POINT_SELECTION_DESCRIPTION, pointSelectionSb)); message.AppendLine(OptiKey.Properties.Resources.MANAGEMENT_CONSOLE_DESCRIPTION); message.AppendLine(OptiKey.Properties.Resources.WEBSITE_DESCRIPTION); inputService.RequestSuspend(); audioService.PlaySound(Settings.Default.InfoSoundFile, Settings.Default.InfoSoundVolume); mainViewModel.RaiseToastNotification( OptiKey.Properties.Resources.OPTIKEY_DESCRIPTION, message.ToString(), NotificationTypes.Normal, () => { inputService.RequestResume(); taskCompletionSource.SetResult(true); }); } else { taskCompletionSource.SetResult(false); } return await taskCompletionSource.Task; }
private void App_OnStartup(object sender, StartupEventArgs e) { try { Log.Info("Boot strapping the services and UI."); //Apply theme applyTheme(); //Define MainViewModel before services so I can setup a delegate to call into the MainViewModel //This is to work around the fact that the MainViewModel is created after the services. MainViewModel mainViewModel = null; Action<KeyValue> fireKeySelectionEvent = kv => { if (mainViewModel != null) //Access to modified closure is a good thing here, for once! { mainViewModel.FireKeySelectionEvent(kv); } }; //Create services var errorNotifyingServices = new List<INotifyErrors>(); IAudioService audioService = new AudioService(); IDictionaryService dictionaryService = new DictionaryService(Settings.Default.AutoCompleteMethod); IPublishService publishService = new PublishService(); ISuggestionStateService suggestionService = new SuggestionStateService(); ICalibrationService calibrationService = CreateCalibrationService(); ICapturingStateManager capturingStateManager = new CapturingStateManager(audioService); ILastMouseActionStateManager lastMouseActionStateManager = new LastMouseActionStateManager(); IKeyStateService keyStateService = new KeyStateService(suggestionService, capturingStateManager, lastMouseActionStateManager, calibrationService, fireKeySelectionEvent); IInputService inputService = CreateInputService(keyStateService, dictionaryService, audioService, calibrationService, capturingStateManager, errorNotifyingServices); IKeyboardOutputService keyboardOutputService = new KeyboardOutputService(keyStateService, suggestionService, publishService, dictionaryService, fireKeySelectionEvent); IMouseOutputService mouseOutputService = new MouseOutputService(publishService); errorNotifyingServices.Add(audioService); errorNotifyingServices.Add(dictionaryService); errorNotifyingServices.Add(publishService); errorNotifyingServices.Add(inputService); //Release keys on application exit ReleaseKeysOnApplicationExit(keyStateService, publishService); //Compose UI var mainWindow = new MainWindow(audioService, dictionaryService, inputService, keyStateService); IWindowManipulationService mainWindowManipulationService = new WindowManipulationService( mainWindow, () => Settings.Default.MainWindowOpacity, () => Settings.Default.MainWindowState, () => Settings.Default.MainWindowPreviousState, () => Settings.Default.MainWindowFloatingSizeAndPosition, () => Settings.Default.MainWindowDockPosition, () => Settings.Default.MainWindowDockSize, () => Settings.Default.MainWindowFullDockThicknessAsPercentageOfScreen, () => Settings.Default.MainWindowCollapsedDockThicknessAsPercentageOfFullDockThickness, () => Settings.Default.MainWindowMinimisedPosition, o => Settings.Default.MainWindowOpacity = o, state => Settings.Default.MainWindowState = state, state => Settings.Default.MainWindowPreviousState = state, rect => Settings.Default.MainWindowFloatingSizeAndPosition = rect, pos => Settings.Default.MainWindowDockPosition = pos, size => Settings.Default.MainWindowDockSize = size, t => Settings.Default.MainWindowFullDockThicknessAsPercentageOfScreen = t, t => Settings.Default.MainWindowCollapsedDockThicknessAsPercentageOfFullDockThickness = t); errorNotifyingServices.Add(mainWindowManipulationService); mainWindow.WindowManipulationService = mainWindowManipulationService; mainViewModel = new MainViewModel( audioService, calibrationService, dictionaryService, keyStateService, suggestionService, capturingStateManager, lastMouseActionStateManager, inputService, keyboardOutputService, mouseOutputService, mainWindowManipulationService, errorNotifyingServices); mainWindow.MainView.DataContext = mainViewModel; //Setup actions to take once main view is loaded (i.e. the view is ready, so hook up the services which kicks everything off) Action postMainViewLoaded = mainViewModel.AttachServiceEventHandlers; if(mainWindow.MainView.IsLoaded) { postMainViewLoaded(); } else { RoutedEventHandler loadedHandler = null; loadedHandler = (s, a) => { postMainViewLoaded(); mainWindow.MainView.Loaded -= loadedHandler; //Ensure this handler only triggers once }; mainWindow.MainView.Loaded += loadedHandler; } //Show the main window mainWindow.Show(); //Display splash screen and check for updates (and display message) after the window has been sized and positioned for the 1st time EventHandler sizeAndPositionInitialised = null; sizeAndPositionInitialised = async (_, __) => { mainWindowManipulationService.SizeAndPositionInitialised -= sizeAndPositionInitialised; //Ensure this handler only triggers once await ShowSplashScreen(inputService, audioService, mainViewModel); inputService.RequestResume(); //Start the input service await CheckForUpdates(inputService, audioService, mainViewModel); }; if (mainWindowManipulationService.SizeAndPositionIsInitialised) { sizeAndPositionInitialised(null, null); } else { mainWindowManipulationService.SizeAndPositionInitialised += sizeAndPositionInitialised; } } catch (Exception ex) { Log.Error("Error starting up application", ex); throw; } }
private async void App_OnStartup(object sender, StartupEventArgs e) { try { Log.Info("Boot strapping the services and UI."); //Apply theme applyTheme(); //Create services var errorNotifyingServices = new List<INotifyErrors>(); IAudioService audioService = new AudioService(); IDictionaryService dictionaryService = new DictionaryService(); IPublishService publishService = new PublishService(); ISuggestionStateService suggestionService = new SuggestionStateService(); ICalibrationService calibrationService = CreateCalibrationService(); ICapturingStateManager capturingStateManager = new CapturingStateManager(audioService); ILastMouseActionStateManager lastMouseActionStateManager = new LastMouseActionStateManager(); IWindowStateService mainWindowStateService = new WindowStateService(); IKeyboardService keyboardService = new KeyboardService(suggestionService, capturingStateManager, lastMouseActionStateManager, calibrationService, mainWindowStateService); IInputService inputService = CreateInputService(keyboardService, dictionaryService, audioService, calibrationService, capturingStateManager, errorNotifyingServices); IOutputService outputService = new OutputService(keyboardService, suggestionService, publishService, dictionaryService); errorNotifyingServices.Add(audioService); errorNotifyingServices.Add(dictionaryService); errorNotifyingServices.Add(publishService); errorNotifyingServices.Add(inputService); //Release keys on application exit ReleaseKeysOnApplicationExit(keyboardService, publishService); //Compose UI var mainWindow = new MainWindow(audioService, dictionaryService, inputService); mainWindowStateService.Window = mainWindow; IWindowManipulationService mainWindowManipulationService = new WindowManipulationService(mainWindow, () => Settings.Default.MainWindowTop, d => Settings.Default.MainWindowTop = d, () => Settings.Default.MainWindowLeft, d => Settings.Default.MainWindowLeft = d, () => Settings.Default.MainWindowHeight, d => Settings.Default.MainWindowHeight = d, () => Settings.Default.MainWindowWidth, d => Settings.Default.MainWindowWidth = d, () => Settings.Default.MainWindowState, s => Settings.Default.MainWindowState = s, Settings.Default, true, true); errorNotifyingServices.Add(mainWindowManipulationService); var mainViewModel = new MainViewModel( audioService, calibrationService, dictionaryService, keyboardService, suggestionService, capturingStateManager, lastMouseActionStateManager, inputService, outputService, mainWindowManipulationService, errorNotifyingServices); mainWindow.MainView.DataContext = mainViewModel; //Setup actions to take once main view is loaded (i.e. the view is ready, so hook up the services which kicks everything off) Action postMainViewLoaded = mainViewModel.AttachServiceEventHandlers; if(mainWindow.MainView.IsLoaded) { postMainViewLoaded(); } else { RoutedEventHandler loadedHandler = null; loadedHandler = (s, a) => { postMainViewLoaded(); mainWindow.MainView.Loaded -= loadedHandler; //Ensure this handler only triggers once }; mainWindow.MainView.Loaded += loadedHandler; } //Show the main window mainWindow.Show(); await ShowSplashScreen(inputService, audioService, mainViewModel); inputService.State = RunningStates.Running; //Start the input service await CheckForUpdates(inputService, audioService, mainViewModel); } catch (Exception ex) { Log.Error("Error starting up application", ex); throw; } }
private async Task<bool> ShowSplashScreen(IInputService inputService, IAudioService audioService, MainViewModel mainViewModel) { var taskCompletionSource = new TaskCompletionSource<bool>(); //Used to make this method awaitable on the InteractionRequest callback if (Settings.Default.ShowSplashScreen) { Log.Debug("Showing splash screen."); var message = new StringBuilder(); message.AppendLine(string.Format("Version: {0}", DiagnosticInfo.AssemblyVersion)); message.AppendLine(string.Format("Language: {0}", Settings.Default.Language.ToDescription())); message.AppendLine(string.Format("Pointing: {0}", Settings.Default.PointsSource.ToDescription())); var keySelectionSb = new StringBuilder(); keySelectionSb.Append(Settings.Default.KeySelectionTriggerSource.ToDescription()); switch (Settings.Default.KeySelectionTriggerSource) { case TriggerSources.Fixations: keySelectionSb.Append(string.Format(" ({0:#,###}ms)", Settings.Default.KeySelectionTriggerFixationCompleteTime.TotalMilliseconds)); break; case TriggerSources.KeyboardKeyDownsUps: keySelectionSb.Append(string.Format(" ({0})", Settings.Default.KeySelectionTriggerKeyboardKeyDownUpKey)); break; case TriggerSources.MouseButtonDownUps: keySelectionSb.Append(string.Format(" ({0})", Settings.Default.KeySelectionTriggerMouseDownUpButton)); break; } message.AppendLine(string.Format("Key selection: {0}", keySelectionSb)); var pointSelectionSb = new StringBuilder(); pointSelectionSb.Append(Settings.Default.PointSelectionTriggerSource.ToDescription()); switch (Settings.Default.PointSelectionTriggerSource) { case TriggerSources.Fixations: pointSelectionSb.Append(string.Format(" ({0:#,###}ms)", Settings.Default.PointSelectionTriggerFixationCompleteTime.TotalMilliseconds)); break; case TriggerSources.KeyboardKeyDownsUps: pointSelectionSb.Append(string.Format(" ({0})", Settings.Default.PointSelectionTriggerKeyboardKeyDownUpKey)); break; case TriggerSources.MouseButtonDownUps: pointSelectionSb.Append(string.Format(" ({0})", Settings.Default.PointSelectionTriggerMouseDownUpButton)); break; } message.AppendLine(string.Format("Point selection: {0}", pointSelectionSb)); message.AppendLine("Management console: ALT + M"); message.AppendLine("Website: www.optikey.org"); inputService.State = RunningStates.Paused; audioService.PlaySound(Settings.Default.InfoSoundFile, Settings.Default.InfoSoundVolume); mainViewModel.RaiseToastNotification( "OptiKey : Type · Click · Speak", message.ToString(), NotificationTypes.Normal, () => { inputService.State = RunningStates.Running; taskCompletionSource.SetResult(true); }); } else { taskCompletionSource.SetResult(false); } return await taskCompletionSource.Task; }