diff --git a/Morphic.Client/App.xaml.cs b/Morphic.Client/App.xaml.cs index 236a0285..cd66bd39 100644 --- a/Morphic.Client/App.xaml.cs +++ b/Morphic.Client/App.xaml.cs @@ -22,8 +22,6 @@ // * Consumer Electronics Association Foundation using AutoUpdaterDotNET; -using CountlySDK; -using CountlySDK.Entities; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -48,7 +46,6 @@ namespace Morphic.Client using Bar; using Bar.Data; using Config; - using CountlySDK.CountlyCommon; using Dialogs; using Menu; using Microsoft.Win32; @@ -171,7 +168,6 @@ public class EnabledFeature public string? scope { get; set; } } // - public EnabledFeature? atOnDemand { get; set; } public EnabledFeature? autorunAfterLogin { get; set; } public EnabledFeature? checkForUpdates { get; set; } public EnabledFeature? cloudSettingsTransfer { get; set; } @@ -191,7 +187,6 @@ public class MorphicBarConfigSection // private struct CommonConfigurationContents { - public bool AtOnDemandIsEnabled; public ConfigurableFeatures.AutorunConfigOption? AutorunConfig; public bool CheckForUpdatesIsEnabled; public bool CloudSettingsTransferIsEnabled; @@ -205,9 +200,6 @@ private async Task GetCommonConfigurationAsync() // set up default configuration var result = new CommonConfigurationContents(); // - // at on demand - result.AtOnDemandIsEnabled = true; - // // autorun result.AutorunConfig = null; // @@ -301,20 +293,6 @@ private async Task GetCommonConfigurationAsync() } } - // capture the at on demand "is enabled" setting - if (deserializedJson.features?.atOnDemand?.enabled is not null) - { - result.AtOnDemandIsEnabled = deserializedJson.features.atOnDemand.enabled.Value; - } - else - { - // NOTE: for version 0 of the config.json file, we set AtOnDemandIsEnabled to FALSE by default - if (deserializedJson.version == 0) - { - result.AtOnDemandIsEnabled = false; - } - } - // capture the check for updates "is enabled" setting if (deserializedJson.features?.checkForUpdates?.enabled is not null) { @@ -574,21 +552,15 @@ private void ConfigureServices(IServiceCollection services) services.AddSingleton(s => Solutions.FromFile(s, AppPaths.GetAppFile("solutions.json5"))); } - internal async Task Countly_RecordEventAsync(string Key) { - if (ConfigurableFeatures.TelemetryIsEnabled == true) - { - await Countly.RecordEvent(Key); - - _telemetryClient?.EnqueueEvent(Key, null); - } + internal void SuppressTaskbarButtonResurfaceChecks(bool suppress) + { + // OBSERVATION: in the current implementation, the taskbar ("tray") button is owned by the menu control + this.morphicMenu?.SuppressTaskbarButtonResurfaceChecks(suppress); } - internal async Task Countly_RecordEventAsync(string Key, int Count, Segmentation Segmentation) - { + internal async Task Telemetry_RecordEventAsync(string Key) { if (ConfigurableFeatures.TelemetryIsEnabled == true) { - await Countly.RecordEvent(Key, Count, Segmentation); - _telemetryClient?.EnqueueEvent(Key, null); } } @@ -745,27 +717,6 @@ private TelemetryIdComponents GetOrCreateTelemetryIdComponents() return new TelemetryIdComponents() { CompositeId = telemetryCompositeId, SiteId = telemetrySiteId, DeviceUuid = telemetryDeviceUuid }; } - private async Task ConfigureCountlyAsync() - { - // TODO: Move metrics related things to own class. - - // retrieve the telemetry composite ID for this device; if it doesn't exist then create a new one - var telemetryDeviceCompositeId = this.GetOrCreateTelemetryIdComponents().CompositeId; - - IConfigurationSection? section = this.Configuration.GetSection("Countly"); - CountlyConfig cc = new CountlyConfig - { - serverUrl = section["ServerUrl"], - appKey = section["AppKey"], - appVersion = BuildInfo.Current.InformationalVersion, - developerProvidedDeviceId = telemetryDeviceCompositeId - }; - - await Countly.Instance.Init(cc); - await Countly.Instance.SessionBegin(); - CountlyBase.IsLoggingEnabled = true; - } - private string PrependSiteIdToTelemetryCompositeId(string value, string telemetrySiteId) { var telemetryDeviceUuid = value; @@ -947,16 +898,6 @@ internal record SessionTelemetryEventData #endregion Telemetry - private void RecordedException(Task task) - { - if (task.Exception is Exception e) - { - this.Logger.LogError("exception thrown while countly recording exception: {msg}", e.Message); - throw e; - } - this.Logger.LogDebug("successfully recorded countly exception"); - } - void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) { // TODO: Improve error logging/reporting. @@ -967,10 +908,6 @@ void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptio { this.Logger.LogError("handled uncaught exception: {msg}", ex.Message); this.Logger.LogError(ex.StackTrace); - - Dictionary extraData = new Dictionary(); - CountlyBase.RecordException(ex.Message, ex.StackTrace, extraData, true) - .ContinueWith(this.RecordedException, TaskScheduler.FromCurrentSynchronizationContext()); } catch (Exception) { @@ -1088,7 +1025,6 @@ protected override async void OnStartup(StartupEventArgs e) // NOTE: we currently load this AFTER setting up the logger because the GetCommonConfigurationAsync function logs config file errors to the logger var commonConfiguration = await this.GetCommonConfigurationAsync(); ConfigurableFeatures.SetFeatures( - atOnDemandIsEnabled: commonConfiguration.AtOnDemandIsEnabled, autorunConfig: commonConfiguration.AutorunConfig, checkForUpdatesIsEnabled: commonConfiguration.CheckForUpdatesIsEnabled, cloudSettingsTransferIsEnabled: commonConfiguration.CloudSettingsTransferIsEnabled, @@ -1110,7 +1046,6 @@ protected override async void OnStartup(StartupEventArgs e) if (ConfigurableFeatures.TelemetryIsEnabled == true) { - await this.ConfigureCountlyAsync(); await this.ConfigureTelemetryAsync(); } @@ -1605,12 +1540,6 @@ protected override async void OnExit(ExitEventArgs e) } } catch { } - // - try - { - await Countly.Instance.SessionEnd(); - } - catch { } } if (ConfigurableFeatures.ResetSettingsIsEnabled == true) diff --git a/Morphic.Client/Bar/Data/Actions/ApplicationAction.cs b/Morphic.Client/Bar/Data/Actions/ApplicationAction.cs index f70dad12..bfa91111 100644 --- a/Morphic.Client/Bar/Data/Actions/ApplicationAction.cs +++ b/Morphic.Client/Bar/Data/Actions/ApplicationAction.cs @@ -700,7 +700,7 @@ protected override Task> InvokeAsyncImpl // NOTE: ideally we would change this whole function into an async function (instead of using the Task.FromResult pattern) Task.Run(async () => { - await App.Current.Countly_RecordEventAsync(this.TelemetryEventName!); + await App.Current.Telemetry_RecordEventAsync(this.TelemetryEventName!); }); } } diff --git a/Morphic.Client/Bar/Data/Actions/BarAction.cs b/Morphic.Client/Bar/Data/Actions/BarAction.cs index 5a3b2392..059f5c86 100644 --- a/Morphic.Client/Bar/Data/Actions/BarAction.cs +++ b/Morphic.Client/Bar/Data/Actions/BarAction.cs @@ -10,7 +10,6 @@ namespace Morphic.Client.Bar.Data.Actions { - using CountlySDK; using Microsoft.Extensions.Logging; using Morphic.Core; using Newtonsoft.Json; @@ -92,17 +91,17 @@ private async Task SendTelemetryForBarAction(string? source = null, bool? toggle switch (this.Id) { case "log-off": - await App.Current.Countly_RecordEventAsync("signOut"); + await App.Current.Telemetry_RecordEventAsync("signOut"); break; case "volume": { if (source == "up") { - await App.Current.Countly_RecordEventAsync("volumeUp"); + await App.Current.Telemetry_RecordEventAsync("volumeUp"); } else if (source == "down") { - await App.Current.Countly_RecordEventAsync("volumeDown"); + await App.Current.Telemetry_RecordEventAsync("volumeDown"); } } break; @@ -110,11 +109,11 @@ private async Task SendTelemetryForBarAction(string? source = null, bool? toggle { if (source == "on") { - await App.Current.Countly_RecordEventAsync("magnifierShow"); + await App.Current.Telemetry_RecordEventAsync("magnifierShow"); } else if (source == "off") { - await App.Current.Countly_RecordEventAsync("magnifierHide"); + await App.Current.Telemetry_RecordEventAsync("magnifierHide"); } } break; @@ -122,11 +121,11 @@ private async Task SendTelemetryForBarAction(string? source = null, bool? toggle { if (source == "play") { - await App.Current.Countly_RecordEventAsync("readSelectedPlay"); + await App.Current.Telemetry_RecordEventAsync("readSelectedPlay"); } else if (source == "stop") { - await App.Current.Countly_RecordEventAsync("readSelectedStop"); + await App.Current.Telemetry_RecordEventAsync("readSelectedStop"); } } break; @@ -134,10 +133,10 @@ private async Task SendTelemetryForBarAction(string? source = null, bool? toggle switch (source) { case "on": - await App.Current.Countly_RecordEventAsync("colorFiltersOn"); + await App.Current.Telemetry_RecordEventAsync("colorFiltersOn"); break; case "off": - await App.Current.Countly_RecordEventAsync("colorFiltersOff"); + await App.Current.Telemetry_RecordEventAsync("colorFiltersOff"); break; } break; @@ -145,10 +144,10 @@ private async Task SendTelemetryForBarAction(string? source = null, bool? toggle switch (source) { case "on": - await App.Current.Countly_RecordEventAsync("darkModeOn"); + await App.Current.Telemetry_RecordEventAsync("darkModeOn"); break; case "off": - await App.Current.Countly_RecordEventAsync("darkModeOff"); + await App.Current.Telemetry_RecordEventAsync("darkModeOff"); break; } break; @@ -156,10 +155,10 @@ private async Task SendTelemetryForBarAction(string? source = null, bool? toggle switch (source) { case "100": - await App.Current.Countly_RecordEventAsync("highContrastOn"); + await App.Current.Telemetry_RecordEventAsync("highContrastOn"); break; case "1": - await App.Current.Countly_RecordEventAsync("highContrastOff"); + await App.Current.Telemetry_RecordEventAsync("highContrastOff"); break; } break; @@ -167,10 +166,10 @@ private async Task SendTelemetryForBarAction(string? source = null, bool? toggle switch (source) { case "on": - await App.Current.Countly_RecordEventAsync("nightModeOn"); + await App.Current.Telemetry_RecordEventAsync("nightModeOn"); break; case "off": - await App.Current.Countly_RecordEventAsync("nightModeOff"); + await App.Current.Telemetry_RecordEventAsync("nightModeOff"); break; } break; @@ -181,11 +180,11 @@ private async Task SendTelemetryForBarAction(string? source = null, bool? toggle { if (toggleState == true) { - await App.Current.Countly_RecordEventAsync("colorFiltersOn"); + await App.Current.Telemetry_RecordEventAsync("colorFiltersOn"); } else { - await App.Current.Countly_RecordEventAsync("colorFiltersOff"); + await App.Current.Telemetry_RecordEventAsync("colorFiltersOff"); } } break; @@ -193,11 +192,11 @@ private async Task SendTelemetryForBarAction(string? source = null, bool? toggle { if (toggleState == true) { - await App.Current.Countly_RecordEventAsync("highContrastOn"); + await App.Current.Telemetry_RecordEventAsync("highContrastOn"); } else { - await App.Current.Countly_RecordEventAsync("highContrastOff"); + await App.Current.Telemetry_RecordEventAsync("highContrastOff"); } } break; @@ -205,48 +204,48 @@ private async Task SendTelemetryForBarAction(string? source = null, bool? toggle { if (toggleState == true) { - await App.Current.Countly_RecordEventAsync("nightModeOn"); + await App.Current.Telemetry_RecordEventAsync("nightModeOn"); } else { - await App.Current.Countly_RecordEventAsync("nightModeOff"); + await App.Current.Telemetry_RecordEventAsync("nightModeOff"); } } break; case "copy": // NOTE: this is the basic bar variant of "copy" (below) { - await App.Current.Countly_RecordEventAsync("screenSnip"); + await App.Current.Telemetry_RecordEventAsync("screenSnip"); } break; case "dark-mode": { if (toggleState == true) { - await App.Current.Countly_RecordEventAsync("darkModeOn"); + await App.Current.Telemetry_RecordEventAsync("darkModeOn"); } else { - await App.Current.Countly_RecordEventAsync("darkModeOff"); + await App.Current.Telemetry_RecordEventAsync("darkModeOff"); } } break; case "openallusb": - await App.Current.Countly_RecordEventAsync("openUsbDrives"); + await App.Current.Telemetry_RecordEventAsync("openUsbDrives"); break; case "ejectallusb": - await App.Current.Countly_RecordEventAsync("ejectUsbDrives"); + await App.Current.Telemetry_RecordEventAsync("ejectUsbDrives"); break; case "volumeDown": // NOTE: this is captured here because telemetry for volumeDown on the +3 bar is not captured by SendTelemetryForBarAction - await App.Current.Countly_RecordEventAsync("volumeDown"); + await App.Current.Telemetry_RecordEventAsync("volumeDown"); break; case "volumeMute": - await App.Current.Countly_RecordEventAsync("volumeMute"); + await App.Current.Telemetry_RecordEventAsync("volumeMute"); break; case "volumeUp": // NOTE: this is captured here because telemetry for volumeUp on the +3 bar is not captured by SendTelemetryForBarAction - await App.Current.Countly_RecordEventAsync("volumeUp"); + await App.Current.Telemetry_RecordEventAsync("volumeUp"); break; case null: // no tags; this is the Morphie button or another custom element with no known tags @@ -258,12 +257,20 @@ private async Task SendTelemetryForBarAction(string? source = null, bool? toggle } break; case "screen-zoom": - // this action type's telemetry is logged elsewhere + switch (source) + { + case "inc": + await App.Current.Telemetry_RecordEventAsync("textSizeIncrease"); + break; + case "dec": + await App.Current.Telemetry_RecordEventAsync("textSizeDecrease"); + break; + } break; case "snip": // NOTE: this is the custom bar variant of "copy" (above) { - await App.Current.Countly_RecordEventAsync("screenSnip"); + await App.Current.Telemetry_RecordEventAsync("screenSnip"); } break; default: @@ -338,7 +345,7 @@ protected async override Task> InvokeAsy { if (this.TelemetryEventName is not null) { - await App.Current.Countly_RecordEventAsync(this.TelemetryEventName!); + await App.Current.Telemetry_RecordEventAsync(this.TelemetryEventName!); } } } diff --git a/Morphic.Client/Bar/Data/Actions/WebAction.cs b/Morphic.Client/Bar/Data/Actions/WebAction.cs index 56b9d0ed..d9bd8510 100644 --- a/Morphic.Client/Bar/Data/Actions/WebAction.cs +++ b/Morphic.Client/Bar/Data/Actions/WebAction.cs @@ -100,7 +100,7 @@ protected override Task> InvokeAsyncImpl // NOTE: ideally we would change this whole function into an async function (instead of using the Task.FromResult pattern) Task.Run(async () => { - await App.Current.Countly_RecordEventAsync(this.TelemetryEventName!); + await App.Current.Telemetry_RecordEventAsync(this.TelemetryEventName!); }); } } diff --git a/Morphic.Client/Bar/UI/BarWindow.xaml.cs b/Morphic.Client/Bar/UI/BarWindow.xaml.cs index 9b13998a..caad2ec8 100644 --- a/Morphic.Client/Bar/UI/BarWindow.xaml.cs +++ b/Morphic.Client/Bar/UI/BarWindow.xaml.cs @@ -12,7 +12,6 @@ namespace Morphic.Client.Bar.UI { using AppBarWindow; using BarControls; - using CountlySDK; using Data; using Microsoft.Win32; using Mouse = Morphic.WindowsNative.Input.Mouse; @@ -575,9 +574,7 @@ private async void CloseButton_Click(object sender, RoutedEventArgs e) } finally { - var segmentation = new Segmentation(); - segmentation.Add("eventSource", "closeButton"); - await App.Current.Countly_RecordEventAsync("morphicBarHide", 1, segmentation); + await App.Current.Telemetry_RecordEventAsync("morphicBarHide"); } } } diff --git a/Morphic.Client/Config/ConfigurableFeatures.cs b/Morphic.Client/Config/ConfigurableFeatures.cs index 130f5861..03b3179f 100644 --- a/Morphic.Client/Config/ConfigurableFeatures.cs +++ b/Morphic.Client/Config/ConfigurableFeatures.cs @@ -20,8 +20,6 @@ public enum MorphicBarVisibilityAfterLoginOption Hide // always hide the MorphicBar after login } - public static bool AtOnDemandIsEnabled = true; - // public static AutorunConfigOption? AutorunConfig = null; // public static bool CheckForUpdatesIsEnabled = false; @@ -39,7 +37,6 @@ public enum MorphicBarVisibilityAfterLoginOption public static string? TelemetrySiteId = null; public static void SetFeatures( - bool atOnDemandIsEnabled, AutorunConfigOption? autorunConfig, bool checkForUpdatesIsEnabled, bool cloudSettingsTransferIsEnabled, @@ -50,7 +47,6 @@ public static void SetFeatures( string? telemetrySiteId ) { - ConfigurableFeatures.AtOnDemandIsEnabled = atOnDemandIsEnabled; ConfigurableFeatures.AutorunConfig = autorunConfig; ConfigurableFeatures.CheckForUpdatesIsEnabled = checkForUpdatesIsEnabled; ConfigurableFeatures.CloudSettingsTransferIsEnabled = cloudSettingsTransferIsEnabled; diff --git a/Morphic.Client/Dialogs/AtOnDemand/AtOnDemandHelpers.cs b/Morphic.Client/Dialogs/AtOnDemand/AtOnDemandHelpers.cs index f2f4d6de..ec30432d 100644 --- a/Morphic.Client/Dialogs/AtOnDemand/AtOnDemandHelpers.cs +++ b/Morphic.Client/Dialogs/AtOnDemand/AtOnDemandHelpers.cs @@ -21,6 +21,8 @@ // * Adobe Foundation // * Consumer Electronics Association Foundation +#define OFFLINE_INSTALL + namespace Morphic.Client.Dialogs.AtOnDemand; using Morphic.Core; @@ -41,12 +43,87 @@ namespace Morphic.Client.Dialogs.AtOnDemand; internal class AtOnDemandHelpers { + // knownfolders.h + internal struct KNOWNFOLDERID + { + public static Guid FOLDERID_Downloads = new Guid(0x374de290, 0x123f, 0x4565, 0x91, 0x64, 0x39, 0xc4, 0x92, 0x5e, 0x46, 0x7b); // {374DE290-123F-4565-9164-39C4925E467B} + } + + // NOTE: when using this function, ppszPath must be freed manually using FreeCoTaskMem + [DllImport("shell32.dll", CharSet=CharSet.Unicode)] + private static extern int SHGetKnownFolderPath( + [MarshalAs(UnmanagedType.LPStruct)] Guid rfid, + uint dwFlags, + IntPtr hToken, + out IntPtr ppszPath); + + private const int S_OK = 0; + + internal static MorphicResult GetKnownFolderPath(Guid knownFolderId) + { + IntPtr pointerToPath; + var getKnownFolderPathResult = AtOnDemandHelpers.SHGetKnownFolderPath(knownFolderId, 0, IntPtr.Zero, out pointerToPath); + try + { + if (getKnownFolderPathResult == S_OK) + { + var path = Marshal.PtrToStringUni(pointerToPath); + return MorphicResult.OkResult(path!); + } + else + { + return MorphicResult.ErrorResult(); + } + } + finally + { + Marshal.FreeCoTaskMem(pointerToPath); + } + } + // TODO: make sure the user is logged in before calling this function (and make sure we get the user's login info/preferences so we can search for AT which is not installed) internal static List GetListOfAtSoftwareToInstall() { +#if OFFLINE_INSTALL + var getKnownFolderPathResult = AtOnDemandHelpers.GetKnownFolderPath(KNOWNFOLDERID.FOLDERID_Downloads); + string pathToDownloadsFolder; + if (getKnownFolderPathResult.IsError == true) + { + Debug.Assert(false, "Could not get path to downloads folder"); + return new(); + } + pathToDownloadsFolder = getKnownFolderPathResult.Value!; +#endif + var listOfAllAtSoftware = new List(); + // NOTE: in the future, we should move the list of available AT to a data file (or to a trusted server) // TODO: check the user's preferences (and the set of available AT), to determine which AT they want to use (instead of adding the full list here) + +#if OFFLINE_INSTALL + // + // fusion + var fusionSoftware = new AtSoftwareDetails() + { + ShortName = "fusion", + ProductName = "Fusion", + ManufacturerName = "Freedom Scientific Inc.", + DownloadUri = new Uri(System.IO.Path.Combine(pathToDownloadsFolder, "atod\\fusion")), // local URI + InstallMethod = AtSoftwareInstallMethod.MultipleOfflineInstallers + }; + listOfAllAtSoftware.Add(fusionSoftware); + // + // JAWS + var jawsSoftware = new AtSoftwareDetails() + { + ShortName = "jaws", + ProductName = "JAWS", + ManufacturerName = "Freedom Scientific Inc.", + DownloadUri = new Uri(System.IO.Path.Combine(pathToDownloadsFolder, "atod\\jaws")), // local URI + InstallMethod = AtSoftwareInstallMethod.MultipleOfflineInstallers + }; + listOfAllAtSoftware.Add(jawsSoftware); +#endif // // read&write var readAndWriteSoftware = new AtSoftwareDetails() @@ -54,11 +131,28 @@ internal static List GetListOfAtSoftwareToInstall() ShortName = "readandwrite", ProductName = "Read&Write", ManufacturerName = "Texthelp Ltd", +#if OFFLINE_INSTALL + DownloadUri = new Uri(System.IO.Path.Combine(pathToDownloadsFolder, "atod\\readandwrite\\setup.zip")), // local URI +#else DownloadUri = new Uri("https://fastdownloads2.texthelp.com/readwrite12/installers/us/setup.zip"), // US download URI //DownloadUri = new Uri("https://fastdownloads2.texthelp.com/readwrite12/installers/uk/setup.zip"), // UK download URI +#endif InstallMethod = AtSoftwareInstallMethod.ZipFileWithEmbeddedMsi("setup.msi") }; listOfAllAtSoftware.Add(readAndWriteSoftware); +#if OFFLINE_INSTALL + // + // ZoomText + var zoomtextSoftware = new AtSoftwareDetails() + { + ShortName = "zoomtext", + ProductName = "ZoomText", + ManufacturerName = "Freedom Scientific Inc.", + DownloadUri = new Uri(System.IO.Path.Combine(pathToDownloadsFolder, "atod\\zoomtext")), // local URI + InstallMethod = AtSoftwareInstallMethod.MultipleOfflineInstallers + }; + listOfAllAtSoftware.Add(zoomtextSoftware); +#endif var result = new System.Collections.Generic.List(); @@ -89,9 +183,20 @@ private static MorphicResult IsAtSoftwareInstalled(string sho switch (shortName) { + case "fusion": + installerGuid = new Guid(0x7FD10D99, 0x9C47, 0x448F, 0x90, 0x08, 0x9E, 0x1D, 0x2C, 0xC4, 0xE5, 0xEA); // {7FD10D99-9C47-448F-9008-9E1D2CC4E5EA} + break; + case "jaws": + // NOTE: this is not listed in the Program Files when JAWS is installed with Fusion; we should double-check against visible entries when JAWS is installed by itself + installerGuid = new Guid(0xCCEA40E1, 0x5E4E, 0x4707, 0xB2, 0xF4, 0xC1, 0x2A, 0x74, 0xA9, 0x09, 0x56); // {CCEA40E1-5E4E-4707-B2F4-C12A74A90956} // JAWS 2023 Base (from the Fusion Suite, is assumed to be JAWS itself) + break; case "readandwrite": installerGuid = new Guid(0x355AB00F, 0x48E8, 0x474E, 0xAC, 0xC4, 0xD9, 0x17, 0xBA, 0xFA, 0x4D, 0x58); // {355AB00F-48E8-474E-ACC4-D917BAFA4D58} break; + case "zoomtext": + // NOTE: this is not listed in the Program Files when ZoomText is installed with Fusion; we should double-check against visible entries when ZoomText is installed by itself + installerGuid = new Guid(0x06A0FE6D, 0xFB33, 0x45A0, 0xB7, 0x43, 0xFE, 0xFE, 0x21, 0xE8, 0xCD, 0x99); // {06A0FE6D-FB33-45A0-B743-FEFE21E8CD99} + break; default: return MorphicResult.ErrorResult(); } diff --git a/Morphic.Client/Dialogs/AtOnDemand/AtSoftwareInstallMethod.cs b/Morphic.Client/Dialogs/AtOnDemand/AtSoftwareInstallMethod.cs index 35e54e45..83949238 100644 --- a/Morphic.Client/Dialogs/AtOnDemand/AtSoftwareInstallMethod.cs +++ b/Morphic.Client/Dialogs/AtOnDemand/AtSoftwareInstallMethod.cs @@ -35,10 +35,12 @@ public record AtSoftwareInstallMethod : MorphicAssociatedValueEnum new(Values.MultipleOfflineInstallers); public static AtSoftwareInstallMethod ZipFileWithEmbeddedMsi(string pathToMsi) => new(Values.ZipFileWithEmbeddedMsi) { PathToMsi = pathToMsi }; // associated values diff --git a/Morphic.Client/Dialogs/AtOnDemand/DownloadAndInstallAppsPanel.xaml.cs b/Morphic.Client/Dialogs/AtOnDemand/DownloadAndInstallAppsPanel.xaml.cs index 865ce6b7..363d7ca8 100644 --- a/Morphic.Client/Dialogs/AtOnDemand/DownloadAndInstallAppsPanel.xaml.cs +++ b/Morphic.Client/Dialogs/AtOnDemand/DownloadAndInstallAppsPanel.xaml.cs @@ -78,6 +78,20 @@ private async Task DoInstallAsync() switch (appToInstall.InstallMethod.Value) { + case AtSoftwareInstallMethod.Values.MultipleOfflineInstallers: + { + var installResult = await this.InstallMultipleOfflineInstallersAsync(appToInstall); + if (installResult.IsSuccess == true) + { + installWasSuccess = true; + + if (installResult.Value!.RebootRequired == true) + { + rebootRequired = true; + } + } + } + break; case AtSoftwareInstallMethod.Values.ZipFileWithEmbeddedMsi: { var installResult = await this.InstallZipFileWithEmbeddedMsiAsync(appToInstall); @@ -173,6 +187,1108 @@ await Application.Current.Dispatcher.InvokeAsync(() => return result; } + private async Task> InstallCustom_FusionAsync(AtSoftwareDetails atSoftwareDetails) + { + bool rebootRequired = false; + + int currentStep = 0; + const int TOTAL_STEP_COUNT = 61; + + // Sentinel System Driver Installer 7.6.1.exe + var installExeResult = await this.InstallExeAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "prerequisites\\Sentinel System Driver Installer 7.6.1.exe"), "/S /v/qn"); + if (installExeResult.IsError == true) + { + return MorphicResult.ErrorResult(); + } + this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT).Invoke(1.0); + currentStep += 1; + + // vcredist2022_x64.exe + installExeResult = await this.InstallExeAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "prerequisites\\vcredist2022_x64.exe"), "/install /quiet /norestart"); + if (installExeResult.IsError == true) + { + return MorphicResult.ErrorResult(); + } + this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT).Invoke(1.0); + currentStep += 1; + + // vcredist2022_x86.exe + installExeResult = await this.InstallExeAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "prerequisites\\vcredist2022_x86.exe"), "/install /quiet /norestart"); + if (installExeResult.IsError == true) + { + return MorphicResult.ErrorResult(); + } + this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT).Invoke(1.0); + currentStep += 1; + + // windowsdesktop-runtime-6.0.11-win-x64.exe + installExeResult = await this.InstallExeAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "prerequisites\\windowsdesktop-runtime-6.0.11-win-x64.exe"), "/install /quiet /norestart"); + if (installExeResult.IsError == true) + { + return MorphicResult.ErrorResult(); + } + this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT).Invoke(1.0); + currentStep += 1; + + // windowsdesktop-runtime-6.0.11-win-x86.exe + installExeResult = await this.InstallExeAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "prerequisites\\windowsdesktop-runtime-6.0.11-win-x86.exe"), "/install /quiet /norestart"); + if (installExeResult.IsError == true) + { + return MorphicResult.ErrorResult(); + } + this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT).Invoke(1.0); + currentStep += 1; + + // create registry key + //// NOTE: we should create a "CreateSubKey" function in Morphic.WindowsNative.Registry.RegistryKey + //var openSubKeyResult = Morphic.WindowsNative.Registry.CurrentUser.OpenSubKey("SOFTWARE", true); + //if (openSubKeyResult.IsError == true) + //{ + // return MorphicResult.ErrorResult(); + //} + //var hkcuSoftwareKey = openSubKeyResult.Value!; + //// + //var createSubKeyResult = hkcuSoftwareKey.CreateSubKey("Freedom Scientific"); + //if (createSubKeyResult.IsError == true) + //{ + // return MorphicResult.ErrorResult(); + //} + // + try + { + var hkcuSubKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("SOFTWARE", true); + if (hkcuSubKey is null) + { + return MorphicResult.ErrorResult(); + } + else + { + bool couldOpenExistingKey = false; + try + { + var freedomScientificSubKey = hkcuSubKey.OpenSubKey("Freedom Scientific", true); + couldOpenExistingKey = true; + } + catch + { + } + if (couldOpenExistingKey == false) + { + hkcuSubKey.CreateSubKey("Freedom Scientific", true); + } + } + } + catch + { + return MorphicResult.ErrorResult(); + } + + Dictionary msiCommandLineArgs = new(); + msiCommandLineArgs.Add("ARPSYSTEMCOMPONENT", "1"); + + // Eloquence.msi + var progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + var installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "Eloquence.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // VideoAccessibility.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x64\\VideoAccessibility.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // Utilities.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x64\\Utilities.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // fsElevation.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x64\\fsElevation.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // fsSynth.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x64\\FsSynth.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // UIAHooks.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x64\\UIAHooks.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FSReader.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x64\\FSReader.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // TableOfContents.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x64\\TableOfContents.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FSOmnipage.msi (unknown architecture) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "FSOmnipage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FSOcr.msi (x86) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x86\\FSOcr.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FSOcr.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x64\\FSOcr.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FSOcrTombstone (x86) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x86\\FSOcrTombstone.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FSOcrTombstone.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x64\\FSOcrTombstone.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FSSupportTool.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\FSSupportTool.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ErrorReporting.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x64\\ErrorReporting.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // TouchServer.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x64\\TouchServer.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FusionInterface.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x64\\FusionInterface.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // Authorization.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x64\\Authorization.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // RdpSupport.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\RdpSupport.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FSCam.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x64\\FSCam.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // HookManager.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x64\\HookManager.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // AccEventCache.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\AccEventCache.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // GlobalHooksDispatcher.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\GlobalHooksDispatcher.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // Telemetry.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\Telemetry.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // VoiceAssistant (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\VoiceAssistant.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // JAWSBase.msi (x64) + msiCommandLineArgs.Add("SETUP_LANGUAGES", "enu"); + msiCommandLineArgs.Add("TANDEM", "1"); + // + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x64\\JAWSBase.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + // + msiCommandLineArgs.Remove("SETUP_LANGUAGES"); + msiCommandLineArgs.Remove("TANDEM"); + + // JAWSLanguage.msi (x64) + msiCommandLineArgs.Add("PRIMARY_LANGUAGE", "1"); + msiCommandLineArgs.Add("TANDEM", "1"); + // + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\JAWSLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + // + msiCommandLineArgs.Remove("PRIMARY_LANGUAGE"); + msiCommandLineArgs.Remove("TANDEM"); + + // JAWSStart.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\JAWSStart.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomTextLanguage.msi (arb) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "arb\\x64\\ZoomTextLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomTextLanguage.msi (csy) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "csy\\x64\\ZoomTextLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomTextLanguage.msi (dan) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "dan\\x64\\ZoomTextLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomTextLanguage.msi (deu) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "deu\\x64\\ZoomTextLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomTextLanguage.msi (eng) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "eng\\x64\\ZoomTextLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomTextLanguage.msi (esn) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "esn\\x64\\ZoomTextLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomTextLanguage.msi (fin) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "fin\\x64\\ZoomTextLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomTextLanguage.msi (fra) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "fra\\x64\\ZoomTextLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomTextLanguage.msi (heb) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "heb\\x64\\ZoomTextLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomTextLanguage.msi (hun) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "hun\\x64\\ZoomTextLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomTextLanguage.msi (isl) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "isl\\x64\\ZoomTextLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomTextLanguage.msi (ita) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "ita\\x64\\ZoomTextLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomTextLanguage.msi (jpn) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "jpn\\x64\\ZoomTextLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomTextLanguage.msi (kor) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "kor\\x64\\ZoomTextLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomTextLanguage.msi (nld) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "nld\\x64\\ZoomTextLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomTextLanguage.msi (nor) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "nor\\x64\\ZoomTextLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomTextLanguage.msi (plk) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "plk\\x64\\ZoomTextLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomTextLanguage.msi (ptb) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "ptb\\x64\\ZoomTextLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomTextLanguage.msi (rus) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "rus\\x64\\ZoomTextLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomTextLanguage.msi (sky) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "sky\\x64\\ZoomTextLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomTextLanguage.msi (sve) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "sve\\x64\\ZoomTextLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomTextLanguage.msi (trk) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "trk\\x64\\ZoomTextLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // KeyboardManager.msi (x86) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x86\\KeyboardManager.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // VocalizerExpressive-2.2.206-enu-Tom-Compact-enu.msi (unknown architecture) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "VocalizerExpressive-2.2.206-enu-Tom-Compact-enu.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // VocalizerExpressive-2.2.206-enu-Zoe-Compact-enu.msi (unknown architecture) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "VocalizerExpressive-2.2.206-enu-Zoe-Compact-enu.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomText.msi (enu) (x64) + msiCommandLineArgs.Add("PRODUCT_TYPE", "2"); + msiCommandLineArgs.Add("PRIMARY_LANGUAGE", "1"); + // + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\ZoomText.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + // + msiCommandLineArgs.Remove("PRODUCT_TYPE"); + msiCommandLineArgs.Remove("PRIMARY_LANGUAGE"); + + // Fusion.msi (enu) (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\Fusion.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FusionBundle.exe + installExeResult = await this.InstallExeAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "FusionBundle.exe"), "/Type silent"); + if (installExeResult.IsError == true) + { + return MorphicResult.ErrorResult(); + } + this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT).Invoke(1.0); + currentStep += 1; + + var result = new InstallMsiResult() { RebootRequired = rebootRequired }; + return MorphicResult.OkResult(result); + } + + private async Task> InstallCustom_JawsAsync(AtSoftwareDetails atSoftwareDetails) + { + bool rebootRequired = false; + + int currentStep = 0; + const int TOTAL_STEP_COUNT = 33; + + // Sentinel System Driver Installer 7.6.1.exe + var installExeResult = await this.InstallExeAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "prerequisites\\Sentinel System Driver Installer 7.6.1.exe"), "/S /v/qn"); + if (installExeResult.IsError == true) + { + return MorphicResult.ErrorResult(); + } + this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT).Invoke(1.0); + currentStep += 1; + + // vcredist2022_x64.exe + installExeResult = await this.InstallExeAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "prerequisites\\vcredist2022_x64.exe"), "/install /quiet /norestart"); + if (installExeResult.IsError == true) + { + return MorphicResult.ErrorResult(); + } + this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT).Invoke(1.0); + currentStep += 1; + + // windowsdesktop-runtime-6.0.11-win-x64.exe + installExeResult = await this.InstallExeAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "prerequisites\\windowsdesktop-runtime-6.0.11-win-x64.exe"), "/install /quiet /norestart"); + if (installExeResult.IsError == true) + { + return MorphicResult.ErrorResult(); + } + this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT).Invoke(1.0); + currentStep += 1; + + // create registry key + //// NOTE: we should create a "CreateSubKey" function in Morphic.WindowsNative.Registry.RegistryKey + //var openSubKeyResult = Morphic.WindowsNative.Registry.CurrentUser.OpenSubKey("SOFTWARE", true); + //if (openSubKeyResult.IsError == true) + //{ + // return MorphicResult.ErrorResult(); + //} + //var hkcuSoftwareKey = openSubKeyResult.Value!; + //// + //var createSubKeyResult = hkcuSoftwareKey.CreateSubKey("Freedom Scientific"); + //if (createSubKeyResult.IsError == true) + //{ + // return MorphicResult.ErrorResult(); + //} + // + try + { + var hkcuSubKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("SOFTWARE", true); + if (hkcuSubKey is null) + { + return MorphicResult.ErrorResult(); + } + else + { + bool couldOpenExistingKey = false; + try + { + var freedomScientificSubKey = hkcuSubKey.OpenSubKey("Freedom Scientific", true); + couldOpenExistingKey = true; + } + catch + { + } + if (couldOpenExistingKey == false) + { + hkcuSubKey.CreateSubKey("Freedom Scientific", true); + } + } + } + catch + { + return MorphicResult.ErrorResult(); + } + + Dictionary msiCommandLineArgs = new(); + msiCommandLineArgs.Add("ARPSYSTEMCOMPONENT", "1"); + + // Eloquence.msi (x86) + var progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + var installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x86\\Eloquence.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // VideoAccessibility.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\VideoAccessibility.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // Utilities.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\Utilities.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FSElevation.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\FSElevation.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FsSynth.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\FsSynth.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // UIAHooks.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\UIAHooks.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // HookManager.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\HookManager.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // AccEventCache.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\AccEventCache.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // GlobalHooksDispatcher.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\GlobalHooksDispatcher.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FSReader.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\FSReader.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // TableOfContents.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\TableOfContents.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FSOmnipage.msi (x86) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x86\\FSOmnipage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FSOcr.msi (x86) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x86\\FSOcr.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FSOcr.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\FSOcr.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FSOcrTombstone (x86) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x86\\FSOcrTombstone.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FSOcrTombstone.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\FSOcrTombstone.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FSSupportTool.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\FSSupportTool.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ErrorReporting.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\ErrorReporting.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // TouchServer.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\TouchServer.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FusionInterface.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\FusionInterface.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // Authorization.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\Authorization.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // KeyboardManager.msi (x86) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x86\\KeyboardManager.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FSCam.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\FSCam.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // RdpSupport.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\RdpSupport.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // Telemetry.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\Telemetry.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // VoiceAssistant (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\VoiceAssistant.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // JAWSBase.msi (x64) + msiCommandLineArgs.Add("ENABLE_UNTRUSTED_FONTS_EXCEPTION", "1"); + msiCommandLineArgs.Add("SETUP_LANGUAGES", "enu"); + msiCommandLineArgs.Add("TANDEM", "1"); + msiCommandLineArgs.Add("REMOTE_ONLY", "0"); + // + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\JAWSBase.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + // + msiCommandLineArgs.Remove("ENABLE_UNTRUSTED_FONTS_EXCEPTION"); + msiCommandLineArgs.Remove("SETUP_LANGUAGES"); + msiCommandLineArgs.Remove("TANDEM"); + msiCommandLineArgs.Remove("REMOTE_ONLY"); + + // JAWSLanguage.msi (x64) + msiCommandLineArgs.Add("PRIMARY_LANGUAGE", "1"); + msiCommandLineArgs.Add("TANDEM", "1"); + // + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\JAWSLanguage.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + // + msiCommandLineArgs.Remove("PRIMARY_LANGUAGE"); + msiCommandLineArgs.Remove("TANDEM"); + + // JAWSStart.msi + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\JAWSStart.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // JAWS setup package.exe + installExeResult = await this.InstallExeAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "JAWS setup package.exe"), "/Type silent"); + if (installExeResult.IsError == true) + { + return MorphicResult.ErrorResult(); + } + this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT).Invoke(1.0); + currentStep += 1; + + var result = new InstallMsiResult() { RebootRequired = rebootRequired }; + return MorphicResult.OkResult(result); + } + + private async Task> InstallCustom_ZoomTextAsync(AtSoftwareDetails atSoftwareDetails) + { + bool rebootRequired = false; + + int currentStep = 0; + const int TOTAL_STEP_COUNT = 22; + + // Sentinel System Driver Installer 7.6.1.exe + var installExeResult = await this.InstallExeAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "prerequisites\\Sentinel System Driver Installer 7.6.1.exe"), "/S /v/qn"); + if (installExeResult.IsError == true) + { + return MorphicResult.ErrorResult(); + } + this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT).Invoke(1.0); + currentStep += 1; + + // vcredist2022_x64.exe + installExeResult = await this.InstallExeAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "prerequisites\\vcredist2022_x64.exe"), "/install /quiet /norestart"); + if (installExeResult.IsError == true) + { + return MorphicResult.ErrorResult(); + } + this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT).Invoke(1.0); + currentStep += 1; + + // vcredist2022_x86.exe + installExeResult = await this.InstallExeAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "prerequisites\\vcredist2022_x86.exe"), "/install /quiet /norestart"); + if (installExeResult.IsError == true) + { + return MorphicResult.ErrorResult(); + } + this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT).Invoke(1.0); + currentStep += 1; + + // windowsdesktop-runtime-6.0.11-win-x64.exe + installExeResult = await this.InstallExeAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "prerequisites\\windowsdesktop-runtime-6.0.11-win-x64.exe"), "/install /quiet /norestart"); + if (installExeResult.IsError == true) + { + return MorphicResult.ErrorResult(); + } + this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT).Invoke(1.0); + currentStep += 1; + + // windowsdesktop-runtime-6.0.11-win-x86.exe + installExeResult = await this.InstallExeAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "prerequisites\\windowsdesktop-runtime-6.0.11-win-x86.exe"), "/install /quiet /norestart"); + if (installExeResult.IsError == true) + { + return MorphicResult.ErrorResult(); + } + this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT).Invoke(1.0); + currentStep += 1; + + // create registry key + //// NOTE: we should create a "CreateSubKey" function in Morphic.WindowsNative.Registry.RegistryKey + //var openSubKeyResult = Morphic.WindowsNative.Registry.CurrentUser.OpenSubKey("SOFTWARE", true); + //if (openSubKeyResult.IsError == true) + //{ + // return MorphicResult.ErrorResult(); + //} + //var hkcuSoftwareKey = openSubKeyResult.Value!; + //// + //var createSubKeyResult = hkcuSoftwareKey.CreateSubKey("Freedom Scientific"); + //if (createSubKeyResult.IsError == true) + //{ + // return MorphicResult.ErrorResult(); + //} + // + try + { + var hkcuSubKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("SOFTWARE", true); + if (hkcuSubKey is null) + { + return MorphicResult.ErrorResult(); + } + else + { + bool couldOpenExistingKey = false; + try + { + var freedomScientificSubKey = hkcuSubKey.OpenSubKey("Freedom Scientific", true); + couldOpenExistingKey = true; + } + catch + { + } + if (couldOpenExistingKey == false) + { + hkcuSubKey.CreateSubKey("Freedom Scientific", true); + } + } + } + catch + { + return MorphicResult.ErrorResult(); + } + + Dictionary msiCommandLineArgs = new(); + msiCommandLineArgs.Add("ARPSYSTEMCOMPONENT", "1"); + + // fsElevation.msi (x64) + var progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + var installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x64\\fsElevation.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FSSupportTool.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\FSSupportTool.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ErrorReporting.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\ErrorReporting.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // Authorization.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x64\\Authorization.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // KeyboardManager.msi (unknown architecture) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "KeyboardManager.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // FsSynth.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x64\\FsSynth.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // RdpSupport.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "x64\\RdpSupport.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // VocalizerExpressive-2.2.206-enu-Tom-Compact-enu.msi (unknown architecture) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "VocalizerExpressive-2.2.206-enu-Tom-Compact-enu.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // VocalizerExpressive-2.2.206-enu-Zoe-Compact-enu.msi (unknown architecture) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "VocalizerExpressive-2.2.206-enu-Zoe-Compact-enu.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // HookManager.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\HookManager.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // Telemetry.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\Telemetry.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // AccEventCache.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\AccEventCache.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // GlobalHooksDispatcher.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\GlobalHooksDispatcher.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // UIAHooks.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\UIAHooks.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // VoiceAssistant.msi (x64) + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\VoiceAssistant.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + + // ZoomText.msi (enu) (x64) + msiCommandLineArgs.Add("PRODUCT_TYPE", "2"); + msiCommandLineArgs.Add("PRIMARY_LANGUAGE", "1"); + msiCommandLineArgs.Add("SETUP_LANGUAGES", "enu"); + // + progressFunction = this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT); + installMsiResult = await this.InstallMsiAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "enu\\x64\\ZoomText.msi"), msiCommandLineArgs, progressFunction); + if (installMsiResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installMsiResult.Value!.RebootRequired == true) { rebootRequired = true; } + currentStep += 1; + // + msiCommandLineArgs.Remove("PRODUCT_TYPE"); + msiCommandLineArgs.Remove("PRIMARY_LANGUAGE"); + msiCommandLineArgs.Remove("SETUP_LANGUAGES"); + + // ZoomTextSetupPackage.exe + installExeResult = await this.InstallExeAsync(System.IO.Path.Combine(atSoftwareDetails.DownloadUri.LocalPath, "ZoomTextSetupPackage.exe"), "/Type silent"); + if (installExeResult.IsError == true) + { + return MorphicResult.ErrorResult(); + } + this.CreateProgressFunction(currentStep, TOTAL_STEP_COUNT).Invoke(1.0); + currentStep += 1; + + var result = new InstallMsiResult() { RebootRequired = rebootRequired }; + return MorphicResult.OkResult(result); + } + + private async Task> InstallMultipleOfflineInstallersAsync(AtSoftwareDetails atSoftwareDetails) + { + bool rebootRequired = false; + + await Application.Current.Dispatcher.InvokeAsync(() => + { + this.InstallStatusLabel.Content = "Installing " + atSoftwareDetails.ProductName + "..."; + this.ProgressBar.Value = 0.0; + this.ProgressBar.IsIndeterminate = false; + }); + + switch (atSoftwareDetails.ShortName) + { + case "fusion": + { + var installResult = await InstallCustom_FusionAsync(atSoftwareDetails); + if (installResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installResult.Value!.RebootRequired == true) { rebootRequired = true; } + } + break; + case "jaws": + { + var installResult = await InstallCustom_JawsAsync(atSoftwareDetails); + if (installResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installResult.Value!.RebootRequired == true) { rebootRequired = true; } + } + break; + case "zoomtext": + { + var installResult = await InstallCustom_ZoomTextAsync(atSoftwareDetails); + if (installResult.IsError == true) { return MorphicResult.ErrorResult(); } + if (installResult.Value!.RebootRequired == true) { rebootRequired = true; } + } + break; + default: + throw new Exception("invalid case"); + } + + var result = new InstallMsiResult() { RebootRequired = rebootRequired }; + return MorphicResult.OkResult(result); + } + private async Task> InstallZipFileWithEmbeddedMsiAsync(AtSoftwareDetails atSoftwareDetails) { bool rebootRequired; diff --git a/Morphic.Client/Dialogs/LoginPanel.xaml.cs b/Morphic.Client/Dialogs/LoginPanel.xaml.cs index 04c50fd7..bcee07ff 100644 --- a/Morphic.Client/Dialogs/LoginPanel.xaml.cs +++ b/Morphic.Client/Dialogs/LoginPanel.xaml.cs @@ -33,8 +33,6 @@ namespace Morphic.Client.Dialogs using Microsoft.Extensions.Logging; using Service; using Settings.SolutionsRegistry; - using System.Linq; - using Morphic.Client.Config; /// /// Login window for authenticating users and applying their settings @@ -116,7 +114,7 @@ private async Task Login() else { // login successful - await App.Current.Countly_RecordEventAsync("signIn"); + await App.Current.Telemetry_RecordEventAsync("signIn"); // first, apply preferences // NOTE: in the future, we should filter out any preferences which are specific to an AT application which is not yet installed (and then we can apply THOSE preferences immediately after installing those applications--or perhaps after reboot if the applications needed a reboot after installation and we haven't marked them as "okay to apply settings before reboot") @@ -127,25 +125,14 @@ private async Task Login() } // before we apply preferences, offer to install any necessary AT software using AToD - System.Collections.Generic.List atSoftwareToInstall = new(); - if (ConfigurableFeatures.AtOnDemandIsEnabled == true) - { - // get the list of AT software which could be installed on this computer - var availableAtSoftwareToInstall = Morphic.Client.Dialogs.AtOnDemand.AtOnDemandHelpers.GetListOfAtSoftwareToInstall(); - // - // capture a list of AT software which the user's cloud vault says should be installed (or at least offered to be installed) - var userPreferencesAtSoftwareToInstall = this.morphicSession.GetListOfAtSoftwareToInstall(); - // - // create a final list of AT software to offer to the user (based on the list of software which can be installed, filtered by the list of which software the user would like to be installed) - atSoftwareToInstall = availableAtSoftwareToInstall.Where((atSoftware) => userPreferencesAtSoftwareToInstall.Contains(atSoftware.ShortName) == true).ToList(); - } + var atSoftwareToInstall = Morphic.Client.Dialogs.AtOnDemand.AtOnDemandHelpers.GetListOfAtSoftwareToInstall(); // if some AT needs to be installed (or at least offered to the user for install), do so now; otherwise apply preferences and complete the login process if (atSoftwareToInstall.Count > 0) { var selectAppsPanel = this.StepFrame.PushPanel(); selectAppsPanel.ApplyPreferencesAfterLogin = this.ApplyPreferencesAfterLogin; - selectAppsPanel.ListOfAtSoftware = atSoftwareToInstall!; + selectAppsPanel.ListOfAtSoftware = atSoftwareToInstall; selectAppsPanel.Completed += (o, args) => this.Completed?.Invoke(this, EventArgs.Empty); } else diff --git a/Morphic.Client/Menu/MorphicHybridTrayIcon.cs b/Morphic.Client/Menu/MorphicHybridTrayIcon.cs index 49168f86..c603347f 100644 --- a/Morphic.Client/Menu/MorphicHybridTrayIcon.cs +++ b/Morphic.Client/Menu/MorphicHybridTrayIcon.cs @@ -143,6 +143,13 @@ public bool Visible // + public void SuppressTaskbarButtonResurfaceChecks(bool suppress) + { + _trayButton?.SuppressTaskbarButtonResurfaceChecks(suppress); + } + + // + private void InitializeTrayIcon() { if (_notifyIcon is not null) { diff --git a/Morphic.Client/Menu/MorphicMenu.xaml.cs b/Morphic.Client/Menu/MorphicMenu.xaml.cs index bc00e684..fcb082e8 100644 --- a/Morphic.Client/Menu/MorphicMenu.xaml.cs +++ b/Morphic.Client/Menu/MorphicMenu.xaml.cs @@ -1,7 +1,6 @@ namespace Morphic.Client.Menu { using Bar.UI; - using CountlySDK; using Morphic.Client.Config; using Morphic.Client.Dialogs; using Morphic.WindowsNative.Input; @@ -84,42 +83,37 @@ internal async Task ShowAsync(Control? control = null, MenuOpenedSource? menuOpe this.PlacementTarget = control; } + // to prevent the bottom of our menu from being covered, defer any "resurface task button" checks until our menu is closed; we do this because resurfacing the tray button--whose owner is the taskbar--can cause our menu to be covered by the taskbar; this is especially problematic when a timer does this frequently + App.Current.SuppressTaskbarButtonResurfaceChecks(true); + // + // [when the menu is closed, we will cancel the suppression so that our task button can continue resurfacing itself + this.Closed += (sender, eventArgs) => { + App.Current.SuppressTaskbarButtonResurfaceChecks(false); + }; + // + // open our menu this.IsOpen = true; - var segmentation = CreateMenuOpenedSourceSegmentation(_menuOpenedSource); - await App.Current.Countly_RecordEventAsync("showMenu", 1, segmentation); - } - - private CountlySDK.Segmentation CreateMenuOpenedSourceSegmentation(MenuOpenedSource? menuOpenedSource) - { - var segmentation = new CountlySDK.Segmentation(); - if (_menuOpenedSource is not null) - { - segmentation.Add("eventSource", _menuOpenedSource.ToString() + "Menu"); - } - return segmentation; + await App.Current.Telemetry_RecordEventAsync("showMenu"); } private async void ShowBarClick(object sender, RoutedEventArgs e) { this.App.BarManager.ShowBar(); // - var segmentation = CreateMenuOpenedSourceSegmentation(_menuOpenedSource); - await App.Current.Countly_RecordEventAsync("morphicBarShow", 1, segmentation); + await App.Current.Telemetry_RecordEventAsync("morphicBarShow"); } private async void HideBarClick(object sender, RoutedEventArgs e) { this.App.BarManager.HideBar(); // - var segmentation = CreateMenuOpenedSourceSegmentation(_menuOpenedSource); - await App.Current.Countly_RecordEventAsync("morphicBarHide", 1, segmentation); + await App.Current.Telemetry_RecordEventAsync("morphicBarHide"); } private async void QuitClick(object sender, RoutedEventArgs e) { - var segmentation = CreateMenuOpenedSourceSegmentation(_menuOpenedSource); - await App.Current.Countly_RecordEventAsync("quit", 1, segmentation); + await App.Current.Telemetry_RecordEventAsync("quit"); this.App.BarManager.CloseBar(); this.App.Shutdown(); @@ -130,10 +124,10 @@ private async void AutorunAfterLoginClicked(object sender, RoutedEventArgs e) switch (AutorunAfterLoginItem.IsChecked) { case true: - await App.Current.Countly_RecordEventAsync("autorunAfterLoginEnabled"); + await App.Current.Telemetry_RecordEventAsync("autorunAfterLoginEnabled"); break; case false: - await App.Current.Countly_RecordEventAsync("autorunAfterLoginDisabled"); + await App.Current.Telemetry_RecordEventAsync("autorunAfterLoginDisabled"); break; } } @@ -143,10 +137,10 @@ private async void ShowMorphicBarAfterLoginClicked(object sender, RoutedEventArg switch (ShowMorphicBarAfterLoginItem.IsChecked) { case true: - await App.Current.Countly_RecordEventAsync("showMorphicBarAfterLoginEnabled"); + await App.Current.Telemetry_RecordEventAsync("showMorphicBarAfterLoginEnabled"); break; case false: - await App.Current.Countly_RecordEventAsync("showMorphicBarAfterLoginDisabled"); + await App.Current.Telemetry_RecordEventAsync("showMorphicBarAfterLoginDisabled"); break; } } @@ -291,11 +285,11 @@ private async void StopKeyRepeatToggle(object sender, RoutedEventArgs e) if (menuItem.IsChecked == true) { - await App.Current.Countly_RecordEventAsync("stopKeyRepeatOn"); + await App.Current.Telemetry_RecordEventAsync("stopKeyRepeatOn"); } else { - await App.Current.Countly_RecordEventAsync("stopKeyRepeatOff"); + await App.Current.Telemetry_RecordEventAsync("stopKeyRepeatOff"); } } } @@ -304,6 +298,12 @@ private async void StopKeyRepeatToggle(object sender, RoutedEventArgs e) private MorphicHybridTrayIcon? _trayIcon = null; + internal void SuppressTaskbarButtonResurfaceChecks(bool suppress) + { + // OBSERVATION: in the current implementation, the taskbar ("tray") button is owned by the menu control + _trayIcon?.SuppressTaskbarButtonResurfaceChecks(suppress); + } + private void ShowTrayIcon() { // TODO: re-implement using solutions registry. @@ -347,17 +347,13 @@ await this.Dispatcher.InvokeAsync(async () => { this.App.BarManager.HideBar(); // - var segmentation = new CountlySDK.Segmentation(); - segmentation.Add("eventSource", "trayIconClick"); - await App.Current.Countly_RecordEventAsync("morphicBarHide", 1, segmentation); + await App.Current.Telemetry_RecordEventAsync("morphicBarHide"); } else { this.App.BarManager.ShowBar(); // - var segmentation = new CountlySDK.Segmentation(); - segmentation.Add("eventSource", "trayIconClick"); - await App.Current.Countly_RecordEventAsync("morphicBarShow", 1, segmentation); + await App.Current.Telemetry_RecordEventAsync("morphicBarShow"); } }); } @@ -381,47 +377,39 @@ private async void Login(object sender, RoutedEventArgs e) private async void CustomizeMorphicbarClicked(object sender, RoutedEventArgs e) { - var segmentation = CreateMenuOpenedSourceSegmentation(_menuOpenedSource); - await App.Current.Countly_RecordEventAsync("customizeMorphicbar", 1, segmentation); + await App.Current.Telemetry_RecordEventAsync("customizeMorphicbar"); // NOTE: when we make "navigate to URL" a custom action (rather than something linked in the menu itself), then we should navigate to the appsettings value for the key"BarEditorWebAppUrlAsString" } private async void ContactUsClicked(object sender, RoutedEventArgs e) { - var segmentation = CreateMenuOpenedSourceSegmentation(_menuOpenedSource); - await App.Current.Countly_RecordEventAsync("contactUs", 1, segmentation); + await App.Current.Telemetry_RecordEventAsync("contactUs"); } private async void ExploreMorphicClicked(object sender, RoutedEventArgs e) { - var segmentation = CreateMenuOpenedSourceSegmentation(_menuOpenedSource); - await App.Current.Countly_RecordEventAsync("exploreMorphic", 1, segmentation); + await App.Current.Telemetry_RecordEventAsync("exploreMorphic"); } private async void HowToCopySetupsClicked(object sender, RoutedEventArgs e) { - var segmentation = CreateMenuOpenedSourceSegmentation(_menuOpenedSource); - await App.Current.Countly_RecordEventAsync("howToCopySetups", 1, segmentation); + await App.Current.Telemetry_RecordEventAsync("howToCopySetups"); } private async void QuickDemoVideosClicked(object sender, RoutedEventArgs e) { - var segmentation = CreateMenuOpenedSourceSegmentation(_menuOpenedSource); - segmentation.Add("category", "main"); - await App.Current.Countly_RecordEventAsync("quickDemoVideo", 1, segmentation); + await App.Current.Telemetry_RecordEventAsync("quickDemoVideo"); } private async void OtherHelpfulThingsClicked(object sender, RoutedEventArgs e) { - var segmentation = CreateMenuOpenedSourceSegmentation(_menuOpenedSource); - await App.Current.Countly_RecordEventAsync("otherHelpfulThings", 1, segmentation); + await App.Current.Telemetry_RecordEventAsync("otherHelpfulThings"); } private async void AboutMorphicClicked(object sender, RoutedEventArgs e) { - var segmentation = CreateMenuOpenedSourceSegmentation(_menuOpenedSource); - await App.Current.Countly_RecordEventAsync("aboutMorphic", 1, segmentation); + await App.Current.Telemetry_RecordEventAsync("aboutMorphic"); } private void SelectBasicMorphicBarClick(object sender, RoutedEventArgs e) @@ -431,7 +419,5 @@ private void SelectBasicMorphicBarClick(object sender, RoutedEventArgs e) App.Current.BarManager.LoadBasicMorphicBar(); } } - - } diff --git a/Morphic.Client/Menu/MorphicMenuItem.cs b/Morphic.Client/Menu/MorphicMenuItem.cs index 27dd0e5b..5133b459 100644 --- a/Morphic.Client/Menu/MorphicMenuItem.cs +++ b/Morphic.Client/Menu/MorphicMenuItem.cs @@ -1,7 +1,6 @@ namespace Morphic.Client.Menu { using Config; - using CountlySDK; using System; using System.Collections.Generic; using System.Diagnostics; @@ -64,45 +63,17 @@ internal static async Task RecordMenuItemTelemetryAsync(string? openPath, Morphi { case MorphicMenuItemTelemetryType.Settings: { - var segmentation = new Segmentation(); - var settingCategoryName = telemetryCategory; - if (settingCategoryName is not null) - { - segmentation.Add("category", settingCategoryName); - } - // - segmentation.Add("eventSource", eventSource); - // - await App.Current.Countly_RecordEventAsync("systemSettings", 1, segmentation); - //await App.Current.Countly_RecordEventAsync("systemSettings" + settingCategoryName); + await App.Current.Telemetry_RecordEventAsync("systemSettings"); } break; case MorphicMenuItemTelemetryType.LearnMore: { - var segmentation = new Segmentation(); - var settingCategoryName = telemetryCategory; - if (settingCategoryName is not null) - { - segmentation.Add("category", settingCategoryName); - } - // - segmentation.Add("eventSource", eventSource); - // - await App.Current.Countly_RecordEventAsync("learnMore", 1, segmentation); + await App.Current.Telemetry_RecordEventAsync("learnMore"); } break; case MorphicMenuItemTelemetryType.QuickDemoVideo: { - var segmentation = new Segmentation(); - var settingCategoryName = telemetryCategory; - if (settingCategoryName is not null) - { - segmentation.Add("category", settingCategoryName); - } - // - segmentation.Add("eventSource", eventSource); - // - await App.Current.Countly_RecordEventAsync("quickDemoVideo", 1, segmentation); + await App.Current.Telemetry_RecordEventAsync("quickDemoVideo"); } break; default: @@ -159,12 +130,7 @@ internal static async Task RecordMenuItemTelemetryAsync(string? openPath, Morphi } if (settingCategoryName is not null) { - var segmentation = new Segmentation(); - segmentation.Add("category", settingCategoryName); - segmentation.Add("eventSource", eventSource); - // - await App.Current.Countly_RecordEventAsync("systemSettings", 1, segmentation); - //await App.Current.Countly_RecordEventAsync("systemSettings" + settingCategoryName); + await App.Current.Telemetry_RecordEventAsync("systemSettings"); } } break; diff --git a/Morphic.Client/Morphic.Client.csproj b/Morphic.Client/Morphic.Client.csproj index 53aff4a3..8ed36a77 100644 --- a/Morphic.Client/Morphic.Client.csproj +++ b/Morphic.Client/Morphic.Client.csproj @@ -89,7 +89,6 @@ - diff --git a/Morphic.Client/appsettings.Debug.json b/Morphic.Client/appsettings.Debug.json index 7de06dc3..f82c9345 100644 --- a/Morphic.Client/appsettings.Debug.json +++ b/Morphic.Client/appsettings.Debug.json @@ -6,10 +6,6 @@ "Update": { "AppCastUrl": "" }, - "Countly": { - "ServerUrl": "https://countly.morphic.dev", - "AppKey": "b518ed6ca13aae3f60197dc5fdda2a0e649a54b3" - }, "Telemetry": { "ServerHostname": "telemetrymqtt.morphic.org", "AppName": "morphic-windows", diff --git a/Morphic.Client/appsettings.Development.canary.json b/Morphic.Client/appsettings.Development.canary.json index fe5dd0c3..f47e2009 100644 --- a/Morphic.Client/appsettings.Development.canary.json +++ b/Morphic.Client/appsettings.Development.canary.json @@ -4,12 +4,10 @@ "Default": "Debug", "System": "Information", "Microsoft": "Information", - "CountlySDK": "Debug" }, "Console": { "IncludeScopes": true, "LogLevel": { - "CountlySDK": "Debug" } } }, @@ -21,10 +19,6 @@ "Update": { "AppCastUrl": "https://app.morphic.org/autoupdate/morphic-windows-canary.appcast.xml" }, - "Countly": { - "ServerUrl": "https://countly.morphic.org", - "AppKey": "809a28e3be9fbc1e7e178cf99186af799fa87048" - }, "Telemetry": { "ServerHostname": "telemetrymqtt.morphic.org", "AppName": "morphic-windows", diff --git a/Morphic.Client/appsettings.Development.dev.json b/Morphic.Client/appsettings.Development.dev.json index ef04840b..e3c25dbf 100644 --- a/Morphic.Client/appsettings.Development.dev.json +++ b/Morphic.Client/appsettings.Development.dev.json @@ -4,12 +4,10 @@ "Default": "Debug", "System": "Information", "Microsoft": "Information", - "CountlySDK": "Debug" }, "Console": { "IncludeScopes": true, "LogLevel": { - "CountlySDK": "Debug" } } }, @@ -21,10 +19,6 @@ "Update": { "AppCastUrl": "https://app.morphic.dev/autoupdate/morphic-windows.appcast.xml" }, - "Countly": { - "ServerUrl": "https://countly.morphic.dev", - "AppKey": "b518ed6ca13aae3f60197dc5fdda2a0e649a54b3" - }, "Telemetry": { "ServerHostname": "telemetrymqtt.morphic.org", "AppName": "morphic-windows", diff --git a/Morphic.Client/appsettings.Development.json b/Morphic.Client/appsettings.Development.json index fe5dd0c3..f47e2009 100644 --- a/Morphic.Client/appsettings.Development.json +++ b/Morphic.Client/appsettings.Development.json @@ -4,12 +4,10 @@ "Default": "Debug", "System": "Information", "Microsoft": "Information", - "CountlySDK": "Debug" }, "Console": { "IncludeScopes": true, "LogLevel": { - "CountlySDK": "Debug" } } }, @@ -21,10 +19,6 @@ "Update": { "AppCastUrl": "https://app.morphic.org/autoupdate/morphic-windows-canary.appcast.xml" }, - "Countly": { - "ServerUrl": "https://countly.morphic.org", - "AppKey": "809a28e3be9fbc1e7e178cf99186af799fa87048" - }, "Telemetry": { "ServerHostname": "telemetrymqtt.morphic.org", "AppName": "morphic-windows", diff --git a/Morphic.Client/appsettings.Development.preview.json b/Morphic.Client/appsettings.Development.preview.json index a36fb31b..2c180095 100644 --- a/Morphic.Client/appsettings.Development.preview.json +++ b/Morphic.Client/appsettings.Development.preview.json @@ -4,12 +4,10 @@ "Default": "Debug", "System": "Information", "Microsoft": "Information", - "CountlySDK": "Debug" }, "Console": { "IncludeScopes": true, "LogLevel": { - "CountlySDK": "Debug" } } }, @@ -21,10 +19,6 @@ "Update": { "AppCastUrl": "https://app.morphic.org/autoupdate/morphic-windows-preview.appcast.xml" }, - "Countly": { - "ServerUrl": "https://countly.morphic.org", - "AppKey": "809a28e3be9fbc1e7e178cf99186af799fa87048" - }, "Telemetry": { "ServerHostname": "telemetrymqtt.morphic.org", "AppName": "morphic-windows", diff --git a/Morphic.Client/appsettings.Production.json b/Morphic.Client/appsettings.Production.json index 1f120810..b192c88c 100644 --- a/Morphic.Client/appsettings.Production.json +++ b/Morphic.Client/appsettings.Production.json @@ -4,12 +4,10 @@ "Default": "Debug", "System": "Information", "Microsoft": "Information", - "CountlySDK": "Debug" }, "Console": { "IncludeScopes": true, "LogLevel": { - "CountlySDK": "Debug" } } }, @@ -21,10 +19,6 @@ "Update": { "AppCastUrl": "https://app.morphic.org/autoupdate/morphic-windows.appcast.xml" }, - "Countly": { - "ServerUrl": "https://countly.morphic.org", - "AppKey": "809a28e3be9fbc1e7e178cf99186af799fa87048" - }, "Telemetry": { "ServerHostname": "telemetrymqtt.morphic.org", "AppName": "morphic-windows", diff --git a/Morphic.Client/appsettings.Staging.json b/Morphic.Client/appsettings.Staging.json index 58e62fa5..5c422107 100644 --- a/Morphic.Client/appsettings.Staging.json +++ b/Morphic.Client/appsettings.Staging.json @@ -4,12 +4,10 @@ "Default": "Debug", "System": "Information", "Microsoft": "Information", - "CountlySDK": "Debug" }, "Console": { "IncludeScopes": true, "LogLevel": { - "CountlySDK": "Debug" } } }, @@ -20,10 +18,6 @@ "Update": { "AppCastUrl": "https://app.morphic-staging.com/autoupdate/morphic-windows.appcast.xml" }, - "Countly": { - "ServerUrl": "https://countly.morphic.org", - "AppKey": "809a28e3be9fbc1e7e178cf99186af799fa87048" - }, "Telemetry": { "ServerHostname": "telemetrymqtt.morphic.org", "AppName": "morphic-windows", diff --git a/Morphic.Client/solutions.json5 b/Morphic.Client/solutions.json5 index 018d2999..6c912d8a 100644 --- a/Morphic.Client/solutions.json5 +++ b/Morphic.Client/solutions.json5 @@ -102,17 +102,5 @@ } } ] - }, - "com.texthelp.readandwrite": { - settings: [ - { - type: "installedApplication", - shortName: "readandwrite", - productCode: "355AB00F-48E8-474E-ACC4-D917BAFA4D58", - settings: { - installed: "isInstalled" - } - } - ] } } \ No newline at end of file diff --git a/Morphic.Controls/TrayButton/TrayButton.cs b/Morphic.Controls/TrayButton/TrayButton.cs index ef586476..43713d59 100644 --- a/Morphic.Controls/TrayButton/TrayButton.cs +++ b/Morphic.Controls/TrayButton/TrayButton.cs @@ -208,4 +208,10 @@ public bool Visible } } } + + public void SuppressTaskbarButtonResurfaceChecks(bool suppress) + { + _trayButton?.SuppressTaskbarButtonResurfaceChecks(suppress); + } + } diff --git a/Morphic.Controls/TrayButton/Windows11/TrayButton.cs b/Morphic.Controls/TrayButton/Windows11/TrayButton.cs index 0ea7c494..c5893d1e 100644 --- a/Morphic.Controls/TrayButton/Windows11/TrayButton.cs +++ b/Morphic.Controls/TrayButton/Windows11/TrayButton.cs @@ -171,6 +171,13 @@ public void Hide() // + public void SuppressTaskbarButtonResurfaceChecks(bool suppress) + { + _nativeWindow?.SuppressTaskbarButtonResurfaceChecks(suppress); + } + + // + private MorphicResult CreateNativeWindow() { // if our native window already exists, return an error diff --git a/Morphic.Controls/TrayButton/Windows11/TrayButtonNativeWindow.cs b/Morphic.Controls/TrayButton/Windows11/TrayButtonNativeWindow.cs index 46b314bc..538a58d7 100644 --- a/Morphic.Controls/TrayButton/Windows11/TrayButtonNativeWindow.cs +++ b/Morphic.Controls/TrayButton/Windows11/TrayButtonNativeWindow.cs @@ -45,6 +45,9 @@ internal class TrayButtonNativeWindow : NativeWindow, IDisposable private System.Windows.Visibility _visibility; private bool _taskbarIsTopmost; + private System.Threading.Timer? _resurfaceTaskbarButtonTimer; + private static readonly TimeSpan RESURFACE_TASKBAR_BUTTON_INTERVAL_TIMESPAN = new TimeSpan(0, 0, 30); + private ArgbImageNativeWindow? _argbImageNativeWindow = null; private IntPtr _tooltipWindowHandle; @@ -226,6 +229,9 @@ private TrayButtonNativeWindow() result._tooltipWindowHandle = result.CreateTooltipWindow(); result._tooltipText = null; + // start a timer on the new instance, to resurface the Morphic tray button icon from time to time (just in case it gets hidden under the taskbar) + result._resurfaceTaskbarButtonTimer = new(result.ResurfaceTaskButtonTimerCallback, null, TrayButtonNativeWindow.RESURFACE_TASKBAR_BUTTON_INTERVAL_TIMESPAN, TrayButtonNativeWindow.RESURFACE_TASKBAR_BUTTON_INTERVAL_TIMESPAN); + return MorphicResult.OkResult(result); } @@ -738,7 +744,7 @@ private void LocationChangeWindowEventProc(IntPtr hWinEventHook, uint eventType, { // if the window being moved was one of the task list windows (i.e. the windows that pop up above the taskbar), then our zorder has probably been pushed down. To counteract this, we make sure our window is "TOPMOST" // NOTE: in initial testing, we set the window to TOPMOST in the ExStyles during handle construction. This was not always successful in keeping the window topmost, however, possibly because the taskbar becomes "more" topmost sometimes. So we re-set the window zorder here instead (without activating the window). - PInvoke.User32.SetWindowPos(this.Handle, WindowsApi.HWND_TOPMOST, 0, 0, 0, 0, PInvoke.User32.SetWindowPosFlags.SWP_NOMOVE | PInvoke.User32.SetWindowPosFlags.SWP_NOSIZE | PInvoke.User32.SetWindowPosFlags.SWP_NOACTIVATE); + this.BringTaskButtonTopmostWithoutActivating(); } else if (className == "Shell_TrayWnd"/* || className == "ReBarWindow32"*/ || className == "TrayNotifyWnd") { @@ -749,6 +755,31 @@ private void LocationChangeWindowEventProc(IntPtr hWinEventHook, uint eventType, } } + // NOTE: this function is used to temporary suppress taskbar button resurface checks (which are done when the app needs to place other content above the taskbar and above our control...such as a right-click context menu) + public void SuppressTaskbarButtonResurfaceChecks(bool suppress) + { + if (suppress == true) + { + _resurfaceTaskbarButtonTimer?.Dispose(); + _resurfaceTaskbarButtonTimer = null; + } + else + { + _resurfaceTaskbarButtonTimer = new(this.ResurfaceTaskButtonTimerCallback, null, TrayButtonNativeWindow.RESURFACE_TASKBAR_BUTTON_INTERVAL_TIMESPAN, TrayButtonNativeWindow.RESURFACE_TASKBAR_BUTTON_INTERVAL_TIMESPAN); + } + } + + // NOTE: just in case we miss any edge cases to resurface our button, we resurface it from time to time on a timer + private void ResurfaceTaskButtonTimerCallback(object? state) + { + this.BringTaskButtonTopmostWithoutActivating(); + } + + private void BringTaskButtonTopmostWithoutActivating() + { + PInvoke.User32.SetWindowPos(this.Handle, WindowsApi.HWND_TOPMOST, 0, 0, 0, 0, PInvoke.User32.SetWindowPosFlags.SWP_NOMOVE | PInvoke.User32.SetWindowPosFlags.SWP_NOSIZE | PInvoke.User32.SetWindowPosFlags.SWP_NOACTIVATE); + } + private void ObjectReorderWindowEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint idEventThread, uint dwmsEventTime) { // we cannot process an object reorder message if the hwnd is zero @@ -765,10 +796,17 @@ private void ObjectReorderWindowEventProc(IntPtr hWinEventHook, uint eventType, className = getWindowClassNameResult.Value!; } - if (className == "Shell_TrayWnd") + // capture the desktop handle + var desktopHandle = PInvoke.User32.GetDesktopWindow(); + + // if the reordered window was either the taskbar or the desktop, update the _taskbarIsTopmost state; this will generally be triggered when an app goes full-screen (or full-screen mode is exited) + if (className == "Shell_TrayWnd" || hwnd == desktopHandle) { + // whenever the window ordering changes, resurface our control + this.BringTaskButtonTopmostWithoutActivating(); + // determine if the taskbar is topmost; the taskbar's topmost flag is removed when an app goes full-screen and should cover the taskbar (e.g. a full-screen video) - _taskbarIsTopmost = TrayButtonNativeWindow.IsTaskbarTopmost(hwnd); + _taskbarIsTopmost = TrayButtonNativeWindow.IsTaskbarTopmost(/*hwnd -- not passed in, since the handle could be the desktop */); // // NOTE: UpdateVisibility takes both the .Visibility property and the topmost state of the taskbar into consideration to determine whether or not to show the control this.UpdateVisibility(); @@ -921,6 +959,13 @@ private void UpdateTooltipTextAndTracking() return; } + var trayButtonNativeWindowHandle = this.Handle; + if (trayButtonNativeWindowHandle == IntPtr.Zero) + { + // tray button window does not exist; there is no tool window to update + return; + } + var getClientRectSuccess = PInvoke.User32.GetClientRect(this.Handle, out var trayButtonClientRect); if (getClientRectSuccess == false) { @@ -1005,8 +1050,8 @@ internal static PInvoke.RECT CalculateCenterRectInsideRect(PInvoke.RECT outerRec var taskbarHandle = TrayButtonNativeWindow.GetWindowsTaskbarHandle(); if (taskbarHandle == IntPtr.Zero) { return MorphicResult.ErrorResult(); } // - //var taskButtonContainerHandle = TrayButtonNativeWindow.GetWindowsTaskbarTaskButtonContainerHandle(taskbarHandle); - //if (taskButtonContainerHandle == IntPtr.Zero) { return MorphicResult.ErrorResult(); } + var taskButtonContainerHandle = TrayButtonNativeWindow.GetWindowsTaskbarTaskButtonContainerHandle(taskbarHandle); + if (taskButtonContainerHandle == IntPtr.Zero) { return MorphicResult.ErrorResult(); } // var notifyTrayHandle = TrayButtonNativeWindow.GetWindowsTaskbarNotificationTrayHandle(taskbarHandle); if (notifyTrayHandle == IntPtr.Zero) { return MorphicResult.ErrorResult(); } @@ -1016,8 +1061,8 @@ internal static PInvoke.RECT CalculateCenterRectInsideRect(PInvoke.RECT outerRec var getTaskbarRectSuccess = PInvoke.User32.GetWindowRect(taskbarHandle, out var taskbarRect); if (getTaskbarRectSuccess == false) { return MorphicResult.ErrorResult(); } // - //var getTaskButtonContainerRectSuccess = PInvoke.User32.GetWindowRect(taskButtonContainerHandle, out var taskButtonContainerRect); - //if (getTaskButtonContainerRectSuccess == false) { return MorphicResult.ErrorResult(); } + var getTaskButtonContainerRectSuccess = PInvoke.User32.GetWindowRect(taskButtonContainerHandle, out var taskButtonContainerRect); + if (getTaskButtonContainerRectSuccess == false) { return MorphicResult.ErrorResult(); } // var getNotifyTrayRectSuccess = PInvoke.User32.GetWindowRect(notifyTrayHandle, out var notifyTrayRect); if (getNotifyTrayRectSuccess == false) { return MorphicResult.ErrorResult(); } @@ -1037,14 +1082,30 @@ internal static PInvoke.RECT CalculateCenterRectInsideRect(PInvoke.RECT outerRec // establish the appropriate size for our tray button (i.e. same height/width as taskbar, and with an aspect ratio of 8:10) int trayButtonHeight; int trayButtonWidth; + // NOTE: on some computers, the taskbar and notify tray return an inaccurate size, but the task button container appears to always return the correct size; therefore we match our primary dimension to the taskbutton container's same dimension + // NOTE: the inaccurate size returned by GetWindowRect may be due to our moving this class from the main application to a helper library (i.e. perhaps the pixel scaling isn't applying correctly), or it could just be a weird quirk on some computers. + // [The GetWindowRect issue hapepns with both our own homebuilt PINVOKE methods as well as with PInvoke.User32.GetWindowRect; the function is returning the correct left, bottom and right positions of the taskbar and notify tray--but is + // sometimes misrepresenting the top (i.e. height) value of both the taskbar and notify tray rects] if (taskbarOrientation == Orientation.Horizontal) { - trayButtonHeight = taskbarRect.bottom - taskbarRect.top; + // option 1: base our primary dimension off of the taskbutton container's same dimension + trayButtonHeight = taskButtonContainerRect.bottom - taskButtonContainerRect.top; + // + // option 2: base our primary dimension off of the taskbar's same dimension + //trayButtonHeight = taskbarRect.bottom - taskbarRect.top; + // + // [and then scale the secondary dimension to 80% of the size of the primary dimension] trayButtonWidth = (int)((Double)trayButtonHeight * 0.8); } else { - trayButtonWidth = taskbarRect.right - taskbarRect.left; + // option 1: base our primary dimension off of the taskbutton container's same dimension + trayButtonWidth = taskButtonContainerRect.right - taskButtonContainerRect.left; + // + // option 2: base our primary dimension off of the taskbar's same dimension + //trayButtonWidth = taskbarRect.right - taskbarRect.left; + // + // [and then scale the secondary dimension to 80% of the size of the primary dimension] trayButtonHeight = (int)((Double)trayButtonWidth * 0.8); } @@ -1054,11 +1115,13 @@ internal static PInvoke.RECT CalculateCenterRectInsideRect(PInvoke.RECT outerRec if (taskbarOrientation == Orientation.Horizontal) { trayButtonX = notifyTrayRect.left - trayButtonWidth; - trayButtonY = taskbarRect.top; + // NOTE: if we have any issues with positioning, try to replace taskbarRect.bottom with taskButtoncontainerRect.bottom (if we chose option #1 for our size calculations above) + trayButtonY = taskbarRect.bottom - trayButtonHeight; } else { - trayButtonX = taskbarRect.left; + // NOTE: if we have any issues with positioning, try to replace taskbarRect.bottom with taskButtoncontainerRect.right (if we chose option #1 for our size calculations above) + trayButtonX = taskbarRect.right - trayButtonWidth; trayButtonY = notifyTrayRect.top - trayButtonHeight; } diff --git a/Morphic.Service/MorphicSession.cs b/Morphic.Service/MorphicSession.cs index b94e202b..2148a3e8 100644 --- a/Morphic.Service/MorphicSession.cs +++ b/Morphic.Service/MorphicSession.cs @@ -219,18 +219,6 @@ public async Task ApplyAllPreferences() } } - public List GetListOfAtSoftwareToInstall() - { - if (this.Preferences is not null) - { - return this.Solutions.GetListOfAtSoftwareToInstall(this.Preferences); - } - else - { - return new List(); - } - } - /// /// Get a string preference /// diff --git a/Morphic.Settings/Morphic.Settings.csproj b/Morphic.Settings/Morphic.Settings.csproj index 74fc02dd..c4d04d0b 100644 --- a/Morphic.Settings/Morphic.Settings.csproj +++ b/Morphic.Settings/Morphic.Settings.csproj @@ -9,7 +9,6 @@ - diff --git a/Morphic.Settings/SettingsHandlers/Display/DisplayHandler.cs b/Morphic.Settings/SettingsHandlers/Display/DisplayHandler.cs index 489a6874..36cc791d 100644 --- a/Morphic.Settings/SettingsHandlers/Display/DisplayHandler.cs +++ b/Morphic.Settings/SettingsHandlers/Display/DisplayHandler.cs @@ -1,6 +1,5 @@ namespace Morphic.Settings.SettingsHandlers.Display { - using CountlySDK; using Morphic.WindowsNative.Display; using SolutionsRegistry; using System; @@ -106,42 +105,6 @@ public async Task SetZoom(Setting setting, object? newValue) // // set the new percentage _ = await targetDisplay.SetDpiScaleAsync(newDpiScale.Value); - // report the display scale (percentage) change - if (oldDpiScale is not null) - { - var segmentation = new Segmentation(); - if (recommendedDpiScale is not null) - { - var relativePercent = newDpiScale / recommendedDpiScale; - segmentation.Add("scalePercent", ((int)(relativePercent * 100)).ToString()); - - var recommendedDpiIndex = -currentDpiOffsetAndRange!.Value.MinimumDpiOffset; - var relativeDotOffset = index - recommendedDpiIndex; - segmentation.Add("dotOffset", relativeDotOffset.ToString()); - } - // - if (newDpiScale > oldDpiScale) { - // NOTE: we can't call our main Countly logic from here (which skips Countly event recording if it's not enabled), so we just swallow any "not init'd" errors here - try - { - await Countly.RecordEvent("textSizeIncrease", 1, segmentation); - } - catch (InvalidOperationException) - { - } - } - else - { - // NOTE: we can't call our main Countly logic from here (which skips Countly event recording if it's not enabled), so we just swallow any "not init'd" errors here - try - { - await Countly.RecordEvent("textSizeDecrease", 1, segmentation); - } - catch (InvalidOperationException) - { - } - } - } } } diff --git a/Morphic.Settings/SettingsHandlers/Install/InstalledApplicationSettingGroup.cs b/Morphic.Settings/SettingsHandlers/Install/InstalledApplicationSettingGroup.cs deleted file mode 100644 index 8141ab48..00000000 --- a/Morphic.Settings/SettingsHandlers/Install/InstalledApplicationSettingGroup.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2023 Raising the Floor - US, Inc. -// -// Licensed under the New BSD license. You may not use this file except in -// compliance with this License. -// -// You may obtain a copy of the License at -// https://github.com/raisingthefloor/morphic-windows/blob/master/LICENSE.txt -// -// The R&D leading to these results received funding from the: -// * Rehabilitation Services Administration, US Dept. of Education under -// grant H421A150006 (APCP) -// * National Institute on Disability, Independent Living, and -// Rehabilitation Research (NIDILRR) -// * Administration for Independent Living & Dept. of Education under grants -// H133E080022 (RERC-IT) and H133E130028/90RE5003-01-00 (UIITA-RERC) -// * European Union's Seventh Framework Programme (FP7/2007-2013) grant -// agreement nos. 289016 (Cloud4all) and 610510 (Prosperity4All) -// * William and Flora Hewlett Foundation -// * Ontario Ministry of Research and Innovation -// * Canadian Foundation for Innovation -// * Adobe Foundation -// * Consumer Electronics Association Foundation - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Morphic.Settings.SettingsHandlers.Install -{ - using Newtonsoft.Json; - - [SettingsHandlerType("installedApplication", typeof(InstalledApplicationSettingsHandler))] - public class InstalledApplicationSettingGroup : SettingGroup - { - /// The short name used to identify the application (including multiple installers as a group) by AT on Demand and Morphic. - [JsonProperty("shortName", Required = Required.Always)] - public string ShortName { get; set; } = null!; - - /// The product code GUID used by Windows to identify that an application (or MSI package) is installed. - [JsonProperty("productCode", Required = Required.Always)] - public string ProductCode { get; set; } = null!; - } -} diff --git a/Morphic.Settings/SettingsHandlers/Install/InstalledApplicationSettingsHandler.cs b/Morphic.Settings/SettingsHandlers/Install/InstalledApplicationSettingsHandler.cs deleted file mode 100644 index 1e28d12e..00000000 --- a/Morphic.Settings/SettingsHandlers/Install/InstalledApplicationSettingsHandler.cs +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2023 Raising the Floor - US, Inc. -// -// Licensed under the New BSD license. You may not use this file except in -// compliance with this License. -// -// You may obtain a copy of the License at -// https://github.com/raisingthefloor/morphic-windows/blob/master/LICENSE.txt -// -// The R&D leading to these results received funding from the: -// * Rehabilitation Services Administration, US Dept. of Education under -// grant H421A150006 (APCP) -// * National Institute on Disability, Independent Living, and -// Rehabilitation Research (NIDILRR) -// * Administration for Independent Living & Dept. of Education under grants -// H133E080022 (RERC-IT) and H133E130028/90RE5003-01-00 (UIITA-RERC) -// * European Union's Seventh Framework Programme (FP7/2007-2013) grant -// agreement nos. 289016 (Cloud4all) and 610510 (Prosperity4All) -// * William and Flora Hewlett Foundation -// * Ontario Ministry of Research and Innovation -// * Canadian Foundation for Innovation -// * Adobe Foundation -// * Consumer Electronics Association Foundation - -namespace Morphic.Settings.SettingsHandlers.Install -{ - using Morphic.Core; - using SolutionsRegistry; - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.IO; - using System.Threading.Tasks; - - [SrService] - public class InstalledApplicationSettingsHandler : FixedSettingsHandler - { - [Getter("isInstalled")] - public Task GetIsInstalled(Setting setting) - { - var settingGroup = setting.SettingGroup as InstalledApplicationSettingGroup; - var productCodeAsString = settingGroup.ProductCode; - Guid productCodeAsGuid; - var guidParseSuccess = Guid.TryParse(productCodeAsString, out productCodeAsGuid); - if (guidParseSuccess == false) - { - Debug.Assert(false, "Could not parse application's GUID from solution registry"); - // gracefully degrade by returning false - return Task.FromResult(false); - } - - // reformat the GUID in the format used in the registry - var installerGuidAsString = productCodeAsGuid.ToString("B"); - - // NOTE: this code is largely duplicated in Morphic.Client; we should refactor it out into a helper library - - // x64 bit installer, per-machine based installation - // HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall - var subkey1Result = Morphic.WindowsNative.Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"); - if (subkey1Result.IsError == false) - { - var registryKey = subkey1Result.Value!; - var subKeyExistsResult = registryKey.SubKeyExists(installerGuidAsString); - if (subKeyExistsResult.IsSuccess == true && subKeyExistsResult.Value! == true) - { - return Task.FromResult(true); - } - } - - // x86 bit installer, per-machine based installation - // HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall - var subkey2Result = Morphic.WindowsNative.Registry.LocalMachine.OpenSubKey("SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"); - if (subkey2Result.IsError == false) - { - var registryKey = subkey2Result.Value!; - var subKeyExistsResult = registryKey.SubKeyExists(installerGuidAsString); - if (subKeyExistsResult.IsSuccess == true && subKeyExistsResult.Value! == true) - { - return Task.FromResult(true); - } - } - - // x64 bit installer, per-user based installation - // HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall - var subkey3Result = Morphic.WindowsNative.Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"); - if (subkey3Result.IsError == false) - { - var registryKey = subkey3Result.Value!; - var subKeyExistsResult = registryKey.SubKeyExists(installerGuidAsString); - if (subKeyExistsResult.IsSuccess == true && subKeyExistsResult.Value! == true) - { - return Task.FromResult(true); - } - } - - // x86 bit installer, per-user based installation - // HKCU\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall - var subkey4Result = Morphic.WindowsNative.Registry.CurrentUser.OpenSubKey("SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"); - if (subkey4Result.IsError == false) - { - var registryKey = subkey4Result.Value!; - var subKeyExistsResult = registryKey.SubKeyExists(installerGuidAsString); - if (subKeyExistsResult.IsSuccess == true && subKeyExistsResult.Value! == true) - { - return Task.FromResult(true); - } - } - - // if we reach here, we were unable to find proof that the application is installed on the system - return Task.FromResult(false); - } - } -} diff --git a/Morphic.Settings/SolutionsRegistry/Solutions.cs b/Morphic.Settings/SolutionsRegistry/Solutions.cs index 634d7a6e..6711f476 100644 --- a/Morphic.Settings/SolutionsRegistry/Solutions.cs +++ b/Morphic.Settings/SolutionsRegistry/Solutions.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.IO; - using System.Linq; using System.Threading.Tasks; using Morphic.Core; using Morphic.Core.Legacy; @@ -126,43 +125,6 @@ public async Task> CapturePreferencesAsy return success ? MorphicResult.OkResult() : MorphicResult.ErrorResult(); } - public List GetListOfAtSoftwareToInstall(Preferences preferences) - { - if (preferences.Default is null) - { - return new List(); - } - - var result = new List(); - foreach ((string solutionId, SolutionPreferences solutionPreferences) in preferences.Default) - { - if (this.All.TryGetValue(solutionId, out Solution? solution)) - { - foreach(SettingGroup settingGroup in solution.SettingGroups) - { - if (settingGroup is Morphic.Settings.SettingsHandlers.Install.InstalledApplicationSettingGroup) - { - foreach ((string settingId, object? value) in solutionPreferences.Values) - { - Setting? installedSetting; - if (settingGroup.TryGetSetting("installed", out installedSetting)) - { - var valueAsBool = value as bool?; - if (valueAsBool is not null && valueAsBool == true) - { - var applicationShortName = ((Morphic.Settings.SettingsHandlers.Install.InstalledApplicationSettingGroup)settingGroup).ShortName; - result.Add(applicationShortName); - } - } - } - } - } - } - } - - return result; - } - public async Task> ApplyPreferencesAsync(Preferences preferences, bool captureCurrent = false, bool async = false) { var success = true;