private void Awake()
 {
     if (!Instance)
     {
         Instance = this;
     }
 }
示例#2
0
    protected void OnDestroy()
    {
        internalDestroy();

        this.connection.RemoveListener(MicrophoneController.microphonePlaybackMessageID, this.listener);

        this.listener.Dispose();
        this.listener = null;

        Instance = null;
    }
示例#3
0
 private void Awake()
 {
     MicrophoneController.Instance = this;
     this.lockObject     = new object();
     this.MicBoostAmount = this.DefaultMicBoostAmount;
 }
示例#4
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);
        }
示例#5
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);
        }