public ApplicationUpdaterViewModel(
            [NotNull] IApplicationUpdaterModel updaterModel,
            [NotNull] IConfigProvider <UpdateSettingsConfig> configProvider,
            [NotNull][Dependency(WellKnownSchedulers.UI)] IScheduler uiScheduler,
            [NotNull][Dependency(WellKnownSchedulers.Background)] IScheduler bgScheduler)
        {
            Guard.ArgumentNotNull(updaterModel, nameof(updaterModel));
            Guard.ArgumentNotNull(uiScheduler, nameof(uiScheduler));
            Guard.ArgumentNotNull(bgScheduler, nameof(bgScheduler));
            Guard.ArgumentNotNull(configProvider, nameof(configProvider));

            this.updaterModel = updaterModel;

            CheckForUpdatesCommand = CommandWrapper
                                     .Create(CheckForUpdatesCommandExecuted);

            CheckForUpdatesCommand
            .ThrownExceptions
            .Subscribe(ex => SetError($"Update error: {ex.Message}"))
            .AddTo(Anchors);

            this.RaiseWhenSourceValue(x => x.UpdatedVersion, updaterModel, x => x.UpdatedVersion, uiScheduler).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.LatestVersion, updaterModel, x => x.LatestVersion, uiScheduler).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.ProgressPercent, updaterModel, x => x.ProgressPercent, uiScheduler).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.IsBusy, updaterModel, x => x.IsBusy, uiScheduler).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.UpdateSource, updaterModel, x => x.UpdateSource, uiScheduler).AddTo(Anchors);

            RestartCommand = CommandWrapper
                             .Create(RestartCommandExecuted);

            RestartCommand
            .ThrownExceptions
            .Subscribe(ex => SetError($"Restart error: {ex.Message}"))
            .AddTo(Anchors);

            configProvider
            .ListenTo(x => x.UpdateSource)
            .Subscribe(x => updaterModel.UpdateSource = x)
            .AddTo(Anchors);

            Observable.Merge(
                configProvider
                .ListenTo(x => x.AutoUpdateTimeout)
                .WithPrevious((prev, curr) => new { prev, curr })
                .Do(timeout => Log.Debug($"[ApplicationUpdaterViewModel] AutoUpdate timeout changed: {timeout.prev} => {timeout.curr}"))
                .Select(
                    timeout => timeout.curr <= TimeSpan.Zero
                                ? Observable.Never <long>()
                                : Observable.Timer(DateTimeOffset.MinValue, timeout.curr, bgScheduler))
                .Switch()
                .ToUnit(),
                updaterModel.WhenAnyProperty(x => x.UpdateSource).ToUnit())
            .ObserveOn(uiScheduler)
            .Subscribe(() => CheckForUpdatesCommand.Execute(null), Log.HandleException)
            .AddTo(Anchors);

            ApplyUpdate = CommandWrapper.Create(
                ApplyUpdateCommandExecuted,
                this.updaterModel.WhenAnyValue(x => x.LatestVersion).ObserveOn(uiScheduler).Select(x => x != null));
        }
Esempio n. 2
0
        public OverlayAuraViewModel(
            OverlayAuraProperties initialProperties,
            [NotNull] IFactory <IPropertyEditorViewModel> propertiesEditorFactory,
            [NotNull] IFactory <IOverlayAuraModel> auraModelFactory)
        {
            this.auraModelFactory = auraModelFactory;
            loadedModelAnchors.AddTo(Anchors);
            RenameCommand = new DelegateCommand <string>(RenameCommandExecuted);

            this.RaiseWhenSourceValue(x => x.TabName, tabName, x => x.Value).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.DefaultTabName, tabName, x => x.DefaultValue).AddTo(Anchors);

            GeneralEditor = propertiesEditorFactory.Create();

            Properties = initialProperties;
            IsEnabled  = properties.IsEnabled;
            Id         = properties.Id;

            tabName.SetValue(properties.Name);
            tabName.SetDefaultValue(properties.Name);

            this.WhenAnyValue(x => x.IsEnabled)
            .Subscribe(() => Model = ReloadModel())
            .AddTo(Anchors);

            EnableCommand = CommandWrapper.Create(() => IsEnabled = true);
        }
Esempio n. 3
0
 public MessageBoxViewModel(
     [NotNull] IClipboardManager clipboardManager
     )
 {
     CloseMessageBoxCommand = CommandWrapper.Create(() => IsOpen = false);
     CopyAllCommand         = CommandWrapper.Create(() => clipboardManager.SetText(Content));
 }
Esempio n. 4
0
        public ExceptionDialog()
        {
            activeWindowAnchors.AddTo(Anchors);

            this.RaiseWhenSourceValue(x => x.Title, this, x => x.Config).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.AppName, this, x => x.AppName).AddTo(Anchors);

            CloseCommand = CommandWrapper.Create(() => activeWindowAnchors.Disposable = null);
        }
Esempio n. 5
0
        public void TestSerializeCommandWrapped()
        {
            var c = new C {
                X = 1
            };
            var w        = CommandWrapper.Create(c);
            var json     = w.ToJson();
            var expected = "{ 'X' : 1 }".Replace("'", "\"");

            Assert.Equal(expected, json);
        }
        public PropertyEditorViewModel(
            [NotNull] IAuraRepository auraRepository)
        {
            this.auraRepository = auraRepository;
            activeValueEditorAnchors.AddTo(Anchors);

            closeCommand = CommandWrapper.Create(CloseCommandExecuted, CloseCommandCanExecute);

            this.WhenAnyValue(x => x.Value)
            .Subscribe(Reinitialize)
            .AddTo(Anchors);
        }
        public PrismModuleStatusViewModel(
            IModuleCatalog moduleCatalog,
            IModuleManager moduleManager)
        {
            IsVisible = AppArguments.Instance.IsDebugMode;
            
            this.moduleManager = moduleManager;
            moduleList
                .Connect()
                .Bind(out var modules)
                .Subscribe()
                .AddTo(Anchors);

            Modules = modules;

            Observable.FromEventPattern<EventHandler<LoadModuleCompletedEventArgs>, LoadModuleCompletedEventArgs>(
                    h => moduleManager.LoadModuleCompleted += h,
                    h => moduleManager.LoadModuleCompleted -= h)
                .StartWithDefault()
                .Select(() => moduleCatalog.Modules.Select(x => new PrismModuleStatus(x)).ToArray())
                .DistinctUntilChanged()
                .Subscribe(
                    items =>
                    {
                        moduleList.Clear();
                        moduleList.AddRange(items);
                    })
                .AddTo(Anchors);

            allModulesLoaded = modules.ToObservableChangeSet()
                .Select(() => modules.Any() && modules.All(x => x.IsLoaded))
                .ToPropertyHelper(this, x => x.AllModulesLoaded)
                .AddTo(Anchors);
            
            LoadModuleCommand = CommandWrapper.Create<PrismModuleStatus>(LoadModuleCommandExecuted, LoadModuleCommandCanExecute);
        }
Esempio n. 8
0
        public MainWindowViewModel(
            [NotNull] IAppArguments appArguments,
            [NotNull] IFactory <IStartupManager, StartupManagerArgs> startupManagerFactory,
            [NotNull] IMicrophoneControllerEx microphoneController,
            [NotNull] IMicSwitchOverlayViewModel overlay,
            [NotNull] IAudioNotificationsManager audioNotificationsManager,
            [NotNull] IFactory <IAudioNotificationSelectorViewModel> audioSelectorFactory,
            [NotNull] IApplicationUpdaterViewModel appUpdater,
            [NotNull][Dependency(WellKnownWindows.MainWindow)] IWindowTracker mainWindowTracker,
            [NotNull] IConfigProvider <MicSwitchConfig> configProvider,
            [NotNull] IComplexHotkeyTracker hotkeyTracker,
            [NotNull] IMicrophoneProvider microphoneProvider,
            [NotNull] IImageProvider imageProvider,
            [NotNull] IViewController viewController,
            [NotNull][Dependency(WellKnownSchedulers.UI)] IScheduler uiScheduler)
        {
            var startupManagerArgs = new StartupManagerArgs
            {
                UniqueAppName   = $"{appArguments.AppName}{(appArguments.IsDebugMode ? "-debug" : string.Empty)}",
                ExecutablePath  = appUpdater.GetLatestExecutable().FullName,
                CommandLineArgs = appArguments.StartupArgs,
                AutostartFlag   = appArguments.AutostartFlag
            };

            this.startupManager = startupManagerFactory.Create(startupManagerArgs);

            this.appArguments         = appArguments;
            this.microphoneController = microphoneController;

            ApplicationUpdater     = appUpdater;
            this.mainWindowTracker = mainWindowTracker;
            this.configProvider    = configProvider;
            this.imageProvider     = imageProvider;
            this.RaiseWhenSourceValue(x => x.IsActive, mainWindowTracker, x => x.IsActive).AddTo(Anchors);

            AudioSelectorWhenMuted   = audioSelectorFactory.Create();
            AudioSelectorWhenUnmuted = audioSelectorFactory.Create();

            Observable.Merge(
                AudioSelectorWhenMuted.ObservableForProperty(x => x.SelectedValue, skipInitial: true),
                AudioSelectorWhenUnmuted.ObservableForProperty(x => x.SelectedValue, skipInitial: true))
            .Subscribe(() => this.RaisePropertyChanged(nameof(AudioNotification)), Log.HandleException)
            .AddTo(Anchors);

            configProvider.ListenTo(x => x.Notification)
            .ObserveOn(uiScheduler)
            .Subscribe(cfg =>
            {
                Log.Debug($"Applying new notification configuration: {cfg.DumpToTextRaw()} (current: {AudioNotification.DumpToTextRaw()})");
                AudioNotification = cfg;
            }, Log.HandleException)
            .AddTo(Anchors);

            configProvider.ListenTo(x => x.IsPushToTalkMode)
            .ObserveOn(uiScheduler)
            .Subscribe(x =>
            {
                IsPushToTalkMode = x;
                if (isPushToTalkMode)
                {
                    MuteMicrophoneCommand.Execute(true);
                }
            }, Log.HandleException)
            .AddTo(Anchors);

            configProvider.ListenTo(x => x.SuppressHotkey)
            .ObserveOn(uiScheduler)
            .Subscribe(x => SuppressHotkey = x, Log.HandleException)
            .AddTo(Anchors);

            Observable.Merge(configProvider.ListenTo(x => x.MicrophoneHotkey), configProvider.ListenTo(x => x.MicrophoneHotkeyAlt))
            .Select(x => new
            {
                Hotkey    = (HotkeyGesture) new HotkeyConverter().ConvertFrom(configProvider.ActualConfig.MicrophoneHotkey ?? string.Empty),
                HotkeyAlt = (HotkeyGesture) new HotkeyConverter().ConvertFrom(configProvider.ActualConfig.MicrophoneHotkeyAlt ?? string.Empty),
            })
            .ObserveOn(uiScheduler)
            .Subscribe(cfg =>
            {
                Log.Debug($"Setting new hotkeys configuration: {cfg.DumpToTextRaw()} (current: {hotkey}, alt: {hotkeyAlt})");
                Hotkey    = cfg.Hotkey;
                HotkeyAlt = cfg.HotkeyAlt;
            }, Log.HandleException)
            .AddTo(Anchors);

            Overlay = overlay;

            this.RaiseWhenSourceValue(x => x.RunAtLogin, startupManager, x => x.IsRegistered, uiScheduler).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.MicrophoneVolume, microphoneController, x => x.VolumePercent, uiScheduler).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.MicrophoneMuted, microphoneController, x => x.Mute, uiScheduler).AddTo(Anchors);
            ImageProvider = imageProvider;

            microphoneProvider.Microphones
            .ToObservableChangeSet()
            .ObserveOn(uiScheduler)
            .Bind(out var microphones)
            .Subscribe()
            .AddTo(Anchors);
            Microphones = microphones;

            this.ObservableForProperty(x => x.MicrophoneMuted, skipInitial: true)
            .DistinctUntilChanged()
            .Where(x => !MicrophoneLine.IsEmpty)
            .Skip(1)     // skip initial setup
            .Subscribe(x =>
            {
                var cfg = configProvider.ActualConfig.Notification;
                var notificationToPlay = x.Value ? cfg.On : cfg.Off;
                Log.Debug($"Playing notification {notificationToPlay} (cfg: {cfg.DumpToTextRaw()})");
                audioNotificationsManager.PlayNotification(notificationToPlay);
            }, Log.HandleUiException)
            .AddTo(Anchors);

            this.WhenAnyValue(x => x.MicrophoneLine)
            .DistinctUntilChanged()
            .Subscribe(x => microphoneController.LineId = x, Log.HandleUiException)
            .AddTo(Anchors);

            Observable.Merge(
                configProvider.ListenTo(x => x.MicrophoneLineId).ToUnit(),
                Microphones.ToObservableChangeSet().ToUnit())
            .Select(_ => configProvider.ActualConfig.MicrophoneLineId)
            .ObserveOn(uiScheduler)
            .Subscribe(configLineId =>
            {
                Log.Debug($"Microphone line configuration changed, lineId: {configLineId}, known lines: {Microphones.DumpToTextRaw()}");

                var micLine = Microphones.FirstOrDefault(line => line.Equals(configLineId));
                if (micLine.IsEmpty)
                {
                    Log.Debug($"Selecting first one of available microphone lines, known lines: {Microphones.DumpToTextRaw()}");
                    micLine = Microphones.FirstOrDefault();
                }
                MicrophoneLine = micLine;
                MuteMicrophoneCommand.ResetError();
            }, Log.HandleUiException)
            .AddTo(Anchors);

            hotkeyTracker
            .WhenAnyValue(x => x.IsActive)
            .ObserveOn(uiScheduler)
            .Subscribe(async isActive =>
            {
                if (isPushToTalkMode)
                {
                    await MuteMicrophoneCommandExecuted(!isActive);
                }
                else
                {
                    await MuteMicrophoneCommandExecuted(!MicrophoneMuted);
                }
            }, Log.HandleUiException)
            .AddTo(Anchors);

            ToggleOverlayLockCommand = CommandWrapper.Create(
                () =>
            {
                if (overlay.IsLocked && overlay.UnlockWindowCommand.CanExecute(null))
                {
                    overlay.UnlockWindowCommand.Execute(null);
                }
                else if (!overlay.IsLocked && overlay.LockWindowCommand.CanExecute(null))
                {
                    overlay.LockWindowCommand.Execute(null);
                }
            });

            ExitAppCommand = CommandWrapper.Create(
                () =>
            {
                Log.Debug("Closing application");
                configProvider.Save(configProvider.ActualConfig);
                Application.Current.Shutdown();
            });

            this.WhenAnyValue(x => x.WindowState)
            .Subscribe(x => ShowInTaskbar = x != WindowState.Minimized, Log.HandleUiException)
            .AddTo(Anchors);

            ShowAppCommand = CommandWrapper.Create(
                () =>
            {
                if (Visibility != Visibility.Visible)
                {
                    viewController.Show();
                }
                else
                {
                    viewController.Hide();
                }
            });

            OpenAppDataDirectoryCommand = CommandWrapper.Create(OpenAppDataDirectory);

            ResetOverlayPositionCommand = CommandWrapper.Create(ResetOverlayPositionCommandExecuted);

            RunAtLoginToggleCommand          = CommandWrapper.Create <bool>(RunAtLoginCommandExecuted);
            MuteMicrophoneCommand            = CommandWrapper.Create <bool>(MuteMicrophoneCommandExecuted);
            SelectMicrophoneIconCommand      = CommandWrapper.Create(SelectMicrophoneIconCommandExecuted);
            SelectMutedMicrophoneIconCommand = CommandWrapper.Create(SelectMutedMicrophoneIconCommandExecuted);
            ResetMicrophoneIconsCommand      = CommandWrapper.Create(ResetMicrophoneIconsCommandExecuted);

            var executingAssemblyName = Assembly.GetExecutingAssembly().GetName();

            Title = $"{(appArguments.IsDebugMode ? "[D]" : "")} {executingAssemblyName.Name} v{executingAssemblyName.Version}";

            WindowState = WindowState.Minimized;
            viewController
            .WhenLoaded
            .Take(1)
            .Select(() => configProvider.ListenTo(y => y.StartMinimized))
            .Switch()
            .Take(1)
            .ObserveOn(uiScheduler)
            .Subscribe(
                x =>
            {
                if (x)
                {
                    Log.Debug($"StartMinimized option is active - minimizing window, current state: {WindowState}");
                    StartMinimized = true;
                    viewController.Hide();
                }
                else
                {
                    Log.Debug($"StartMinimized option is not active - showing window as Normal, current state: {WindowState}");
                    StartMinimized = false;
                    viewController.Show();
                }
            }, Log.HandleUiException)
            .AddTo(Anchors);

            // config processing
            Observable.Merge(
                this.ObservableForProperty(x => x.MicrophoneLine, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.IsPushToTalkMode, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.AudioNotification, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.HotkeyAlt, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.Hotkey, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.SuppressHotkey, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.StartMinimized, skipInitial: true).ToUnit())
            .Throttle(ConfigThrottlingTimeout)
            .ObserveOn(uiScheduler)
            .Subscribe(() =>
            {
                var config = configProvider.ActualConfig.CloneJson();
                config.IsPushToTalkMode    = IsPushToTalkMode;
                config.MicrophoneHotkey    = (Hotkey ?? new HotkeyGesture()).ToString();
                config.MicrophoneHotkeyAlt = (HotkeyAlt ?? new HotkeyGesture()).ToString();
                config.MicrophoneLineId    = MicrophoneLine;
                config.Notification        = AudioNotification;
                config.SuppressHotkey      = SuppressHotkey;
                config.StartMinimized      = StartMinimized;
                configProvider.Save(config);
            }, Log.HandleUiException)
            .AddTo(Anchors);
        }
Esempio n. 9
0
        public MainWindowViewModel(
            IAppArguments appArguments,
            IApplicationAccessor applicationAccessor,
            IFactory <IStartupManager, StartupManagerArgs> startupManagerFactory,
            IMicSwitchOverlayViewModel overlay,
            IMicrophoneControllerViewModel microphoneControllerViewModel,
            IOverlayWindowController overlayWindowController,
            IWaveOutDeviceSelectorViewModel waveOutDeviceSelector,
            IAudioNotificationsManager audioNotificationsManager,
            IFactory <IAudioNotificationSelectorViewModel> audioSelectorFactory,
            IApplicationUpdaterViewModel appUpdater,
            [Dependency(WellKnownWindows.MainWindow)] IWindowTracker mainWindowTracker,
            IConfigProvider <MicSwitchConfig> configProvider,
            IConfigProvider <MicSwitchOverlayConfig> overlayConfigProvider,
            IImageProvider imageProvider,
            IErrorMonitorViewModel errorMonitor,
            IAudioNotificationsManager notificationsManager,
            IWindowViewController viewController,
            [Dependency(WellKnownSchedulers.UI)] IScheduler uiScheduler)
        {
            Title = $"{(appArguments.IsDebugMode ? "[D]" : "")} {appArguments.AppName} v{appArguments.Version}";

            this.appArguments          = appArguments;
            this.applicationAccessor   = applicationAccessor;
            this.MicrophoneController  = microphoneControllerViewModel.AddTo(Anchors);
            this.mainWindowTracker     = mainWindowTracker;
            this.configProvider        = configProvider;
            this.overlayConfigProvider = overlayConfigProvider;
            this.notificationsManager  = notificationsManager;
            this.viewController        = viewController;
            ApplicationUpdater         = appUpdater.AddTo(Anchors);
            WaveOutDeviceSelector      = waveOutDeviceSelector;
            ImageProvider            = imageProvider;
            ErrorMonitor             = errorMonitor;
            AudioSelectorWhenMuted   = audioSelectorFactory.Create().AddTo(Anchors);
            AudioSelectorWhenUnmuted = audioSelectorFactory.Create().AddTo(Anchors);
            WindowState = WindowState.Minimized;
            Overlay     = overlay.AddTo(Anchors);

            try
            {
                var startupManagerArgs = new StartupManagerArgs
                {
                    UniqueAppName   = $"{appArguments.AppName}{(appArguments.IsDebugMode ? "-debug" : string.Empty)}",
                    ExecutablePath  = appUpdater.LauncherExecutable.FullName,
                    CommandLineArgs = appArguments.StartupArgs,
                    AutostartFlag   = appArguments.AutostartFlag
                };
                this.startupManager     = startupManagerFactory.Create(startupManagerArgs);
                RunAtLoginToggleCommand = CommandWrapper.Create <bool>(RunAtLoginCommandExecuted, Observable.Return(startupManager?.IsReady ?? false));
            }
            catch (Exception e)
            {
                Log.Warn("Failed to initialize startup manager", e);
            }

            this.RaiseWhenSourceValue(x => x.IsActive, mainWindowTracker, x => x.IsActive, uiScheduler).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.RunAtLogin, startupManager, x => x.IsRegistered, uiScheduler).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.ShowOverlaySettings, Overlay, x => x.OverlayVisibilityMode).AddTo(Anchors);

            audioNotificationSource = Observable.Merge(
                AudioSelectorWhenMuted.ObservableForProperty(x => x.SelectedValue, skipInitial: true),
                AudioSelectorWhenUnmuted.ObservableForProperty(x => x.SelectedValue, skipInitial: true))
                                      .Select(x => new TwoStateNotification
            {
                On  = AudioSelectorWhenUnmuted.SelectedValue,
                Off = AudioSelectorWhenMuted.SelectedValue
            })
                                      .ToProperty(this, x => x.AudioNotification)
                                      .AddTo(Anchors);

            this.WhenAnyValue(x => x.AudioNotificationVolume)
            .Subscribe(x =>
            {
                AudioSelectorWhenUnmuted.Volume = AudioSelectorWhenMuted.Volume = x;
            })
            .AddTo(Anchors);

            MicrophoneController.ObservableForProperty(x => x.MicrophoneMuted, skipInitial: true)
            .DistinctUntilChanged()
            .Where(x => !MicrophoneController.MicrophoneLine.IsEmpty)
            .Select(isMuted => (isMuted.Value ? AudioNotification.Off : AudioNotification.On) ?? default(AudioNotificationType).ToString())
            .Where(notificationToPlay => !string.IsNullOrEmpty(notificationToPlay))
            .Select(notificationToPlay => Observable.FromAsync(async token =>
            {
                Log.Debug($"Playing notification {notificationToPlay}, volume: {audioNotificationVolume}");
                try
                {
                    await audioNotificationsManager.PlayNotification(notificationToPlay, audioNotificationVolume, waveOutDeviceSelector.SelectedItem, token);
                    Log.Debug($"Played notification {notificationToPlay}");
                }
                catch (Exception ex)
                {
                    Log.Debug($"Failed to play notification {notificationToPlay}", ex);
                }
            }))
            .Switch()
            .SubscribeToErrors(Log.HandleUiException)
            .AddTo(Anchors);

            this.WhenAnyValue(x => x.WindowState)
            .SubscribeSafe(x => ShowInTaskbar = x != WindowState.Minimized, Log.HandleUiException)
            .AddTo(Anchors);

            viewController
            .WhenClosing
            .SubscribeSafe(x => HandleWindowClosing(viewController, x), Log.HandleUiException)
            .AddTo(Anchors);

            ToggleOverlayLockCommand         = CommandWrapper.Create(ToggleOverlayCommandExecuted);
            ExitAppCommand                   = CommandWrapper.Create(ExitAppCommandExecuted);
            ShowAppCommand                   = CommandWrapper.Create(ShowAppCommandExecuted);
            OpenAppDataDirectoryCommand      = CommandWrapper.Create(OpenAppDataDirectory);
            ResetOverlayPositionCommand      = CommandWrapper.Create(ResetOverlayPositionCommandExecuted);
            RunAtLoginToggleCommand          = CommandWrapper.Create <bool>(RunAtLoginCommandExecuted, startupManager.WhenAnyValue(x => x.IsReady));
            SelectMicrophoneIconCommand      = CommandWrapper.Create(SelectMicrophoneIconCommandExecuted);
            SelectMutedMicrophoneIconCommand = CommandWrapper.Create(SelectMutedMicrophoneIconCommandExecuted);
            ResetMicrophoneIconsCommand      = CommandWrapper.Create(ResetMicrophoneIconsCommandExecuted);
            AddSoundCommand                  = CommandWrapper.Create(AddSoundCommandExecuted);

            Observable.Merge(configProvider.ListenTo(x => x.Notifications).ToUnit(), configProvider.ListenTo(x => x.NotificationVolume).ToUnit())
            .Select(_ => new { configProvider.ActualConfig.Notifications, configProvider.ActualConfig.NotificationVolume })
            .ObserveOn(uiScheduler)
            .SubscribeSafe(cfg =>
            {
                Log.Debug($"Applying new notification configuration: {cfg.DumpToTextRaw()} (current: {AudioNotification.DumpToTextRaw()}, volume: {AudioNotificationVolume})");
                AudioSelectorWhenMuted.SelectedValue   = cfg.Notifications.Off;
                AudioSelectorWhenUnmuted.SelectedValue = cfg.Notifications.On;
                AudioNotificationVolume = cfg.NotificationVolume;
            }, Log.HandleException)
            .AddTo(Anchors);

            configProvider.ListenTo(x => x.MinimizeOnClose)
            .ObserveOn(uiScheduler)
            .SubscribeSafe(x => MinimizeOnClose = x, Log.HandleException)
            .AddTo(Anchors);

            configProvider.ListenTo(x => x.OutputDeviceId)
            .ObserveOn(uiScheduler)
            .SubscribeSafe(x => WaveOutDeviceSelector.SelectById(x), Log.HandleException)
            .AddTo(Anchors);

            viewController
            .WhenLoaded
            .Take(1)
            .Select(_ => configProvider.ListenTo(y => y.StartMinimized))
            .Switch()
            .Take(1)
            .ObserveOn(uiScheduler)
            .SubscribeSafe(
                x =>
            {
                if (x)
                {
                    Log.Debug($"StartMinimized option is active - minimizing window, current state: {WindowState}");
                    StartMinimized = true;
                    viewController.Hide();
                }
                else
                {
                    Log.Debug($"StartMinimized option is not active - showing window as Normal, current state: {WindowState}");
                    StartMinimized = false;
                    viewController.Show();
                }
            }, Log.HandleUiException)
            .AddTo(Anchors);

            Observable.Merge(
                microphoneControllerViewModel.ObservableForProperty(x => x.MuteMode, skipInitial: true).ToUnit(),
                waveOutDeviceSelector.ObservableForProperty(x => x.SelectedItem, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.AudioNotification, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.MinimizeOnClose, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.Width, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.Height, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.Top, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.Left, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.AudioNotificationVolume, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.StartMinimized, skipInitial: true).ToUnit())
            .Throttle(ConfigThrottlingTimeout)
            .ObserveOn(uiScheduler)
            .SubscribeSafe(() =>
            {
                var config                = configProvider.ActualConfig.CloneJson();
                config.Notifications      = AudioNotification;
                config.NotificationVolume = AudioNotificationVolume;
                config.StartMinimized     = StartMinimized;
                config.MinimizeOnClose    = MinimizeOnClose;
                config.OutputDeviceId     = waveOutDeviceSelector.SelectedItem?.Id;
                config.MainWindowBounds   = new Rect(Left, Top, Width, Height);
                configProvider.Save(config);
            }, Log.HandleUiException)
            .AddTo(Anchors);

            viewController.WhenLoaded
            .SubscribeSafe(() =>
            {
                Log.Debug($"Main window loaded - loading overlay, current process({CurrentProcess.ProcessName} 0x{CurrentProcess.Id:x8}) main window: {CurrentProcess.MainWindowHandle} ({CurrentProcess.MainWindowTitle})");
                overlayWindowController.RegisterChild(Overlay).AddTo(Anchors);
                Log.Debug("Overlay loaded successfully");
            }, Log.HandleUiException)
            .AddTo(Anchors);

            configProvider.ListenTo(x => x.MainWindowBounds)
            .WithPrevious()
            .ObserveOn(uiScheduler)
            .SubscribeSafe(x =>
            {
                Log.Debug($"Main window config bounds updated: {x}");

                Rect bounds;
                if (x.Current == null)
                {
                    var monitorBounds = UnsafeNative.GetMonitorBounds(Rectangle.Empty).ScaleToWpf();
                    var monitorCenter = monitorBounds.Center();
                    bounds            = new Rect(
                        monitorCenter.X - DefaultSize.Width / 2f,
                        monitorCenter.Y - DefaultSize.Height / 2f,
                        DefaultSize.Width,
                        DefaultSize.Height);
                }
                else
                {
                    bounds = x.Current.Value;
                }

                Left   = bounds.Left;
                Top    = bounds.Top;
                Width  = bounds.Width;
                Height = bounds.Height;
            }, Log.HandleUiException)
            .AddTo(Anchors);

            var theme = Theme.Create(
                Theme.Light,
                primary: SwatchHelper.Lookup[(MaterialDesignColor)PrimaryColor.BlueGrey],
                accent: SwatchHelper.Lookup[(MaterialDesignColor)SecondaryColor.LightBlue]);
            var paletteHelper = new PaletteHelper();

            paletteHelper.SetTheme(theme);
        }
Esempio n. 10
0
        public MicrophoneControllerViewModel(
            IMicrophoneControllerEx microphoneController,
            IMicrophoneProvider microphoneProvider,
            IComplexHotkeyTracker hotkeyTracker,
            IFactory <IHotkeyTracker> hotkeyTrackerFactory,
            IFactory <IHotkeyEditorViewModel> hotkeyEditorFactory,
            IConfigProvider <MicSwitchConfig> configProvider,
            IConfigProvider <MicSwitchHotkeyConfig> hotkeyConfigProvider,
            [Dependency(WellKnownSchedulers.UI)] IScheduler uiScheduler)
        {
            microphoneProvider.Microphones
            .ToObservableChangeSet()
            .ObserveOn(uiScheduler)
            .Bind(out var microphones)
            .SubscribeToErrors(Log.HandleUiException)
            .AddTo(Anchors);
            Microphones = microphones;

            this.microphoneController = microphoneController;
            this.hotkeyTrackerFactory = hotkeyTrackerFactory;
            this.hotkeyEditorFactory  = hotkeyEditorFactory;
            this.hotkeyConfigProvider = hotkeyConfigProvider;
            this.uiScheduler          = uiScheduler;
            MuteMicrophoneCommand     = CommandWrapper.Create <object>(MuteMicrophoneCommandExecuted);
            Hotkey           = PrepareHotkey("Mute/Un-mute microphone", x => x.Hotkey, (config, hotkeyConfig) => config.Hotkey = hotkeyConfig);
            HotkeyToggle     = PrepareHotkey("Toggle microphone state", x => x.HotkeyForToggle, (config, hotkeyConfig) => config.HotkeyForToggle = hotkeyConfig);
            HotkeyMute       = PrepareHotkey("Mute microphone", x => x.HotkeyForMute, (config, hotkeyConfig) => config.HotkeyForMute = hotkeyConfig);
            HotkeyUnmute     = PrepareHotkey("Un-mute microphone", x => x.HotkeyForUnmute, (config, hotkeyConfig) => config.HotkeyForUnmute = hotkeyConfig);
            HotkeyPushToMute = PrepareHotkey("Push-To-Mute", x => x.HotkeyForPushToMute, (config, hotkeyConfig) => config.HotkeyForPushToMute = hotkeyConfig);
            HotkeyPushToTalk = PrepareHotkey("Push-To-Talk", x => x.HotkeyForPushToTalk, (config, hotkeyConfig) => config.HotkeyForPushToTalk = hotkeyConfig);

            PrepareTracker(HotkeyMode.Click, HotkeyToggle)
            .ObservableForProperty(x => x.IsActive, skipInitial: true)
            .SubscribeSafe(x =>
            {
                Log.Debug($"[{x.Sender}] Toggling microphone state: {microphoneController}");
                microphoneController.Mute = !microphoneController.Mute;
            }, Log.HandleUiException)
            .AddTo(Anchors);

            PrepareTracker(HotkeyMode.Hold, HotkeyMute)
            .ObservableForProperty(x => x.IsActive, skipInitial: true)
            .Where(x => x.Value)
            .SubscribeSafe(x =>
            {
                Log.Debug($"[{x.Sender}] Muting microphone: {microphoneController}");
                microphoneController.Mute = true;
            }, Log.HandleUiException)
            .AddTo(Anchors);

            PrepareTracker(HotkeyMode.Hold, HotkeyUnmute)
            .ObservableForProperty(x => x.IsActive, skipInitial: true)
            .Where(x => x.Value)
            .SubscribeSafe(x =>
            {
                Log.Debug($"[{x.Sender}] Un-muting microphone: {microphoneController}");
                microphoneController.Mute = false;
            }, Log.HandleUiException)
            .AddTo(Anchors);

            PrepareTracker(HotkeyMode.Hold, HotkeyPushToTalk)
            .ObservableForProperty(x => x.IsActive, skipInitial: true)
            .SubscribeSafe(x =>
            {
                Log.Debug($"[{x.Sender}] Processing push-to-talk hotkey for microphone: {microphoneController}");
                microphoneController.Mute = !x.Value;
            }, Log.HandleUiException)
            .AddTo(Anchors);

            PrepareTracker(HotkeyMode.Hold, HotkeyPushToMute)
            .ObservableForProperty(x => x.IsActive, skipInitial: true)
            .SubscribeSafe(x =>
            {
                Log.Debug($"[{x.Sender}] Processing push-to-mute hotkey for microphone: {microphoneController}");
                microphoneController.Mute = x.Value;
            }, Log.HandleUiException)
            .AddTo(Anchors);

            this.RaiseWhenSourceValue(x => x.MicrophoneVolume, microphoneController, x => x.VolumePercent, uiScheduler).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.MicrophoneMuted, microphoneController, x => x.Mute, uiScheduler).AddTo(Anchors);

            this.WhenAnyValue(x => x.MicrophoneLine)
            .DistinctUntilChanged()
            .SubscribeSafe(x => microphoneController.LineId = x, Log.HandleUiException)
            .AddTo(Anchors);

            hotkeyConfigProvider.ListenTo(x => x.MuteMode)
            .ObserveOn(uiScheduler)
            .Subscribe(x =>
            {
                Log.Debug($"Mute mode loaded from config: {x}");
                MuteMode = x;
            })
            .AddTo(Anchors);

            hotkeyConfigProvider.ListenTo(x => x.EnableAdvancedHotkeys)
            .ObserveOn(uiScheduler)
            .Subscribe(x => EnableAdditionalHotkeys = x)
            .AddTo(Anchors);

            hotkeyConfigProvider.ListenTo(x => x.InitialMicrophoneState)
            .ObserveOn(uiScheduler)
            .Subscribe(x => InitialMicrophoneState = x)
            .AddTo(Anchors);

            configProvider.ListenTo(x => x.VolumeControlEnabled)
            .ObserveOn(uiScheduler)
            .SubscribeSafe(x => MicrophoneVolumeControlEnabled = x, Log.HandleException)
            .AddTo(Anchors);

            Observable.Merge(
                configProvider.ListenTo(x => x.MicrophoneLineId).ToUnit(),
                Microphones.ToObservableChangeSet().ToUnit())
            .Select(_ => configProvider.ActualConfig.MicrophoneLineId)
            .ObserveOn(uiScheduler)
            .SubscribeSafe(configLineId =>
            {
                Log.Debug($"Microphone line configuration changed, lineId: {configLineId}, known lines: {Microphones.DumpToTextRaw()}");

                var micLine = Microphones.FirstOrDefault(line => line.Equals(configLineId));
                if (micLine.IsEmpty)
                {
                    Log.Debug($"Selecting first one of available microphone lines, known lines: {Microphones.DumpToTextRaw()}");
                    micLine = Microphones.FirstOrDefault();
                }
                MicrophoneLine = micLine;
                MuteMicrophoneCommand.ResetError();
            }, Log.HandleUiException)
            .AddTo(Anchors);

            this.WhenAnyValue(x => x.MuteMode, x => x.InitialMicrophoneState)
            .ObserveOn(uiScheduler)
            .SubscribeSafe(_ =>
            {
                Log.Debug($"Processing muteMode: {muteMode}, {microphoneController}.Mute: {microphoneController.Mute}");
                switch (muteMode)
                {
                case MuteMode.PushToTalk:
                    Log.Debug($"{muteMode} mute mode is enabled, un-muting microphone");
                    microphoneController.Mute = true;
                    break;

                case MuteMode.PushToMute:
                    microphoneController.Mute = false;
                    Log.Debug($"{muteMode} mute mode is enabled, muting microphone");
                    break;

                case MuteMode.ToggleMute when initialMicrophoneState == MicrophoneState.Mute:
                    Log.Debug($"{muteMode} enabled, muting microphone");
                    microphoneController.Mute = true;
                    break;

                case MuteMode.ToggleMute when initialMicrophoneState == MicrophoneState.Unmute:
                    Log.Debug($"{muteMode} enabled, un-muting microphone");
                    microphoneController.Mute = false;
                    break;

                default:
                    Log.Debug($"{muteMode} enabled, action is not needed");
                    break;
                }
            }, Log.HandleUiException)
            .AddTo(Anchors);

            hotkeyTracker
            .WhenAnyValue(x => x.IsActive)
            .Skip(1)
            .ObserveOn(uiScheduler)
            .SubscribeSafe(async isActive =>
            {
                Log.Debug($"Handling hotkey press (isActive: {isActive}), mute mode: {muteMode}");
                switch (muteMode)
                {
                case MuteMode.PushToTalk:
                    microphoneController.Mute = !isActive;
                    break;

                case MuteMode.PushToMute:
                    microphoneController.Mute = isActive;
                    break;

                case MuteMode.ToggleMute:
                    microphoneController.Mute = !microphoneController.Mute;
                    break;

                default:
                    throw new ArgumentOutOfRangeException(nameof(muteMode), muteMode, @"Unsupported mute mode");
                }
            }, Log.HandleUiException)
            .AddTo(Anchors);

            Observable.Merge(
                this.ObservableForProperty(x => x.MuteMode, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.EnableAdditionalHotkeys, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.InitialMicrophoneState, skipInitial: true).ToUnit(),
                Hotkey.ObservableForProperty(x => x.Properties, skipInitial: true).ToUnit())
            .Throttle(ConfigThrottlingTimeout)
            .ObserveOn(uiScheduler)
            .SubscribeSafe(() =>
            {
                var hotkeyConfig      = hotkeyConfigProvider.ActualConfig.CloneJson();
                hotkeyConfig.Hotkey   = Hotkey.Properties;
                hotkeyConfig.MuteMode = muteMode;
                hotkeyConfig.EnableAdvancedHotkeys  = enableAdvancedHotkeys;
                hotkeyConfig.InitialMicrophoneState = initialMicrophoneState;
                hotkeyConfigProvider.Save(hotkeyConfig);
            }, Log.HandleUiException)
            .AddTo(Anchors);

            Observable.Merge(
                this.ObservableForProperty(x => x.MicrophoneLine, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.MicrophoneVolumeControlEnabled, skipInitial: true).ToUnit())
            .Throttle(ConfigThrottlingTimeout)
            .ObserveOn(uiScheduler)
            .SubscribeSafe(() =>
            {
                var config = configProvider.ActualConfig.CloneJson();
                config.MicrophoneLineId     = microphoneLine;
                config.VolumeControlEnabled = microphoneVolumeControlEnabled;
                configProvider.Save(config);
            }, Log.HandleUiException)
            .AddTo(Anchors);
        }
Esempio n. 11
0
        public MicSwitchOverlayViewModel(
            IOverlayWindowController overlayWindowController,
            IMicrophoneControllerEx microphoneController,
            IConfigProvider<MicSwitchOverlayConfig> configProvider,
            IImageProvider imageProvider,
            [Dependency(WellKnownSchedulers.UI)] IScheduler uiScheduler)
        {
            this.overlayWindowController = overlayWindowController;
            this.microphoneController = microphoneController;
            this.configProvider = configProvider;
            this.imageProvider = imageProvider;
            OverlayMode = OverlayMode.Transparent;
            MinSize = new Size(40, 40);
            MaxSize = new Size(300, 300);
            DefaultSize = new Size(120, 120);
            SizeToContent = SizeToContent.Manual;
            TargetAspectRatio = MinSize.Width / MinSize.Height;
            IsUnlockable = true;
            Title = "MicSwitch";
            IsEnabled = true;

            this.WhenAnyValue(x => x.IsLocked)
                .SubscribeSafe(isLocked => OverlayMode = isLocked ? OverlayMode.Transparent : OverlayMode.Layered, Log.HandleUiException)
                .AddTo(Anchors);

            this.RaiseWhenSourceValue(x => x.IsEnabled, overlayWindowController, x => x.IsEnabled).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.Mute, microphoneController, x => x.Mute, uiScheduler).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.MicrophoneImage, imageProvider, x => x.MicrophoneImage, uiScheduler).AddTo(Anchors);
            
            ToggleLockStateCommand = CommandWrapper.Create(
                () =>
                {
                    if (IsLocked && UnlockWindowCommand.CanExecute(null))
                    {
                        UnlockWindowCommand.Execute(null);
                    }
                    else if (!IsLocked && LockWindowCommand.CanExecute(null))
                    {
                        LockWindowCommand.Execute(null);
                    }
                    else
                    {
                        throw new ApplicationException($"Something went wrong - invalid Overlay Lock state: {new {IsLocked, IsUnlockable, CanUnlock = UnlockWindowCommand.CanExecute(null), CanLock = LockWindowCommand.CanExecute(null)  }}");
                    }
                });

            this.WhenAnyValue(x => x.IsEnabled)
                .Where(x => !IsEnabled && !IsLocked)
                .ObserveOn(uiScheduler)
                .SubscribeSafe(() => LockWindowCommand.Execute(null), Log.HandleUiException)
                .AddTo(Anchors);

            this.WhenAnyValue(x => x.OverlayWindow)
                .Where(x => x != null)
                .SubscribeSafe(x => x.LogWndProc("MicOverlay").AddTo(Anchors), Log.HandleUiException)
                .AddTo(Anchors);

            this.WhenAnyValue(x => x.OverlayVisibilityMode, x => x.Mute)
                .Select(_ =>
                {
                    return OverlayVisibilityMode switch
                    {
                        OverlayVisibilityMode.Always => true,
                        OverlayVisibilityMode.Never => false,
                        OverlayVisibilityMode.WhenMuted => Mute,
                        OverlayVisibilityMode.WhenUnmuted => !Mute,
                        _ => throw new ArgumentOutOfRangeException(nameof(overlayVisibilityMode), overlayVisibilityMode, "Unknown visibility mode")
                    };
                })
                .DistinctUntilChanged()
                .ObserveOn(uiScheduler)
                .SubscribeSafe(x => IsEnabled = x, Log.HandleUiException)
                .AddTo(Anchors);
            
            WhenLoaded
                .Take(1)
                .SubscribeSafe(_ =>
                {
                    configProvider
                        .WhenChanged
                        .ObserveOn(uiScheduler)
                        .SubscribeSafe(LoadConfig, Log.HandleUiException)
                        .AddTo(Anchors);
                    
                    Observable.Merge(
                            this.ObservableForProperty(x => x.NativeBounds, skipInitial: true).ToUnit(),
                            this.ObservableForProperty(x => x.Opacity, skipInitial: true).ToUnit(),
                            this.ObservableForProperty(x => x.OverlayVisibilityMode, skipInitial: true).ToUnit(),
                            this.ObservableForProperty(x => x.IsEnabled, skipInitial: true).ToUnit(),
                            this.ObservableForProperty(x => x.IsLocked, skipInitial: true).ToUnit())
                        .SkipUntil(WhenLoaded)
                        .Throttle(ConfigThrottlingTimeout)
                        .ObserveOn(uiScheduler)
                        .SubscribeSafe(SaveConfig, Log.HandleUiException)
                        .AddTo(Anchors);
                }, Log.HandleUiException)
                .AddTo(Anchors);
        }
Esempio n. 12
0
        public MainWindowViewModel(
            IAppArguments appArguments,
            IFactory <IStartupManager, StartupManagerArgs> startupManagerFactory,
            IMicSwitchOverlayViewModel overlay,
            IMicrophoneControllerViewModel microphoneControllerViewModel,
            IOverlayWindowController overlayWindowController,
            IAudioNotificationsManager audioNotificationsManager,
            IFactory <IAudioNotificationSelectorViewModel> audioSelectorFactory,
            IApplicationUpdaterViewModel appUpdater,
            [Dependency(WellKnownWindows.MainWindow)] IWindowTracker mainWindowTracker,
            IConfigProvider <MicSwitchConfig> configProvider,
            IConfigProvider <MicSwitchOverlayConfig> overlayConfigProvider,
            IImageProvider imageProvider,
            IAudioNotificationsManager notificationsManager,
            IWindowViewController viewController,
            [Dependency(WellKnownSchedulers.UI)] IScheduler uiScheduler)
        {
            Title = $"{(appArguments.IsDebugMode ? "[D]" : "")} {appArguments.AppName} v{appArguments.Version}";

            this.appArguments          = appArguments;
            this.MicrophoneController  = microphoneControllerViewModel.AddTo(Anchors);
            this.mainWindowTracker     = mainWindowTracker;
            this.configProvider        = configProvider;
            this.overlayConfigProvider = overlayConfigProvider;
            this.notificationsManager  = notificationsManager;
            this.viewController        = viewController;
            ApplicationUpdater         = appUpdater.AddTo(Anchors);
            ImageProvider            = imageProvider;
            AudioSelectorWhenMuted   = audioSelectorFactory.Create().AddTo(Anchors);
            AudioSelectorWhenUnmuted = audioSelectorFactory.Create().AddTo(Anchors);
            WindowState = WindowState.Minimized;
            Overlay     = overlay.AddTo(Anchors);

            var startupManagerArgs = new StartupManagerArgs
            {
                UniqueAppName   = $"{appArguments.AppName}{(appArguments.IsDebugMode ? "-debug" : string.Empty)}",
                ExecutablePath  = appUpdater.GetLatestExecutable().FullName,
                CommandLineArgs = appArguments.StartupArgs,
                AutostartFlag   = appArguments.AutostartFlag
            };

            this.startupManager = startupManagerFactory.Create(startupManagerArgs);

            this.RaiseWhenSourceValue(x => x.IsActive, mainWindowTracker, x => x.IsActive, uiScheduler).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.RunAtLogin, startupManager, x => x.IsRegistered, uiScheduler).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.ShowOverlaySettings, Overlay, x => x.OverlayVisibilityMode).AddTo(Anchors);

            audioNotificationSource = Observable.Merge(
                AudioSelectorWhenMuted.ObservableForProperty(x => x.SelectedValue, skipInitial: true),
                AudioSelectorWhenUnmuted.ObservableForProperty(x => x.SelectedValue, skipInitial: true))
                                      .Select(x => new TwoStateNotification
            {
                On  = AudioSelectorWhenUnmuted.SelectedValue,
                Off = AudioSelectorWhenMuted.SelectedValue
            })
                                      .ToPropertyHelper(this, x => x.AudioNotification)
                                      .AddTo(Anchors);

            this.WhenAnyValue(x => x.AudioNotificationVolume)
            .Subscribe(x =>
            {
                AudioSelectorWhenUnmuted.Volume = AudioSelectorWhenMuted.Volume = x;
            })
            .AddTo(Anchors);

            MicrophoneController.ObservableForProperty(x => x.MicrophoneMuted, skipInitial: true)
            .DistinctUntilChanged()
            .Where(x => !MicrophoneController.MicrophoneLine.IsEmpty)
            .SubscribeSafe(x =>
            {
                var notificationToPlay = (x.Value ? AudioNotification.On : AudioNotification.Off) ?? default(AudioNotificationType).ToString();
                Log.Debug($"Playing notification {notificationToPlay} (cfg: {AudioNotification.DumpToTextRaw()})");
                audioNotificationsManager.PlayNotification(notificationToPlay, audioNotificationVolume);
            }, Log.HandleUiException)
            .AddTo(Anchors);

            this.WhenAnyValue(x => x.WindowState)
            .SubscribeSafe(x => ShowInTaskbar = x != WindowState.Minimized, Log.HandleUiException)
            .AddTo(Anchors);

            viewController
            .WhenClosing
            .SubscribeSafe(x => HandleWindowClosing(viewController, x), Log.HandleUiException)
            .AddTo(Anchors);

            ToggleOverlayLockCommand         = CommandWrapper.Create(ToggleOverlayCommandExecuted);
            ExitAppCommand                   = CommandWrapper.Create(ExitAppCommandExecuted);
            ShowAppCommand                   = CommandWrapper.Create(ShowAppCommandExecuted);
            OpenAppDataDirectoryCommand      = CommandWrapper.Create(OpenAppDataDirectory);
            ResetOverlayPositionCommand      = CommandWrapper.Create(ResetOverlayPositionCommandExecuted);
            RunAtLoginToggleCommand          = CommandWrapper.Create <bool>(RunAtLoginCommandExecuted);
            SelectMicrophoneIconCommand      = CommandWrapper.Create(SelectMicrophoneIconCommandExecuted);
            SelectMutedMicrophoneIconCommand = CommandWrapper.Create(SelectMutedMicrophoneIconCommandExecuted);
            ResetMicrophoneIconsCommand      = CommandWrapper.Create(ResetMicrophoneIconsCommandExecuted);
            AddSoundCommand                  = CommandWrapper.Create(AddSoundCommandExecuted);

            Observable.Merge(configProvider.ListenTo(x => x.Notification).ToUnit(), configProvider.ListenTo(x => x.NotificationVolume).ToUnit())
            .Select(_ => new { configProvider.ActualConfig.Notification, configProvider.ActualConfig.NotificationVolume })
            .ObserveOn(uiScheduler)
            .SubscribeSafe(cfg =>
            {
                Log.Debug($"Applying new notification configuration: {cfg.DumpToTextRaw()} (current: {AudioNotification.DumpToTextRaw()}, volume: {AudioNotificationVolume})");
                AudioSelectorWhenMuted.SelectedValue   = cfg.Notification.Off;
                AudioSelectorWhenUnmuted.SelectedValue = cfg.Notification.On;
                AudioNotificationVolume = cfg.NotificationVolume;
            }, Log.HandleException)
            .AddTo(Anchors);

            configProvider.ListenTo(x => x.MinimizeOnClose)
            .ObserveOn(uiScheduler)
            .SubscribeSafe(x => MinimizeOnClose = x, Log.HandleException)
            .AddTo(Anchors);

            viewController
            .WhenLoaded
            .Take(1)
            .Select(_ => configProvider.ListenTo(y => y.StartMinimized))
            .Switch()
            .Take(1)
            .ObserveOn(uiScheduler)
            .SubscribeSafe(
                x =>
            {
                if (x)
                {
                    Log.Debug($"StartMinimized option is active - minimizing window, current state: {WindowState}");
                    StartMinimized = true;
                    viewController.Hide();
                }
                else
                {
                    Log.Debug($"StartMinimized option is not active - showing window as Normal, current state: {WindowState}");
                    StartMinimized = false;
                    viewController.Show();
                }
            }, Log.HandleUiException)
            .AddTo(Anchors);

            configProvider.WhenChanged
            .Subscribe()
            .AddTo(Anchors);

            Observable.Merge(
                microphoneControllerViewModel.ObservableForProperty(x => x.MuteMode, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.AudioNotification, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.MinimizeOnClose, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.AudioNotificationVolume, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.StartMinimized, skipInitial: true).ToUnit())
            .Throttle(ConfigThrottlingTimeout)
            .ObserveOn(uiScheduler)
            .SubscribeSafe(() =>
            {
                var config                = configProvider.ActualConfig.CloneJson();
                config.Notification       = AudioNotification;
                config.NotificationVolume = AudioNotificationVolume;
                config.StartMinimized     = StartMinimized;
                config.MinimizeOnClose    = MinimizeOnClose;
                configProvider.Save(config);
            }, Log.HandleUiException)
            .AddTo(Anchors);

            viewController.WhenLoaded
            .SubscribeSafe(() =>
            {
                Log.Debug($"Main window loaded - loading overlay, current process({CurrentProcess.ProcessName} 0x{CurrentProcess.Id:x8}) main window: {CurrentProcess.MainWindowHandle} ({CurrentProcess.MainWindowTitle})");
                overlayWindowController.RegisterChild(Overlay).AddTo(Anchors);
                Log.Debug("Overlay loaded successfully");
            }, Log.HandleUiException)
            .AddTo(Anchors);

            var theme = Theme.Create(
                Theme.Light,
                primary: SwatchHelper.Lookup[(MaterialDesignColor)PrimaryColor.BlueGrey],
                accent: SwatchHelper.Lookup[(MaterialDesignColor)SecondaryColor.LightBlue]);
            var paletteHelper = new PaletteHelper();

            paletteHelper.SetTheme(theme);
        }
Esempio n. 13
0
        public EyeOverlayViewModel(
            [NotNull] [Dependency(WellKnownWindows.AllWindows)] IWindowTracker mainWindowTracker,
            [NotNull] IOverlayWindowController overlayWindowController,
            [NotNull] IAuraModelController auraModelController,
            [NotNull] IWindowListProvider windowListProvider,
            [NotNull] ISelectionAdornerViewModel selectionAdorner,
            [NotNull] [Dependency(WellKnownSchedulers.UI)] IScheduler uiScheduler)
        {
            using var sw = new BenchmarkTimer("Initialization", Log, nameof(EyeOverlayViewModel));
            SelectionAdorner = selectionAdorner.AddTo(Anchors);
            activeConfigEditorAnchors.AddTo(Anchors);
            this.mainWindowTracker = mainWindowTracker;
            this.overlayWindowController = overlayWindowController;
            this.auraModelController = auraModelController;
            this.windowListProvider = windowListProvider;
            MinSize = new Size(32, 32);
            SizeToContent = SizeToContent.Manual;
            Width = 400;
            Height = 400;
            Left = 200;
            Top = 200;
            IsUnlockable = true;
            Title = "EyeAuras";
            EnableHeader = false;
            thumbnailOpacity.SetDefaultValue(DefaultThumbnailOpacity);

            ResetRegionCommandExecuted();
            sw.Step("Basic properties initialized");
            
            WhenLoaded
                .Take(1)
                .Subscribe(ApplyConfig)
                .AddTo(Anchors);
            sw.Step("WhenLoaded executed");
            
            resetRegionCommand = CommandWrapper.Create(ResetRegionCommandExecuted, ResetRegionCommandCanExecute);
            selectRegionCommand = CommandWrapper.Create(SelectRegionCommandExecuted, SelectRegionCommandCanExecute);
            closeConfigEditorCommand = CommandWrapper.Create(CloseConfigEditorCommandExecuted);
            fitOverlayCommand = CommandWrapper.Create<double?>(FitOverlayCommandExecuted);
            setAttachedWindowCommand = CommandWrapper.Create<WindowHandle>(SetAttachedWindowCommandExecuted);
            setClickThroughCommand = CommandWrapper.Create<bool?>(SetClickThroughModeExecuted);
            DisableAuraCommand = CommandWrapper.Create(() => auraModelController.IsEnabled = false);
            CloseCommand = CommandWrapper.Create(CloseCommandExecuted, auraModelController.WhenAnyValue(x => x.CloseController).Select(CloseCommandCanExecute));
            ToggleLockStateCommand = CommandWrapper.Create(
                () =>
                {
                    if (IsLocked && UnlockWindowCommand.CanExecute(null))
                    {
                        UnlockWindowCommand.Execute(null);
                    }
                    else if (!IsLocked && LockWindowCommand.CanExecute(null))
                    {
                        LockWindowCommand.Execute(null);
                    }
                    else
                    {
                        throw new ApplicationException($"Something went wrong - invalid Overlay Lock state: {new {IsLocked, IsUnlockable, CanUnlock = UnlockWindowCommand.CanExecute(null), CanLock = LockWindowCommand.CanExecute(null)  }}");
                    }
                });

            auraModelController.WhenAnyValue(x => x.Name)
                .Where(x => !string.IsNullOrEmpty(x))
                .Subscribe(x => OverlayName = x)
                .AddTo(Anchors);

            this.RaiseWhenSourceValue(x => x.ActiveThumbnailOpacity, thumbnailOpacity, x => x.Value).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.ThumbnailOpacity, this, x => x.ActiveThumbnailOpacity).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.SourceBounds, Region, x => x.Bounds).AddTo(Anchors);

            isInEditMode = Observable.Merge(
                    this.WhenAnyProperty(x => x.IsInSelectMode, x => x.IsLocked))
                .Select(change => IsInSelectMode || !IsLocked)
                .ToPropertyHelper(this, x => x.IsInEditMode, uiScheduler)
                .AddTo(Anchors);

            this.WhenAnyValue(x => x.IsLocked)
                .Where(x => x && isInSelectMode)
                .Subscribe(() => IsInSelectMode = false)
                .AddTo(Anchors);

            aspectRatio = this.WhenAnyProperty(x => x.Bounds, x => x.ViewModelLocation)
                .Select(change => Width >= 0 && Height >= 0
                    ? Width / Height
                    : double.PositiveInfinity)
                .ToPropertyHelper(this, x => x.AspectRatio, uiScheduler)
                .AddTo(Anchors);
            sw.ResetStep();
            configEditorSupplier = new Lazy<OverlayConfigEditor>(() => CreateConfigEditor(this));
            sw.Step("Initialized Config editor");
        }
Esempio n. 14
0
        public MainWindowViewModel(
            [NotNull] IFactory <IOverlayAuraViewModel, OverlayAuraProperties> auraViewModelFactory,
            [NotNull] IApplicationUpdaterViewModel appUpdater,
            [NotNull] IClipboardManager clipboardManager,
            [NotNull] IConfigSerializer configSerializer,
            [NotNull] IGenericSettingsViewModel settingsViewModel,
            [NotNull] IMessageBoxViewModel messageBox,
            [NotNull] IHotkeyConverter hotkeyConverter,
            [NotNull] IFactory <HotkeyIsActiveTrigger> hotkeyTriggerFactory,
            [NotNull] IConfigProvider <EyeAurasConfig> configProvider,
            [NotNull] IConfigProvider rootConfigProvider,
            [NotNull] IPrismModuleStatusViewModel moduleStatus,
            [NotNull] IMainWindowBlocksProvider mainWindowBlocksProvider,
            [NotNull] IFactory <IRegionSelectorService> regionSelectorServiceFactory,
            [NotNull] ISharedContext sharedContext,
            [NotNull] IComparisonService comparisonService,
            [NotNull][Dependency(WellKnownSchedulers.UI)] IScheduler uiScheduler)
        {
            using var unused = new OperationTimer(elapsed => Log.Debug($"{nameof(MainWindowViewModel)} initialization took {elapsed.TotalMilliseconds:F0}ms"));

            TabsList     = new ReadOnlyObservableCollection <IEyeAuraViewModel>(sharedContext.AuraList);
            ModuleStatus = moduleStatus.AddTo(Anchors);
            var executingAssemblyName = Assembly.GetExecutingAssembly().GetName();

            Title = $"{(AppArguments.Instance.IsDebugMode ? "[D]" : "")} {executingAssemblyName.Name} v{executingAssemblyName.Version}";
            Disposable.Create(() => Log.Info("Disposing Main view model")).AddTo(Anchors);

            ApplicationUpdater = appUpdater.AddTo(Anchors);
            MessageBox         = messageBox.AddTo(Anchors);
            Settings           = settingsViewModel.AddTo(Anchors);
            StatusBarItems     = mainWindowBlocksProvider.StatusBarItems;

            this.auraViewModelFactory  = auraViewModelFactory;
            this.configProvider        = configProvider;
            this.sharedContext         = sharedContext;
            this.regionSelectorService = regionSelectorServiceFactory.Create();
            this.clipboardManager      = clipboardManager;
            this.configSerializer      = configSerializer;
            this.hotkeyConverter       = hotkeyConverter;
            this.hotkeyTriggerFactory  = hotkeyTriggerFactory;

            CreateNewTabCommand = CommandWrapper.Create(() => AddNewCommandExecuted(OverlayAuraProperties.Default));
            CloseTabCommand     = CommandWrapper
                                  .Create <IOverlayAuraViewModel>(CloseTabCommandExecuted, CloseTabCommandCanExecute)
                                  .RaiseCanExecuteChangedWhen(this.WhenAnyProperty(x => x.SelectedTab));

            DuplicateTabCommand = CommandWrapper
                                  .Create(DuplicateTabCommandExecuted, DuplicateTabCommandCanExecute)
                                  .RaiseCanExecuteChangedWhen(this.WhenAnyProperty(x => x.SelectedTab));
            CopyTabToClipboardCommand = CommandWrapper
                                        .Create(CopyTabToClipboardExecuted, CopyTabToClipboardCommandCanExecute)
                                        .RaiseCanExecuteChangedWhen(this.WhenAnyProperty(x => x.SelectedTab).Select(x => x));

            PasteTabCommand             = CommandWrapper.Create(PasteTabCommandExecuted);
            UndoCloseTabCommand         = CommandWrapper.Create(UndoCloseTabCommandExecuted, UndoCloseTabCommandCanExecute);
            OpenAppDataDirectoryCommand = CommandWrapper.Create(OpenAppDataDirectory);
            SelectRegionCommand         = CommandWrapper.Create(SelectRegionCommandExecuted);

            Observable
            .FromEventPattern <OrderChangedEventArgs>(h => positionMonitor.OrderChanged += h, h => positionMonitor.OrderChanged -= h)
            .Select(x => x.EventArgs)
            .Subscribe(OnTabOrderChanged, Log.HandleUiException)
            .AddTo(Anchors);

            sharedContext
            .AuraList
            .ToObservableChangeSet()
            .ObserveOn(uiScheduler)
            .OnItemAdded(x => SelectedTab = x)
            .Subscribe()
            .AddTo(Anchors);

            this.WhenAnyValue(x => x.SelectedTab)
            .Subscribe(x => Log.Debug($"Selected tab: {x}"))
            .AddTo(Anchors);

            LoadConfig();
            rootConfigProvider.Save();

            if (sharedContext.AuraList.Count == 0)
            {
                CreateNewTabCommand.Execute(null);
            }

            configUpdateSubject
            .Sample(ConfigSaveSamplingTimeout)
            .Subscribe(SaveConfig, Log.HandleException)
            .AddTo(Anchors);

            Observable.Merge(
                this.WhenAnyProperty(x => x.Left, x => x.Top, x => x.Width, x => x.Height)
                .Sample(ConfigSaveSamplingTimeout)
                .Select(x => $"[{x.Sender}] Main window property change: {x.EventArgs.PropertyName}"),
                sharedContext.AuraList.ToObservableChangeSet()
                .Sample(ConfigSaveSamplingTimeout)
                .Select(x => "Tabs list change"),
                sharedContext.AuraList.ToObservableChangeSet()
                .WhenPropertyChanged(x => x.Properties)
                .Sample(ConfigSaveSamplingTimeout)
                .WithPrevious((prev, curr) => new { prev, curr })
                .Select(x => new { x.curr.Sender, ComparisonResult = comparisonService.Compare(x.prev?.Value, x.curr.Value) })
                .Where(x => !x.ComparisonResult.AreEqual)
                .Select(x => $"[{x.Sender.TabName}] Tab properties change: {x.ComparisonResult.DifferencesString}"))
            .Buffer(ConfigSaveSamplingTimeout)
            .Where(x => x.Count > 0)
            .Subscribe(
                reasons =>
            {
                const int maxReasonsToOutput = 50;
                Log.Debug(
                    $"Config Save reasons{(reasons.Count <= maxReasonsToOutput ? string.Empty : $"first {maxReasonsToOutput} of {reasons.Count} items")}:\r\n\t{reasons.Take(maxReasonsToOutput).DumpToTable()}");
                configUpdateSubject.OnNext(Unit.Default);
            },
Esempio n. 15
0
        public WindowSelectorViewModel(
            [NotNull] IWindowListProvider windowListProvider,
            [NotNull][Dependency(WellKnownSchedulers.UI)] IScheduler uiScheduler)
        {
            WindowList = windowListProvider.WindowList;

            this.WhenAnyValue(x => x.WindowTitle)
            .WithPrevious((prev, curr) => new { prev, curr })
            .DistinctUntilChanged()
            .Where(x => !string.IsNullOrEmpty(x.prev) && x.curr == null)
            .Subscribe(x => WindowTitle = x.prev)
            .AddTo(Anchors);

            windowListProvider.WindowList.ToObservableChangeSet()
            .ToUnit()
            .Merge(this.WhenAnyValue(x => x.TargetWindow).ToUnit())
            .Throttle(ThrottlingPeriod)
            .ObserveOn(uiScheduler)
            .Subscribe(x => MatchingWindowList = BuildMatches(windowListProvider.WindowList))
            .AddTo(Anchors);

            this.WhenAnyValue(x => x.ActiveWindow)
            .Where(x => x != null)
            .Where(x => !IsMatch(x, TargetWindow))
            .Subscribe(
                x =>
            {
                var newTargetWindow = new WindowMatchParams
                {
                    Title  = x.Title,
                    Handle = x.Handle
                };
                Log.Debug($"Selected non-matching Overlay source, changing TargetWindow, {TargetWindow} => {newTargetWindow}");
                TargetWindow = newTargetWindow;
            })
            .AddTo(Anchors);

            this.WhenAnyValue(x => x.MatchingWindowList)
            .Where(items => !items.Contains(ActiveWindow))
            .Select(items => items.FirstOrDefault(x => x.Handle == ActiveWindow?.Handle || x.Handle == WindowHandle) ?? items.FirstOrDefault())
            .Where(x => !Equals(ActiveWindow, x))
            .Subscribe(
                x =>
            {
                Log.Debug(
                    $"Setting new Overlay Window(target: {TargetWindow}): {(ActiveWindow == null ? "null" : ActiveWindow.ToString())} => {(x == null ? "null" : x.ToString())}\n\t{MatchingWindowList.DumpToTable()}");
                ActiveWindow = x;
            })
            .AddTo(Anchors);

            enableOverlaySelector = this.WhenAnyProperty(x => x.MatchingWindowList)
                                    .Select(change => MatchingWindowList.Length > 1)
                                    .ToPropertyHelper(this, x => x.EnableOverlaySelector)
                                    .AddTo(Anchors);

            this.WhenAnyValue(x => x.TargetWindow)
            .Subscribe(
                x =>
            {
                WindowTitle        = x.Title;
                WindowTitleIsRegex = x.IsRegex;
                WindowHandle       = x.Handle;
            })
            .AddTo(Anchors);

            this.WhenAnyValue(x => x.WindowTitle, x => x.WindowTitleIsRegex, x => x.WindowHandle)
            .Select(
                x => new WindowMatchParams
            {
                Title   = WindowTitle,
                IsRegex = WindowTitleIsRegex,
                Handle  = WindowHandle
            })
            .DistinctUntilChanged()
            .Throttle(ThrottlingPeriod)
            .ObserveOn(uiScheduler)
            .Subscribe(x => TargetWindow = x)
            .AddTo(Anchors);

            SetWindowTitleCommand = CommandWrapper.Create <WindowHandle>(SetWindowTitleCommandExecuted);
        }
Esempio n. 16
0
        public MicSwitchOverlayViewModel(
            [NotNull] IOverlayWindowController overlayWindowController,
            [NotNull] IMicrophoneControllerEx microphoneController,
            [NotNull] IConfigProvider<MicSwitchConfig> configProvider,
            [NotNull] IImageProvider imageProvider,
            [NotNull] [Dependency(WellKnownSchedulers.UI)] IScheduler uiScheduler)
        {
            this.overlayWindowController = overlayWindowController;
            this.microphoneController = microphoneController;
            this.configProvider = configProvider;
            this.imageProvider = imageProvider;
            OverlayMode = OverlayMode.Transparent;
            MinSize = new Size(120, 120);
            MaxSize = new Size(300, 300);
            SizeToContent = SizeToContent.Manual;
            TargetAspectRatio = MinSize.Width / MinSize.Height;
            IsUnlockable = true;
            Title = "MicSwitch";
            WhenLoaded
                .Take(1)
                .Select(x => configProvider.WhenChanged)
                .Switch()
                .ObserveOn(uiScheduler)
                .Subscribe(ApplyConfig)
                .AddTo(Anchors);

            this.WhenAnyValue(x => x.IsLocked)
                .Subscribe(isLocked => OverlayMode = isLocked ? OverlayMode.Transparent : OverlayMode.Layered)
                .AddTo(Anchors);

            configProvider.ListenTo(x => x.MicrophoneLineId)
                .ObserveOn(uiScheduler)
                .Subscribe(lineId => { microphoneController.LineId = lineId; })
                .AddTo(Anchors);

            this.RaiseWhenSourceValue(x => x.IsEnabled, overlayWindowController, x => x.IsEnabled).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.Mute, microphoneController, x => x.Mute, uiScheduler).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.MicrophoneImage, imageProvider, x => x.ActiveMicrophoneImage, uiScheduler).AddTo(Anchors);
            
            ToggleLockStateCommand = CommandWrapper.Create(
                () =>
                {
                    if (IsLocked && UnlockWindowCommand.CanExecute(null))
                    {
                        UnlockWindowCommand.Execute(null);
                    }
                    else if (!IsLocked && LockWindowCommand.CanExecute(null))
                    {
                        LockWindowCommand.Execute(null);
                    }
                    else
                    {
                        throw new ApplicationException($"Something went wrong - invalid Overlay Lock state: {new {IsLocked, IsUnlockable, CanUnlock = UnlockWindowCommand.CanExecute(null), CanLock = LockWindowCommand.CanExecute(null)  }}");
                    }
                });
            
            Observable.Merge(
                    this.ObservableForProperty(x => x.Left, skipInitial: true).ToUnit(),
                    this.ObservableForProperty(x => x.Top, skipInitial: true).ToUnit(),
                    this.ObservableForProperty(x => x.Width, skipInitial: true).ToUnit(),
                    this.ObservableForProperty(x => x.Height, skipInitial: true).ToUnit(),
                    this.ObservableForProperty(x => x.IsEnabled, skipInitial: true).ToUnit(),
                    this.ObservableForProperty(x => x.IsLocked, skipInitial: true).ToUnit())
                .SkipUntil(WhenLoaded)
                .Throttle(ConfigThrottlingTimeout)
                .ObserveOn(uiScheduler)
                .Subscribe(SaveConfig, Log.HandleUiException)
                .AddTo(Anchors);

            this.WhenAnyValue(x => x.IsEnabled)
                .Where(x => !IsEnabled && !IsLocked)
                .ObserveOn(uiScheduler)
                .Subscribe(() => LockWindowCommand.Execute(null), Log.HandleUiException)
                .AddTo(Anchors);
        }