From e83b0022d281300f0ab77a5b82a4d02ab3cfba38 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Tue, 12 Dec 2023 16:15:42 +1000
Subject: [PATCH 01/43] feat(windows): WIP buc sm doesn't build just backup
background update state machine doesn't even build
---
.../windows/delphi/general/RegistryKeys.pas | 5 +
.../desktop/kmshell/main/BackgroundUpdate.pas | 1244 +++++++++++++++++
.../main/Keyman.System.DownloadUpdate.pas | 166 +++
.../main/Keyman.System.RemoteUpdateCheck.pas | 151 +-
4 files changed, 1458 insertions(+), 108 deletions(-)
create mode 100644 windows/src/desktop/kmshell/main/BackgroundUpdate.pas
create mode 100644 windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
diff --git a/common/windows/delphi/general/RegistryKeys.pas b/common/windows/delphi/general/RegistryKeys.pas
index 62599851cd2..7404a2af4fc 100644
--- a/common/windows/delphi/general/RegistryKeys.pas
+++ b/common/windows/delphi/general/RegistryKeys.pas
@@ -162,6 +162,7 @@ interface
SRegKey_KeymanDesktop_CU = SRegKey_KeymanDesktopRoot_CU;
SRegKey_KeymanDesktop_LM = SRegKey_KeymanDesktopRoot_LM;
+
{ Other Keyman Settings }
SRegValue_DeadkeyConversionMode = 'deadkey conversion mode'; // CU // I4552
@@ -177,6 +178,9 @@ interface
SRegValue_AvailableLanguages = 'available languages'; //CU
SRegValue_CurrentLanguage = 'current language'; //CU
+ SRegValue_Install_Update = 'install update';
+ SRegValue_Update_State = 'update state';
+
{ Privacy }
SRegValue_AutomaticallyReportErrors = 'automatically report errors'; // CU, SRegKey_IDEOptions and SRegKey_KeymanEngine_CU
@@ -297,6 +301,7 @@ interface
SRegKey_KeymanDeveloperRoot_LM = SRegKey_KeymanRoot_LM + '\Keyman Developer'; // LM CU
SRegKey_KeymanDeveloper_LM = SRegKey_KeymanDeveloperRoot_LM; // LM CU
+
SRegKey_IDE_CU = SRegKey_KeymanDeveloper_CU + '\IDE'; // CU
SRegKey_IDEDock_CU = SRegKey_IDE_CU + '\Dock'; // CU
SRegKey_IDEFiles_CU = SRegKey_IDE_CU + '\Files'; // CU
diff --git a/windows/src/desktop/kmshell/main/BackgroundUpdate.pas b/windows/src/desktop/kmshell/main/BackgroundUpdate.pas
new file mode 100644
index 00000000000..a8d4e72cab3
--- /dev/null
+++ b/windows/src/desktop/kmshell/main/BackgroundUpdate.pas
@@ -0,0 +1,1244 @@
+(*
+ Name: BackgroundUpdate
+ Copyright: Copyright (C) SIL International.
+ Documentation:
+ Description:
+ Create Date: 2 Nov 2023
+
+ Modified Date: 2 Nov 2023
+ Authors: rcruickshank
+ Related Files:
+ Dependencies:
+
+ Bugs:
+ Todo:
+ Notes: For the state diagram in mermaid ../BackgroundUpdateStateDiagram.md
+ History:
+*)
+unit BackgroundUpdate;
+
+interface
+
+uses
+ System.Classes,
+ System.SysUtils,
+ System.UITypes,
+ System.IOUtils,
+ System.Types,
+ Vcl.Forms,
+ TypInfo,
+ KeymanPaths,
+ utilkmshell,
+
+ httpuploader,
+ Keyman.System.UpdateCheckResponse,
+ UfrmDownloadProgress;
+
+type
+ EBackgroundUpdate = class(Exception);
+
+ TBackgroundUpdateResult = (oucUnknown, oucShutDown, oucSuccess, oucNoUpdates, oucUpdatesAvailable, oucFailure, oucOffline);
+
+ TUpdateState = (usIdle, usUpdateAvailable, usDownloading, usWaitingRestart, usInstalling, usRetry, usWaitingPostInstall);
+
+ { Keyboard Package Params }
+ TBackgroundUpdateParamsPackage = record
+ ID: string;
+ NewID: string;
+ Description: string;
+ OldVersion, NewVersion: string;
+ DownloadURL: string;
+ SavePath: string;
+ FileName: string;
+ DownloadSize: Integer;
+ Install: Boolean;
+ end;
+ { Main Keyman Program }
+ TBackgroundUpdateParamsKeyman = record
+ OldVersion, NewVersion: string;
+ DownloadURL: string;
+ SavePath: string;
+ FileName: string;
+ DownloadSize: Integer;
+ Install: Boolean;
+ end;
+
+ TBackgroundUpdateParams = record
+ Keyman: TBackgroundUpdateParamsKeyman;
+ Packages: array of TBackgroundUpdateParamsPackage;
+ Result: TBackgroundUpdateResult;
+ end;
+
+ TBackgroundUpdateDownloadParams = record
+ Owner: TfrmDownloadProgress;
+ TotalSize: Integer;
+ TotalDownloads: Integer;
+ StartPosition: Integer;
+ end;
+
+ // Forward declaration
+ TBackgroundUpdate = class;
+ { State Classes Update }
+
+ TStateClass = class of TState;
+
+ TState = class abstract
+ private
+ bucStateContext: TBackgroundUpdate;
+ procedure ChangeState(newState: TStateClass);
+
+ public
+ constructor Create(Context: TBackgroundUpdate);
+ procedure Enter; virtual; abstract;
+ procedure Exit; virtual; abstract;
+ procedure HandleCheck; virtual; abstract;
+ procedure HandleDownload; virtual; abstract;
+ function HandleKmShell : Integer; virtual; abstract;
+ procedure HandleInstall; virtual; abstract;
+ procedure HandleMSIInstallComplete; virtual; abstract;
+ procedure HandleAbort; virtual; abstract;
+
+ // For convenience
+ function StateName: string; virtual; abstract;
+
+ end;
+
+ // Derived classes for each state
+ IdleState = class(TState)
+ public
+ procedure Enter; override;
+ procedure Exit; override;
+ procedure HandleCheck; override;
+ procedure HandleDownload; override;
+ function HandleKmShell : Integer; override;
+ procedure HandleInstall; override;
+ procedure HandleMSIInstallComplete; override;
+ procedure HandleAbort; override;
+ function StateName: string; override;
+ end;
+
+ UpdateAvailableState = class(TState)
+ public
+ procedure Enter; override;
+ procedure Exit; override;
+ procedure HandleCheck; override;
+ procedure HandleDownload; override;
+ function HandleKmShell : Integer; override;
+ procedure HandleInstall; override;
+ procedure HandleMSIInstallComplete; override;
+ procedure HandleAbort; override;
+ function StateName: string; override;
+ end;
+
+ DownloadingState = class(TState)
+ private
+ { These function could become members of the state objects
+ or at the very least controlled by the state objects }
+
+ { TODO: make a comment clear when we are in a elevate process}
+
+ {
+ Performs updates download in the background, without displaying a GUI
+ progress bar. This function is similar to DownloadUpdates, but it runs in
+ the background.
+
+ @returns True if all updates were successfully downloaded, False if any
+ download failed.
+ }
+
+ function DownloadUpdatesBackground: Boolean;
+ {
+ Performs updates download in the background, without displaying a GUI
+ progress bar. This procedure is similar to DownloadUpdates, but it runs in
+ the background.
+
+ @params SavePath The path where the downloaded files will be saved.
+ Result A Boolean value indicating the overall result of the
+ download process.
+ }
+ procedure DoDownloadUpdatesBackground(SavePath: string; var Result: Boolean);
+ {
+ Performs an online update check, including package retrieval and version
+ query.
+
+ This function checks if a week has passed since the last update check. It
+ utilizes the kmcom API to retrieve the current packages. The function then
+ performs an HTTP request to query the remote versions of these packages.
+ The resulting information is stored in the FParams variable. Additionally,
+ the function handles the main Keyman install package.
+
+ @returns A TBackgroundUpdateResult indicating the result of the update
+ check.
+ }
+ // This is just for testing only.
+ procedure DoDownloadUpdatesBackgroundTest(SavePath: string; var Result: Boolean);
+ public
+ procedure Enter; override;
+ procedure Exit; override;
+ procedure HandleCheck; override;
+ procedure HandleDownload; override;
+ function HandleKmShell : Integer; override;
+ procedure HandleInstall; override;
+ procedure HandleMSIInstallComplete; override;
+ procedure HandleAbort; override;
+ function StateName: string; override;
+ end;
+
+ WaitingRestartState = class(TState)
+ public
+ procedure Enter; override;
+ procedure Exit; override;
+ procedure HandleCheck; override;
+ procedure HandleDownload; override;
+ function HandleKmShell : Integer; override;
+ procedure HandleInstall; override;
+ procedure HandleMSIInstallComplete; override;
+ procedure HandleAbort; override;
+ function StateName: string; override;
+ end;
+
+ InstallingState = class(TState)
+ private
+ procedure DoInstallKeyman; overload;
+ function DoInstallKeyman(SavePath: string) : Boolean; overload;
+ {
+ Installs the Keyman file using either msiexec.exe or the setup launched in
+ a separate shell.
+
+ @params Package The package to be installed.
+
+ @returns True if the installation is successful, False otherwise.
+ }
+ function DoInstallPackage(Package: TBackgroundUpdateParamsPackage): Boolean;
+ public
+ procedure Enter; override;
+ procedure Exit; override;
+ procedure HandleCheck; override;
+ procedure HandleDownload; override;
+ function HandleKmShell : Integer; override;
+ procedure HandleInstall; override;
+ procedure HandleMSIInstallComplete; override;
+ procedure HandleAbort; override;
+ function StateName: string; override;
+ end;
+
+ RetryState = class(TState)
+ public
+ procedure Enter; override;
+ procedure Exit; override;
+ procedure HandleCheck; override;
+ procedure HandleDownload; override;
+ function HandleKmShell : Integer; override;
+ procedure HandleInstall; override;
+ procedure HandleMSIInstallComplete; override;
+ procedure HandleAbort; override;
+ function StateName: string; override;
+ end;
+
+ WaitingPostInstallState = class(TState)
+ public
+ procedure Enter; override;
+ procedure Exit; override;
+ procedure HandleCheck; override;
+ procedure HandleDownload; override;
+ function HandleKmShell : Integer; override;
+ procedure HandleInstall; override;
+ procedure HandleMSIInstallComplete; override;
+ procedure HandleAbort; override;
+ function StateName: string; override;
+ end;
+
+ { This class also controls the state flow see }
+ TBackgroundUpdate = class
+ private
+ FOwner: TCustomForm;
+ FSilent: Boolean;
+ FForce: Boolean;
+ FAuto: Boolean;
+ FParams: TBackgroundUpdateParams;
+
+ FErrorMessage: string;
+
+ DownloadTempPath: string;
+
+ FShowErrors: Boolean;
+
+
+
+ FDownload: TBackgroundUpdateDownloadParams;
+
+ CurrentState: TState;
+ // State object for performance (could lazy create?)
+ FIdle: IdleState;
+ FUpdateAvailable: UpdateAvailableState;
+ FDownloading: DownloadingState;
+ FWaitingRestart: WaitingRestartState;
+ FInstalling: InstallingState;
+ FRetry: RetryState;
+ FWaitingPostInstall: WaitingPostInstallState;
+ function GetState: TStateClass;
+ procedure SetState(const Value: TStateClass);
+ function ConvertEnumState(const TEnumState: TUpdateState): TStateClass;
+
+ procedure ShutDown;
+
+ {
+ SavePackageUpgradesToDownloadTempPath saves any new package IDs to a
+ single file in the download tempPath. This procedure saves the IDs of any
+ new packages to a file named "upgrade_packages.inf" in the download
+ tempPath.
+ }
+ procedure SavePackageUpgradesToDownloadTempPath;
+ function IsKeymanRunning: Boolean;
+ function checkUpdateSchedule : Boolean;
+
+ function SetRegistryState (Update : TUpdateState): Boolean;
+
+ protected
+ property State: TStateClass read GetState write SetState;
+
+ public
+ constructor Create(AOwner: TCustomForm; AForce, ASilent: Boolean);
+ destructor Destroy; override;
+
+ procedure HandleCheck;
+ function HandleKmShell : Integer;
+ procedure HandleDownload;
+ procedure HandleInstall;
+ procedure HandleMSIInstallComplete;
+ procedure HandleAbort;
+ function CurrentStateName: string;
+
+ property ShowErrors: Boolean read FShowErrors write FShowErrors;
+ function CheckRegistryState : TUpdateState;
+
+ end;
+
+ IOnlineUpdateSharedData = interface
+ ['{7442A323-C1E3-404B-BEEA-5B24A52BBB0E}']
+ function Params: TBackgroundUpdateParams;
+ end;
+
+ TOnlineUpdateSharedData = class(TInterfacedObject, IOnlineUpdateSharedData)
+ private
+ FParams: TBackgroundUpdateParams;
+ public
+ constructor Create(AParams: TBackgroundUpdateParams);
+ function Params: TBackgroundUpdateParams;
+ end;
+
+implementation
+
+uses
+ Winapi.Shlobj,
+ System.WideStrUtils,
+ Vcl.Dialogs,
+ Winapi.ShellApi,
+ Winapi.Windows,
+ Winapi.WinINet,
+
+ GlobalProxySettings,
+ KLog,
+ keymanapi_TLB,
+ KeymanVersion,
+ kmint,
+ ErrorControlledRegistry,
+ RegistryKeys,
+ Upload_Settings,
+ utildir,
+ utilexecute,
+ OnlineUpdateCheckMessages, // todo create own messages
+ UfrmOnlineUpdateIcon,
+ UfrmOnlineUpdateNewVersion,
+ utilsystem,
+ utiluac,
+ versioninfo;
+
+const
+ SPackageUpgradeFilename = 'upgrade_packages.inf';
+ kmShellContinue = 0;
+ kmShellExit = 1;
+
+{ TBackgroundUpdate }
+
+constructor TBackgroundUpdate.Create(AOwner: TCustomForm; AForce, ASilent: Boolean);
+var TSerailsedState : TUpdateState;
+begin
+ inherited Create;
+
+ FOwner := AOwner;
+
+ FShowErrors := True;
+ FParams.Result := oucUnknown;
+
+ FSilent := ASilent;
+ FForce := AForce;
+ FAuto := True; // Default to automatically check, download, and install
+ FIdle := IdleState.Create(Self);
+ FUpdateAvailable := UpdateAvailableState.Create(Self);
+ FDownloading := DownloadingState.Create(Self);
+ FWaitingRestart := WaitingRestartState.Create(Self);
+ FInstalling := InstallingState.Create(Self);
+ FRetry := RetryState.Create(Self);
+ FWaitingPostInstall := WaitingPostInstallState.Create(Self);
+ // Check the Registry setting.
+ state := ConvertEnumState(CheckRegistryState);
+ KL.Log('TBackgroundUpdate.Create');
+end;
+
+destructor TBackgroundUpdate.Destroy;
+begin
+ if (FErrorMessage <> '') and not FSilent and FShowErrors then
+ ShowMessage(FErrorMessage);
+
+ if FParams.Result = oucShutDown then
+ ShutDown;
+
+ FIdle.Free;
+ FUpdateAvailable.Free;
+ FDownloading.Free;
+ FWaitingRestart.Free;
+ FInstalling.Free;
+ FRetry.Free;
+ FWaitingPostInstall.Free;
+
+ KL.Log('TBackgroundUpdate.Destroy: FErrorMessage = '+FErrorMessage);
+ KL.Log('TBackgroundUpdate.Destroy: FParams.Result = '+IntToStr(Ord(FParams.Result)));
+
+ inherited Destroy;
+end;
+
+
+procedure TBackgroundUpdate.SavePackageUpgradesToDownloadTempPath;
+var
+ i: Integer;
+begin
+ with TStringList.Create do
+ try
+ for i := 0 to High(FParams.Packages) do
+ if FParams.Packages[i].NewID <> '' then
+ Add(FParams.Packages[i].NewID+'='+FParams.Packages[i].ID);
+ if Count > 0 then
+ SaveToFile(DownloadTempPath + SPackageUpgradeFileName);
+ finally
+ Free;
+ end;
+end;
+
+procedure TBackgroundUpdate.ShutDown;
+begin
+ if Assigned(Application) then
+ Application.Terminate;
+end;
+
+
+{ TOnlineUpdateSharedData }
+
+constructor TOnlineUpdateSharedData.Create(AParams: TBackgroundUpdateParams);
+begin
+ inherited Create;
+ FParams := AParams;
+end;
+
+function TOnlineUpdateSharedData.Params: TBackgroundUpdateParams;
+begin
+ Result := FParams;
+end;
+
+
+function TBackgroundUpdate.SetRegistryState(Update : TUpdateState): Boolean;
+var
+ UpdateStr : string;
+begin
+
+ Result := False;
+ with TRegistryErrorControlled.Create do
+ try
+ RootKey := HKEY_LOCAL_MACHINE;
+ KL.Log('SetRegistryState State Entry');
+ if OpenKey(SRegKey_KeymanEngine_LM, True) then
+ begin
+ UpdateStr := GetEnumName(TypeInfo(TUpdateState), Ord(Update));
+ WriteString(SRegValue_Update_State, UpdateStr);
+ KL.Log('SetRegistryState State is:[' + UpdateStr + ']');
+ end;
+ Result := True;
+ finally
+ Free;
+ end;
+
+end;
+
+
+function TBackgroundUpdate.CheckRegistryState : TUpdateState; // I2329
+var
+ UpdateState : TUpdateState;
+
+begin
+ // We will use a registry flag to maintain the state of the background update
+
+ UpdateState := usIdle; // do we need a unknown state ?
+ // check the registry value
+ with TRegistryErrorControlled.Create do // I2890
+ try
+ RootKey := HKEY_LOCAL_MACHINE;
+ if OpenKeyReadOnly(SRegKey_KeymanEngine_LM) and ValueExists(SRegValue_Update_State) then
+ begin
+ UpdateState := TUpdateState(GetEnumValue(TypeInfo(TUpdateState), ReadString(SRegValue_Update_State)));
+ KL.Log('CheckRegistryState State is:[' + ReadString(SRegValue_Update_State) + ']');
+ end
+ else
+ begin
+ UpdateState := usIdle; // do we need a unknown state ?
+ KL.Log('CheckRegistryState State reg value not found default:[' + ReadString(SRegValue_Update_State) + ']');
+ end
+ finally
+ Free;
+ end;
+ Result := UpdateState;
+end;
+
+
+
+function TBackgroundUpdate.IsKeymanRunning: Boolean; // I2329
+begin
+ try
+ Result := kmcom.Control.IsKeymanRunning;
+ except
+ on E:Exception do
+ begin
+ KL.Log(E.Message);
+ Exit(False);
+ end;
+ end;
+end;
+
+function TBackgroundUpdate.CheckUpdateSchedule: Boolean;
+begin
+ try
+ Result := False;
+ with TRegistryErrorControlled.Create do
+ try
+ if OpenKeyReadOnly(SRegKey_KeymanDesktop_CU) then
+ begin
+ if ValueExists(SRegValue_CheckForUpdates) and not ReadBool(SRegValue_CheckForUpdates) and not FForce then
+ begin
+ Result := False;
+ Exit;
+ end;
+ if ValueExists(SRegValue_LastUpdateCheckTime) and (Now - ReadDateTime(SRegValue_LastUpdateCheckTime) < 1) and not FForce then
+ begin
+ Result := False;
+ Exit;
+ end;
+ // Else Time to check for updates
+ Result := True;
+ end;
+ finally
+ Free;
+ end;
+ except
+ { we will not run the check if an error occurs reading the settings }
+ on E:Exception do
+ begin
+ Result := False;
+ FErrorMessage := E.Message;
+ Exit;
+ end;
+ end;
+end;
+
+function TBackgroundUpdate.GetState: TStateClass;
+begin
+ Result := TStateClass(CurrentState.ClassType);
+end;
+
+procedure TBackgroundUpdate.SetState(const Value: TStateClass);
+begin
+ if Assigned(CurrentState) then
+ begin
+ CurrentState.Exit;
+ end;
+
+ if Value = IdleState then
+ begin
+ CurrentState := FIdle;
+ end
+ else if Value = UpdateAvailableState then
+ begin
+ CurrentState := FUpdateAvailable;
+ end
+ else if Value = DownloadingState then
+ begin
+ CurrentState := FDownloading;
+ end
+ else if Value = WaitingRestartState then
+ begin
+ CurrentState := FWaitingRestart;
+ end
+ else if Value = InstallingState then
+ begin
+ CurrentState := FInstalling;
+ end
+ else if Value = RetryState then
+ begin
+ CurrentState := FRetry;
+ end
+ else if Value = WaitingPostInstallState then
+ begin
+ CurrentState := FWaitingPostInstall;
+ end;
+
+ if Assigned(CurrentState) then
+ begin
+ CurrentState.Enter;
+ end
+ else
+ begin
+ // TODO: Unable to set state for Value []
+ end;
+
+end;
+
+function TBackgroundUpdate.ConvertEnumState(const TEnumState: TUpdateState) : TStateClass;
+begin
+ case TEnumState of
+ usIdle: Result := IdleState;
+ usUpdateAvailable: Result := UpdateAvailableState;
+ usDownloading: Result := DownloadingState;
+ usWaitingRestart: Result := WaitingRestartState;
+ usInstalling: Result := InstallingState;
+ usRetry: Result := RetryState;
+ usWaitingPostInstall: Result := WaitingPostInstallState;
+ else
+ // Log error unknown state setting to idle
+ Result := IdleState;
+ end;
+end;
+
+procedure TBackgroundUpdate.HandleCheck;
+begin
+ CurrentState.HandleCheck;
+end;
+
+function TBackgroundUpdate.HandleKmShell;
+begin
+ Result := CurrentState.HandleKmShell;
+end;
+
+procedure TBackgroundUpdate.HandleDownload;
+begin
+ CurrentState.HandleDownload;
+end;
+
+procedure TBackgroundUpdate.HandleInstall;
+begin
+ CurrentState.HandleInstall;
+end;
+
+procedure TBackgroundUpdate.HandleMSIInstallComplete;
+begin
+ CurrentState.HandleMSIInstallComplete;
+end;
+
+procedure TBackgroundUpdate.HandleAbort;
+begin
+ CurrentState.HandleAbort;
+end;
+
+function TBackgroundUpdate.CurrentStateName: string;
+begin
+ // Implement your logic here
+ Result := CurrentState.StateName;
+end;
+
+
+
+{ State Class Memebers }
+constructor TState.Create(Context: TBackgroundUpdate);
+begin
+ bucStateContext := Context;
+end;
+
+procedure TState.ChangeState(NewState: TStateClass);
+begin
+ bucStateContext.State := NewState;
+end;
+
+
+{ IdleState }
+
+procedure IdleState.Enter;
+begin
+ // Enter UpdateAvailableState
+ // register name
+ bucStateContext.SetRegistryState(usIdle);
+end;
+
+procedure IdleState.Exit;
+begin
+ // Exit UpdateAvailableState
+end;
+
+procedure IdleState.HandleCheck;
+begin
+ { TODO: Verify that it has been at least 7 days since last update check -
+ only if FSilent = TRUE }
+
+ { Make a HTTP request out and see if updates are available for now do
+ this all in the Idle HandleCheck message. But could be broken into an
+ seperate state of WaitngCheck RESP }
+ { if Response not OK stay in the idle state and return }
+
+ { Response OK and Update is available }
+ ChangeState(UpdateAvailableState);
+
+end;
+
+procedure IdleState.HandleDownload;
+begin
+ // Implement your logic here
+end;
+
+function IdleState.HandleKmShell;
+begin
+ // Implement your logic here
+ Result := kmShellContinue;
+end;
+
+procedure IdleState.HandleInstall;
+begin
+ // Implement your logic here
+end;
+
+procedure IdleState.HandleMSIInstallComplete;
+begin
+ // Implement your logic here
+end;
+
+procedure IdleState.HandleAbort;
+begin
+ // Implement your logic here
+end;
+
+function IdleState.StateName;
+begin
+ // Implement your logic here
+ Result := 'IdleState';
+end;
+
+{ UpdateAvailableState }
+
+procedure UpdateAvailableState.Enter;
+begin
+ // Enter UpdateAvailableState
+ bucStateContext.SetRegistryState(usUpdateAvailable);
+ if bucStateContext.FAuto then
+ begin
+ bucStateContext.CurrentState.HandleDownload ;
+ end;
+end;
+
+procedure UpdateAvailableState.Exit;
+begin
+ // Exit UpdateAvailableState
+end;
+
+procedure UpdateAvailableState.HandleCheck;
+begin
+ // Implement your logic here
+end;
+
+procedure UpdateAvailableState.HandleDownload;
+begin
+ ChangeState(DownloadingState);
+end;
+
+function UpdateAvailableState.HandleKmShell;
+begin
+ if bucStateContext.FAuto then
+ begin
+ bucStateContext.CurrentState.HandleDownload ;
+ end;
+ Result := kmShellContinue;
+end;
+
+procedure UpdateAvailableState.HandleInstall;
+begin
+ // Implement your logic here
+end;
+
+procedure UpdateAvailableState.HandleMSIInstallComplete;
+begin
+ // Implement your logic here
+end;
+
+procedure UpdateAvailableState.HandleAbort;
+begin
+ // Implement your logic here
+end;
+
+function UpdateAvailableState.StateName;
+begin
+ // Implement your logic here
+ Result := 'UpdateAvailableState';
+end;
+
+{ DownloadingState }
+
+procedure DownloadingState.Enter;
+var DownloadResult : Boolean;
+begin
+ // Enter DownloadingState
+ bucStateContext.SetRegistryState(usDownloading);
+ DownloadResult := DownloadUpdatesBackground;
+ if DownloadResult then
+ begin
+ if bucStateContext.IsKeymanRunning then
+ ChangeState(WaitingRestartState)
+ else
+ ChangeState(InstallingState);
+ end
+ else
+ begin
+ ChangeState(RetryState);
+ end
+end;
+
+procedure DownloadingState.Exit;
+begin
+ // Exit DownloadingState
+end;
+
+procedure DownloadingState.HandleCheck;
+begin
+ // For now just pretend updated found
+end;
+
+procedure DownloadingState.HandleDownload;
+var DownloadResult : Boolean;
+begin
+ // We are already downloading do nothing
+end;
+
+function DownloadingState.HandleKmShell;
+var DownloadResult : Boolean;
+begin
+ DownloadResult := DownloadUpdatesBackground;
+ // TODO check if keyman is running then send to Waiting Restart
+ if DownloadResult then
+ begin
+ ChangeState(InstallingState);
+ end
+ else
+ begin
+ ChangeState(RetryState);
+ end;
+ Result := kmShellContinue;
+end;
+
+procedure DownloadingState.HandleInstall;
+begin
+ // Implement your logic here
+ ChangeState(InstallingState);
+end;
+
+procedure DownloadingState.HandleMSIInstallComplete;
+begin
+ // Implement your logic here
+end;
+
+procedure DownloadingState.HandleAbort;
+begin
+ // Implement your logic here
+end;
+
+function DownloadingState.StateName;
+begin
+ // Implement your logic here
+ Result := 'DownloadingState';
+end;
+
+procedure DownloadingState.DoDownloadUpdatesBackground(SavePath: string; var Result: Boolean);
+begin
+end;
+
+// Test installing only
+procedure DownloadingState.DoDownloadUpdatesBackgroundTest(SavePath: string; var Result: Boolean);
+var
+ i, downloadCount: Integer;
+ UpdateDir : string;
+
+begin
+ try
+ Result := False;
+
+ UpdateDir := 'C:\Projects\rcswag\testCache';
+ KL.Log('DoDownloadUpdatesBackgroundTest SavePath:'+ SavePath);
+ // Check if the update source directory exists
+ if DirectoryExists(UpdateDir) then
+ begin
+ // Create the update cached directory if it doesn't exist
+ if not DirectoryExists(SavePath) then
+ ForceDirectories(SavePath);
+
+ // Copy all files from the updatedir to savepath
+ TDirectory.Copy(UpdateDir, SavePath);
+ Result:= True;
+ KL.Log('All files copied successfully.');
+ end
+ else
+ KL.Log('Source directory does not exist.');
+ except
+ on E: Exception do
+ KL.Log('Error: ' + E.Message);
+ end;
+
+end;
+
+
+function DownloadingState.DownloadUpdatesBackground: Boolean;
+var
+ i: Integer;
+ DownloadBackGroundSavePath : String;
+ DownloadResult : Boolean;
+begin
+ //DownloadTempPath := IncludeTrailingPathDelimiter(CreateTempPath);
+ DownloadBackGroundSavePath := IncludeTrailingPathDelimiter(GetFolderPath(CSIDL_COMMON_APPDATA) + SFolder_CachedUpdateFiles);
+ //DownloadBackGroundSavePath := DownloadBackGroundSavePath + 'RCTest.txt';
+ { For now lets download all the updates. We need to take these from the user via check box form }
+
+ if bucStateContext.FParams.Keyman.DownloadURL <> '' then
+ bucStateContext.FParams.Keyman.Install := True;
+
+ for i := 0 to High(bucStateContext.FParams.Packages) do
+ bucStateContext.FParams.Packages[i].Install := True;
+
+ // Download files
+ // DoDownloadUpdatesBackground(DownloadBackGroundSavePath, DownloadResult);
+ // For development by passing the DoDownloadUpdatesBackground and just add
+ // the cached file on my local disk as a simulation
+ DoDownloadUpdatesBackgroundTest(DownloadBackGroundSavePath, DownloadResult);
+
+ KL.Log('TBackgroundUpdate.DownloadUpdatesBackground: DownloadResult = '+IntToStr(Ord(DownloadResult)));
+ //ShowMessage('TBackgroundUpdate.DownloadUpdatesBackground: DownloadResult = '+IntToStr(Ord(DownloadResult)));
+ Result := DownloadResult;
+end;
+
+{ WaitingRestartState }
+
+procedure WaitingRestartState.Enter;
+begin
+ // Enter DownloadingState
+ bucStateContext.SetRegistryState(usWaitingRestart);
+end;
+
+procedure WaitingRestartState.Exit;
+begin
+ // Exit DownloadingState
+end;
+
+procedure WaitingRestartState.HandleCheck;
+begin
+ // Implement your logic here
+end;
+
+procedure WaitingRestartState.HandleDownload;
+begin
+ // Implement your logic here
+end;
+
+function WaitingRestartState.HandleKmShell;
+begin
+ // Check downloaded cache if available then
+ // change state intalling
+ // else then change to idle and handle checkupdates state
+ // if bucStateContext.IsKeymanRunning then
+ // stay as waiting
+ // log this is unexpected
+ //ChangeState(WaitingRestartState)
+ // else
+ //
+
+ ChangeState(UpdateAvailableState);
+ Result := kmShellExit;
+end;
+
+procedure WaitingRestartState.HandleInstall;
+begin
+ // Implement your logic here
+end;
+
+procedure WaitingRestartState.HandleMSIInstallComplete;
+begin
+ // Implement your logic here
+end;
+
+procedure WaitingRestartState.HandleAbort;
+begin
+ // Implement your logic here
+end;
+
+function WaitingRestartState.StateName;
+begin
+ // Implement your logic here
+ Result := 'WaitingRestartState';
+end;
+
+{ InstallingState }
+
+function InstallingState.DoInstallPackage(Package: TBackgroundUpdateParamsPackage): Boolean;
+var
+ FPackage: IKeymanPackageFile2;
+begin
+ Result := True;
+
+ FPackage := kmcom.Packages.GetPackageFromFile(Package.SavePath) as IKeymanPackageFile2;
+ FPackage.Install2(True); // Force overwrites existing package and leaves most settings for it intact
+ FPackage := nil;
+
+ kmcom.Refresh;
+ kmcom.Apply;
+ System.SysUtils.DeleteFile(Package.SavePath);
+end;
+
+procedure InstallingState.DoInstallKeyman;
+var
+ s: string;
+ FResult: Boolean;
+begin
+ s := LowerCase(ExtractFileExt(bucStateContext.FParams.Keyman.SavePath));
+ if s = '.msi' then
+ FResult := TUtilExecute.Shell(0, 'msiexec.exe', '', '/qb /i "'+bucStateContext.FParams.Keyman.SavePath+'" AUTOLAUNCHPRODUCT=1') // I3349
+ else if s = '.exe' then
+ FResult := TUtilExecute.Shell(0, bucStateContext.FParams.Keyman.SavePath, '', '-au') // I3349
+ else
+ Exit;
+ if not FResult then
+ ShowMessage(SysErrorMessage(GetLastError));
+end;
+
+function InstallingState.DoInstallKeyman(SavePath: string) : Boolean;
+var
+ s: string;
+ FResult: Boolean;
+begin
+ s := LowerCase(ExtractFileExt(SavePath));
+ if s = '.msi' then
+ FResult := TUtilExecute.Shell(0, 'msiexec.exe', '', '/qb /i "'+SavePath+'" AUTOLAUNCHPRODUCT=1') // I3349
+ else if s = '.exe' then
+ FResult := TUtilExecute.Shell(0, SavePath, '', '-au') // I3349
+ else
+ Exit;
+ //Exit(False);
+
+ if not FResult then
+ begin
+ KL.Log('TBackgroundUpdate.InstallingState.DoInstall: Result = '+IntToStr(Ord(FResult)));
+ // Log messageShowMessage(SysErrorMessage(GetLastError));
+ end;
+
+ Result := FResult;
+end;
+
+procedure InstallingState.Enter;
+var
+ SavePath: String;
+ fileExt : String;
+ fileName: String;
+ fileNames: TStringDynArray;
+begin
+ bucStateContext.SetRegistryState(usInstalling);
+ // Needs to be desing discusion about the correct location for the cache
+ //SavePath := IncludeTrailingPathDelimiter(GetFolderPath(CSIDL_COMMON_APPDATA) + SFolder_CachedUpdateFiles);
+ // For testing
+ SavePath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
+
+ GetFileNamesInDirectory(SavePath, fileNames);
+ // for now we only want the exe although excute install can
+ // handle msi
+ for fileName in fileNames do
+ begin
+ fileExt := LowerCase(ExtractFileExt(fileName));
+ if fileExt = '.exe' then
+ break;
+ end;
+ // ExecuteInstall(SavePath + ExtractFileName(fileName));
+ // TODO DoInstallPackages ( this may need to be as state in the enum seperate
+ // to installing the main keyman executable.
+ if DoInstallKeyman(SavePath + ExtractFileName(fileName)) then
+ begin
+ KL.Log('TBackgroundUpdate.InstallingState.Enter: DoInstall OK');
+ end
+ else
+ begin
+ // TODO: clean failed download
+ // TODO: Do we do a retry on install? probably not
+ // install error log the error.
+ KL.Log('TBackgroundUpdate.InstallingState.Enter: DoInstall fail');
+ ChangeState(IdleState);
+ end
+end;
+
+procedure InstallingState.Exit;
+begin
+ // Exit DownloadingState
+end;
+
+procedure InstallingState.HandleCheck;
+begin
+ // Implement your logic here
+end;
+
+procedure InstallingState.HandleDownload;
+begin
+ // Implement your logic here
+end;
+
+function InstallingState.HandleKmShell;
+begin
+ // Result = exit straight away as we are installing (MSI installer)
+ Result := kmShellExit;
+end;
+
+procedure InstallingState.HandleInstall;
+begin
+ // Implement your logic here
+end;
+
+procedure InstallingState.HandleMSIInstallComplete;
+begin
+ // Implement your logic here
+end;
+
+procedure InstallingState.HandleAbort;
+begin
+ ChangeState(IdleState);
+end;
+
+function InstallingState.StateName;
+begin
+ // Implement your logic here
+ Result := 'InstallingState';
+end;
+
+{ RetryState }
+
+procedure RetryState.Enter;
+begin
+ // Enter DownloadingState
+ bucStateContext.SetRegistryState(usRetry);
+end;
+
+procedure RetryState.Exit;
+begin
+ // Exit DownloadingState
+end;
+
+procedure RetryState.HandleCheck;
+begin
+ // Implement your logic here
+end;
+
+procedure RetryState.HandleDownload;
+begin
+ // Implement your logic here
+end;
+
+function RetryState.HandleKmShell;
+begin
+ // TODO Implement retry
+ Result := kmShellContinue
+end;
+
+procedure RetryState.HandleInstall;
+begin
+ // Implement your logic here
+end;
+
+procedure RetryState.HandleMSIInstallComplete;
+begin
+ // Implement your logic here
+end;
+
+procedure RetryState.HandleAbort;
+begin
+ // Implement your logic here
+end;
+
+function RetryState.StateName;
+begin
+ // Implement your logic here
+ Result := 'RetryState';
+end;
+
+{ WaitingPostInstallState }
+
+procedure WaitingPostInstallState.Enter;
+begin
+ // Enter downloading state
+ bucStateContext.SetRegistryState(usWaitingPostInstall);
+end;
+
+procedure WaitingPostInstallState.Exit;
+begin
+ // Exit downloading state
+end;
+
+procedure WaitingPostInstallState.HandleCheck;
+begin
+ // Handle Check
+end;
+
+procedure WaitingPostInstallState.HandleDownload;
+begin
+ // Handle Download
+end;
+
+function WaitingPostInstallState.HandleKmShell;
+begin
+ // TODO maybe have a counter if we get called in this state
+ // to many time we need
+ HandleMSIInstallComplete;
+ Result := kmShellContinue;
+end;
+
+procedure WaitingPostInstallState.HandleInstall;
+begin
+ // Handle Install
+end;
+
+procedure WaitingPostInstallState.HandleMSIInstallComplete;
+var SavePath: string;
+ FileName: String;
+ FileNames: TStringDynArray;
+begin
+ KL.Log('WaitingPostInstallState.HandleMSIInstallComplete');
+ // TODO Remove cached files. Do any loging updating of files etc and then set back to idle
+ SavePath := IncludeTrailingPathDelimiter(GetFolderPath(CSIDL_COMMON_APPDATA) + SFolder_CachedUpdateFiles);
+ /// For testing using local user area cache
+ SavePath := 'C:\Projects\rcswag\testCache';
+ KL.Log('WaitingPostInstallState.HandleMSIInstallComplete remove SavePath:'+ SavePath);
+
+ GetFileNamesInDirectory(SavePath, FileNames);
+ for FileName in FileNames do
+ begin
+ System.SysUtils.DeleteFile(FileName);
+ end;
+ ChangeState(IdleState);
+end;
+
+procedure WaitingPostInstallState.HandleAbort;
+begin
+ // Handle Abort
+end;
+
+function WaitingPostInstallState.StateName;
+begin
+ // Implement your logic here
+ Result := 'WaitingPostInstallState';
+end;
+
+
+
+end.
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
new file mode 100644
index 00000000000..ccdcffa2bd3
--- /dev/null
+++ b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
@@ -0,0 +1,166 @@
+(*
+ Name: WebUpdateCheck
+ Copyright: Copyright (C) SIL International.
+ Documentation:
+ Description:
+ Create Date: 5 Dec 2023
+
+ Modified Date:
+ Authors: rcruickshank
+ Related Files:
+ Dependencies:
+
+ Bugs:
+ Todo:
+ Notes:
+ History:
+*)
+
+unit Keyman.System.DownloadUpdate;
+
+interface
+uses
+ System.Classes,
+ System.SysUtils,
+ KeymanPaths,
+ httpuploader,
+ Keyman.System.UpdateCheckResponse,
+ OnlineUpdateCheck;
+
+const
+ CheckPeriod: Integer = 7; // Days between checking for updates
+
+type
+ TRemoteUpdateCheckDownloadParams = record
+ TotalSize: Integer;
+ TotalDownloads: Integer;
+ StartPosition: Integer;
+ end;
+
+ TDownloadUpdate = class
+ private
+
+ FShowErrors: Boolean;
+ FDownload: TDownloadUpdateDownloadParams;
+ FCheckOnly: Boolean;
+
+ function DownloadUpdates(Params: TUpdateCheckResponse) : Boolean;
+ procedure DoDownloadUpdates(SavePath: string; Params: TUpdateCheckResponse; var Result: Boolean);
+
+ public
+
+ constructor Create(AForce : Boolean; ACheckOnly: Boolean = False);
+ destructor Destroy; override;
+ property ShowErrors: Boolean read FShowErrors write FShowErrors;
+ end;
+
+implementation
+
+procedure TDownloadUpdate.DoDownloadUpdates(SavePath: string; Params: TUpdateCheckResponse; var Result: Boolean);
+var
+ i, downloadCount: Integer;
+
+ function DownloadFile(const url, savepath: string): Boolean;
+ begin
+ with THttpUploader.Create(nil) do
+ try
+ Proxy.Server := GetProxySettings.Server;
+ Proxy.Port := GetProxySettings.Port;
+ Proxy.Username := GetProxySettings.Username;
+ Proxy.Password := GetProxySettings.Password;
+ Request.Agent := API_UserAgent;
+
+ Request.SetURL(url);
+ Upload;
+ if Response.StatusCode = 200 then
+ begin
+ with TFileStream.Create(savepath, fmCreate) do
+ try
+ Write(Response.PMessageBody^, Response.MessageBodyLength);
+ finally
+ Free;
+ end;
+ Result := True;
+ end
+ else // I2742
+ // If it fails we set to false but will try the other files
+ Result := False;
+ Exit;
+ finally
+ Free;
+ end;
+ end;
+
+
+begin
+ Result := False;
+ try
+ FDownload.TotalSize := 0;
+ FDownload.TotalDownloads := 0;
+ downloadCount := 0;
+
+ // Keyboard Packages
+ for i := 0 to High(Params.Packages) do
+ begin
+ Inc(FDownload.TotalDownloads);
+ Inc(FDownload.TotalSize, Params.Packages[i].DownloadSize);
+ Params.Packages[i].SavePath := SavePath + Params.Packages[i].FileName;
+ end;
+
+ // Add the Keyman installer
+ Inc(FDownload.TotalDownloads);
+ Inc(FDownload.TotalSize, Params.InstallSize);
+
+ // Keyboard Packages
+ FDownload.StartPosition := 0;
+ for i := 0 to High(Params.Packages) do
+ begin
+ if not DownloadFile(Params.Packages[i].DownloadURL, Params.Packages[i].SavePath) then // I2742
+ begin
+ Params.Packages[i].Install := False; // Download failed but install other files
+ end
+ else
+ Inc(downloadCount);
+ FDownload.StartPosition := FDownload.StartPosition + Params.Packages[i].DownloadSize;
+ end;
+
+ // Keyamn Installer
+ if not DownloadFile(Params.InstallURL, SavePath + Params.FileName) then // I2742
+ begin
+ // TODO record fail? and log // Download failed but user wants to install other files
+ end
+ else
+ begin
+ Inc(downloadCount)
+ end;
+
+ // There needs to be at least one file successfully downloaded to return
+ // TRUE that files where downloaded
+ if downloadCount > 0 then
+ Result := True;
+ except
+ on E:EHTTPUploader do
+ begin
+ if (E.ErrorCode = 12007) or (E.ErrorCode = 12029)
+ then LogMessage(S_OnlineUpdate_UnableToContact)
+ else LogMessage(WideFormat(S_OnlineUpdate_UnableToContact_Error, [E.Message]));
+ Result := False;
+ end;
+ end;
+end;
+
+function TDownloadUpdate.DownloadUpdates(Params: TUpdateCheckResponse): Boolean;
+var
+ DownloadBackGroundSavePath : String;
+ DownloadResult : Boolean;
+begin
+ DownloadBackGroundSavePath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
+
+ DoDownloadUpdates(DownloadBackGroundSavePath, Params, DownloadResult);
+ KL.Log('TDownloadUpdate.DownloadUpdatesBackground: DownloadResult = '+IntToStr(Ord(DownloadResult)));
+ Result := DownloadResult;
+
+end;
+
+
+end.
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas b/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas
index fc40f584cda..fb2dcc3ddf4 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas
@@ -18,7 +18,6 @@
unit Keyman.System.RemoteUpdateCheck; // I3306
interface
-
uses
System.Classes,
System.SysUtils,
@@ -27,6 +26,9 @@ interface
Keyman.System.UpdateCheckResponse,
OnlineUpdateCheck;
+const
+ CheckPeriod: Integer = 7; // Days between checking for updates
+
type
ERemoteUpdateCheck = class(Exception);
@@ -61,6 +63,7 @@ TRemoteUpdateCheck = class
end;
procedure LogMessage(LogMessage: string);
+function CheckForUpdates: Boolean;
implementation
@@ -122,111 +125,6 @@ function TRemoteUpdateCheck.Run: TRemoteUpdateCheckResult;
end;
-procedure TRemoteUpdateCheck.DoDownloadUpdates(SavePath: string; Params: TUpdateCheckResponse; var Result: Boolean);
-var
- i, downloadCount: Integer;
-
- function DownloadFile(const url, savepath: string): Boolean;
- begin
- with THttpUploader.Create(nil) do
- try
- Proxy.Server := GetProxySettings.Server;
- Proxy.Port := GetProxySettings.Port;
- Proxy.Username := GetProxySettings.Username;
- Proxy.Password := GetProxySettings.Password;
- Request.Agent := API_UserAgent;
-
- Request.SetURL(url);
- Upload;
- if Response.StatusCode = 200 then
- begin
- with TFileStream.Create(savepath, fmCreate) do
- try
- Write(Response.PMessageBody^, Response.MessageBodyLength);
- finally
- Free;
- end;
- Result := True;
- end
- else // I2742
- // If it fails we set to false but will try the other files
- Result := False;
- Exit;
- finally
- Free;
- end;
- end;
-
-
-begin
- Result := False;
- try
- FDownload.TotalSize := 0;
- FDownload.TotalDownloads := 0;
- downloadCount := 0;
-
- // Keyboard Packages
- for i := 0 to High(Params.Packages) do
- begin
- Inc(FDownload.TotalDownloads);
- Inc(FDownload.TotalSize, Params.Packages[i].DownloadSize);
- Params.Packages[i].SavePath := SavePath + Params.Packages[i].FileName;
- end;
-
- // Add the Keyman installer
- Inc(FDownload.TotalDownloads);
- Inc(FDownload.TotalSize, Params.InstallSize);
-
- // Keyboard Packages
- FDownload.StartPosition := 0;
- for i := 0 to High(Params.Packages) do
- begin
- if not DownloadFile(Params.Packages[i].DownloadURL, Params.Packages[i].SavePath) then // I2742
- begin
- Params.Packages[i].Install := False; // Download failed but install other files
- end
- else
- Inc(downloadCount);
- FDownload.StartPosition := FDownload.StartPosition + Params.Packages[i].DownloadSize;
- end;
-
- // Keyamn Installer
- if not DownloadFile(Params.InstallURL, SavePath + Params.FileName) then // I2742
- begin
- // TODO record fail? and log // Download failed but user wants to install other files
- end
- else
- begin
- Inc(downloadCount)
- end;
-
- // There needs to be at least one file successfully downloaded to return
- // TRUE that files where downloaded
- if downloadCount > 0 then
- Result := True;
- except
- on E:EHTTPUploader do
- begin
- if (E.ErrorCode = 12007) or (E.ErrorCode = 12029)
- then LogMessage(S_OnlineUpdate_UnableToContact)
- else LogMessage(WideFormat(S_OnlineUpdate_UnableToContact_Error, [E.Message]));
- Result := False;
- end;
- end;
-end;
-
-function TRemoteUpdateCheck.DownloadUpdates(Params: TUpdateCheckResponse): Boolean;
-var
- DownloadBackGroundSavePath : String;
- DownloadResult : Boolean;
-begin
- DownloadBackGroundSavePath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
-
- DoDownloadUpdates(DownloadBackGroundSavePath, Params, DownloadResult);
- KL.Log('TRemoteUpdateCheck.DownloadUpdatesBackground: DownloadResult = '+IntToStr(Ord(DownloadResult)));
- Result := DownloadResult;
-
-end;
function TRemoteUpdateCheck.DoRun: TRemoteUpdateCheckResult;
var
@@ -246,7 +144,7 @@ function TRemoteUpdateCheck.DoRun: TRemoteUpdateCheckResult;
Exit;
end;
- { Verify that it has been at least 7 days since last update check }
+ { Verify that it has been at least CheckPeriod days since last update check }
try
with TRegistryErrorControlled.Create do // I2890
try
@@ -257,7 +155,7 @@ function TRemoteUpdateCheck.DoRun: TRemoteUpdateCheckResult;
Result := wucNoUpdates;
Exit;
end;
- if ValueExists(SRegValue_LastUpdateCheckTime) and (Now - ReadDateTime(SRegValue_LastUpdateCheckTime) < 7) and not FForce then
+ if ValueExists(SRegValue_LastUpdateCheckTime) and (Now - ReadDateTime(SRegValue_LastUpdateCheckTime) < CheckPeriod) and not FForce then
begin
Result := wucNoUpdates;
// TODO: This exit is just to remove the time check for testing.
@@ -383,4 +281,41 @@ function TRemoteUpdateCheck.DoRun: TRemoteUpdateCheckResult;
KL.Log(LogMessage);
end;
+function CheckForUpdates: Boolean;
+begin
+{ Verify that it has been at least CheckPeriod days since last update check }
+ try
+ with TRegistryErrorControlled.Create do // I2890
+ try
+ if OpenKeyReadOnly(SRegKey_KeymanDesktop_CU) then
+ begin
+ if ValueExists(SRegValue_CheckForUpdates) and not ReadBool(SRegValue_CheckForUpdates) then
+ begin
+ Result := False;
+ Exit;
+ end;
+ if ValueExists(SRegValue_LastUpdateCheckTime) and (Now - ReadDateTime(SRegValue_LastUpdateCheckTime) > CheckPeriod) then
+ begin
+ Result := True;
+ end
+ else
+ begin
+ Result := False;
+ end;
+ Exit;
+ end;
+ finally
+ Free;
+ end;
+ except
+ { we will not run the check if an error occurs reading the settings }
+ on E:Exception do
+ begin
+ Result := False;
+ LogMessage(E.Message);
+ Exit;
+ end;
+ end;
+end;
+
end.
From 947b8299cec68201645975aabbc8743dd0752c00 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Wed, 13 Dec 2023 15:36:42 +1000
Subject: [PATCH 02/43] feat(windows): WIP
---
windows/src/desktop/kmshell/kmshell.dpr | 4 +++-
windows/src/desktop/kmshell/kmshell.dproj | 14 ++++++++------
.../kmshell/main/Keyman.System.DownloadUpdate.pas | 4 ++--
3 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/windows/src/desktop/kmshell/kmshell.dpr b/windows/src/desktop/kmshell/kmshell.dpr
index 71e434af915..c3a2312f6b2 100644
--- a/windows/src/desktop/kmshell/kmshell.dpr
+++ b/windows/src/desktop/kmshell/kmshell.dpr
@@ -179,7 +179,9 @@ uses
Keyman.System.AndroidStringToKeymanLocaleString in '..\..\..\..\common\windows\delphi\general\Keyman.System.AndroidStringToKeymanLocaleString.pas',
UpdateXMLRenderer in 'render\UpdateXMLRenderer.pas',
Keyman.System.UpdateCheckStorage in 'main\Keyman.System.UpdateCheckStorage.pas',
- Keyman.System.RemoteUpdateCheck in 'main\Keyman.System.RemoteUpdateCheck.pas';
+ Keyman.System.RemoteUpdateCheck in 'main\Keyman.System.RemoteUpdateCheck.pas',
+ BackgroundUpdate in 'main\BackgroundUpdate.pas',
+ Keyman.System.DownloadUpdate in 'main\Keyman.System.DownloadUpdate.pas';
{$R VERSION.RES}
{$R manifest.res}
diff --git a/windows/src/desktop/kmshell/kmshell.dproj b/windows/src/desktop/kmshell/kmshell.dproj
index 16f49e757d8..d09d24cdc19 100644
--- a/windows/src/desktop/kmshell/kmshell.dproj
+++ b/windows/src/desktop/kmshell/kmshell.dproj
@@ -356,6 +356,8 @@
+
+
Cfg_2
@@ -417,12 +419,6 @@
False
-
-
- kmshell.exe
- true
-
-
.\
@@ -435,6 +431,12 @@
true
+
+
+ kmshell.exe
+ true
+
+
1
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
index ccdcffa2bd3..f68e422337f 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
@@ -31,7 +31,7 @@ interface
CheckPeriod: Integer = 7; // Days between checking for updates
type
- TRemoteUpdateCheckDownloadParams = record
+ TDownloadUpdateParams = record
TotalSize: Integer;
TotalDownloads: Integer;
StartPosition: Integer;
@@ -41,7 +41,7 @@ TDownloadUpdate = class
private
FShowErrors: Boolean;
- FDownload: TDownloadUpdateDownloadParams;
+ FDownload: TDownloadUpdateParams;
FCheckOnly: Boolean;
function DownloadUpdates(Params: TUpdateCheckResponse) : Boolean;
From bca532972a8772987b6d62663ea9f05cd5bbe397 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Thu, 14 Dec 2023 10:18:56 +1000
Subject: [PATCH 03/43] feat(windows): WIP merge sm
---
.../desktop/kmshell/main/BackgroundUpdate.pas | 3 +-
.../main/Keyman.System.DownloadUpdate.pas | 161 ++++++++++--------
.../main/Keyman.System.RemoteUpdateCheck.pas | 2 +-
3 files changed, 95 insertions(+), 71 deletions(-)
diff --git a/windows/src/desktop/kmshell/main/BackgroundUpdate.pas b/windows/src/desktop/kmshell/main/BackgroundUpdate.pas
index a8d4e72cab3..cd545045c4e 100644
--- a/windows/src/desktop/kmshell/main/BackgroundUpdate.pas
+++ b/windows/src/desktop/kmshell/main/BackgroundUpdate.pas
@@ -352,7 +352,8 @@ implementation
UfrmOnlineUpdateNewVersion,
utilsystem,
utiluac,
- versioninfo;
+ versioninfo,
+ Keyman.System.DownloadUpdate;
const
SPackageUpgradeFilename = 'upgrade_packages.inf';
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
index f68e422337f..b5f570579b5 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
@@ -56,97 +56,119 @@ TDownloadUpdate = class
implementation
+
+uses
+ GlobalProxySettings,
+ KLog,
+ keymanapi_TLB,
+ KeymanVersion,
+ Keyman.System.UpdateCheckStorage,
+ kmint,
+ ErrorControlledRegistry,
+ RegistryKeys,
+ Upload_Settings,
+ OnlineUpdateCheckMessages;
+
+ // temp wrapper for converting showmessage to logs don't know where
+ // if nt using klog
+ procedure LogMessage(LogMessage: string);
+ begin
+ KL.Log(LogMessage);
+ end;
+
procedure TDownloadUpdate.DoDownloadUpdates(SavePath: string; Params: TUpdateCheckResponse; var Result: Boolean);
var
i, downloadCount: Integer;
+ http: THttpUploader;
+ fs: TFileStream;
function DownloadFile(const url, savepath: string): Boolean;
begin
- with THttpUploader.Create(nil) do
try
- Proxy.Server := GetProxySettings.Server;
- Proxy.Port := GetProxySettings.Port;
- Proxy.Username := GetProxySettings.Username;
- Proxy.Password := GetProxySettings.Password;
- Request.Agent := API_UserAgent;
-
- Request.SetURL(url);
- Upload;
- if Response.StatusCode = 200 then
+ http := THttpUploader.Create(nil);
+ try
+ http.Proxy.Server := GetProxySettings.Server;
+ http.Proxy.Port := GetProxySettings.Port;
+ http.Proxy.Username := GetProxySettings.Username;
+ http.Proxy.Password := GetProxySettings.Password;
+ http.Request.Agent := API_UserAgent;
+
+ http.Request.SetURL(url);
+ http.Upload;
+ if http.Response.StatusCode = 200 then
+ begin
+ fs := TFileStream.Create(savepath, fmCreate);
+ try
+ fs.Write(http.Response.PMessageBody^, http.Response.MessageBodyLength);
+ finally
+ fs.Free;
+ end;
+ Result := True;
+ end
+ else // I2742
+ // If it fails we set to false but will try the other files
+ Result := False;
+ Exit;
+ finally
+ http.Free;
+ end;
+ except
+ on E:EHTTPUploader do
begin
- with TFileStream.Create(savepath, fmCreate) do
- try
- Write(Response.PMessageBody^, Response.MessageBodyLength);
- finally
- Free;
- end;
- Result := True;
- end
- else // I2742
- // If it fails we set to false but will try the other files
+ if (E.ErrorCode = 12007) or (E.ErrorCode = 12029)
+ then LogMessage(S_OnlineUpdate_UnableToContact)
+ else LogMessage(WideFormat(S_OnlineUpdate_UnableToContact_Error, [E.Message]));
Result := False;
- Exit;
- finally
- Free;
+ end;
end;
end;
-
begin
Result := False;
- try
- FDownload.TotalSize := 0;
- FDownload.TotalDownloads := 0;
- downloadCount := 0;
- // Keyboard Packages
- for i := 0 to High(Params.Packages) do
- begin
- Inc(FDownload.TotalDownloads);
- Inc(FDownload.TotalSize, Params.Packages[i].DownloadSize);
- Params.Packages[i].SavePath := SavePath + Params.Packages[i].FileName;
- end;
+ FDownload.TotalSize := 0;
+ FDownload.TotalDownloads := 0;
+ downloadCount := 0;
- // Add the Keyman installer
+ // Keyboard Packages
+ for i := 0 to High(Params.Packages) do
+ begin
Inc(FDownload.TotalDownloads);
- Inc(FDownload.TotalSize, Params.InstallSize);
+ Inc(FDownload.TotalSize, Params.Packages[i].DownloadSize);
+ Params.Packages[i].SavePath := SavePath + Params.Packages[i].FileName;
+ end;
- // Keyboard Packages
- FDownload.StartPosition := 0;
- for i := 0 to High(Params.Packages) do
- begin
- if not DownloadFile(Params.Packages[i].DownloadURL, Params.Packages[i].SavePath) then // I2742
- begin
- Params.Packages[i].Install := False; // Download failed but install other files
- end
- else
- Inc(downloadCount);
- FDownload.StartPosition := FDownload.StartPosition + Params.Packages[i].DownloadSize;
- end;
+ // Add the Keyman installer
+ Inc(FDownload.TotalDownloads);
+ Inc(FDownload.TotalSize, Params.InstallSize);
- // Keyamn Installer
- if not DownloadFile(Params.InstallURL, SavePath + Params.FileName) then // I2742
+ // Keyboard Packages
+ FDownload.StartPosition := 0;
+ for i := 0 to High(Params.Packages) do
begin
- // TODO record fail? and log // Download failed but user wants to install other files
- end
- else
- begin
- Inc(downloadCount)
+ if not DownloadFile(Params.Packages[i].DownloadURL, Params.Packages[i].SavePath) then // I2742
+ begin
+ Params.Packages[i].Install := False; // Download failed but install other files
+ end
+ else
+ Inc(downloadCount);
+ FDownload.StartPosition := FDownload.StartPosition + Params.Packages[i].DownloadSize;
end;
- // There needs to be at least one file successfully downloaded to return
- // TRUE that files where downloaded
- if downloadCount > 0 then
- Result := True;
- except
- on E:EHTTPUploader do
- begin
- if (E.ErrorCode = 12007) or (E.ErrorCode = 12029)
- then LogMessage(S_OnlineUpdate_UnableToContact)
- else LogMessage(WideFormat(S_OnlineUpdate_UnableToContact_Error, [E.Message]));
- Result := False;
- end;
+ // Keyman Installer
+ if not DownloadFile(Params.InstallURL, SavePath + Params.FileName) then // I2742
+ begin
+ // TODO: #10210record fail? and log // Download failed but user wants to install other files
+ end
+ else
+ begin
+ Inc(downloadCount)
end;
+
+ // There needs to be at least one file successfully downloaded to return
+ // True that files were downloaded
+ if downloadCount > 0 then
+ Result := True;
end;
function TDownloadUpdate.DownloadUpdates(Params: TUpdateCheckResponse): Boolean;
@@ -154,10 +176,11 @@ function TDownloadUpdate.DownloadUpdates(Params: TUpdateCheckResponse): Boolean;
DownloadBackGroundSavePath : String;
DownloadResult : Boolean;
begin
+ // DownloadBackGroundSavePath := IncludeTrailingPathDelimiter(GetFolderPath(CSIDL_COMMON_APPDATA) + SFolder_CachedUpdateFiles);
DownloadBackGroundSavePath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
DoDownloadUpdates(DownloadBackGroundSavePath, Params, DownloadResult);
- KL.Log('TDownloadUpdate.DownloadUpdatesBackground: DownloadResult = '+IntToStr(Ord(DownloadResult)));
+ KL.Log('TRemoteUpdateCheck.DownloadUpdatesBackground: DownloadResult = '+IntToStr(Ord(DownloadResult)));
Result := DownloadResult;
end;
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas b/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas
index b6680ca40ec..63510b946e5 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas
@@ -253,7 +253,7 @@ function TRemoteUpdateCheck.DoRun: TRemoteUpdateCheckResult;
Result := wucNoUpdates;
Exit;
end;
- if ValueExists(SRegValue_LastUpdateCheckTime) and (Now - ReadDateTime(SRegValue_LastUpdateCheckTime) < CheckPeriod) and not FForce then
+ if registry.ValueExists(SRegValue_LastUpdateCheckTime) and (Now - registry.ReadDateTime(SRegValue_LastUpdateCheckTime) < CheckPeriod) and not FForce then
begin
Result := wucNoUpdates;
// TODO: #10210 This exit is just to remove the time check for testing.
From 6cb83989d567ff68ffa3b022dbeac1e3960d0167 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Wed, 20 Dec 2023 08:14:33 +1000
Subject: [PATCH 04/43] feat(windows): WIP added keyman has run atom
---
.../general/Keyman.System.ExecuteHistory.pas | 68 ++++++
windows/src/desktop/kmshell/kmshell.dpr | 3 +-
windows/src/desktop/kmshell/kmshell.dproj | 13 +-
.../desktop/kmshell/main/BackgroundUpdate.pas | 162 ++++---------
.../main/Keyman.System.DownloadUpdate.pas | 57 ++++-
.../main/Keyman.System.RemoteUpdateCheck.pas | 219 +++---------------
windows/src/desktop/kmshell/main/initprog.pas | 2 +-
.../src/desktop/kmshell/util/utilkmshell.pas | 16 +-
windows/src/engine/keyman/keyman.dpr | 5 +-
windows/src/engine/keyman/keyman.dproj | 13 +-
windows/src/engine/keyman/main.pas | 6 +-
11 files changed, 233 insertions(+), 331 deletions(-)
create mode 100644 common/windows/delphi/general/Keyman.System.ExecuteHistory.pas
diff --git a/common/windows/delphi/general/Keyman.System.ExecuteHistory.pas b/common/windows/delphi/general/Keyman.System.ExecuteHistory.pas
new file mode 100644
index 00000000000..558defbdec9
--- /dev/null
+++ b/common/windows/delphi/general/Keyman.System.ExecuteHistory.pas
@@ -0,0 +1,68 @@
+unit Keyman.System.ExecuteHistory;
+
+interface
+
+const
+ AtomName = 'KeymanSessionFlag';
+
+function RecordKeymanStarted : Boolean;
+function HasKeymanRun : Boolean;
+
+implementation
+uses
+ System.SysUtils,KLog,
+ Winapi.Windows;
+
+function RecordKeymanStarted : Boolean;
+var
+ atom: WORD;
+begin
+ Result := False;
+ try
+ atom := GlobalFindAtom(AtomName);
+ if atom = 0 then
+ begin
+ if GetLastError <> ERROR_SUCCESS then
+ RaiseLastOSError;
+ //writeln('The Sample Keyman Session Flag has not been set, so the process have never been started in this session.');
+ atom := GlobalAddAtom(AtomName);
+ Result := True;
+ if atom = 0 then
+ RaiseLastOSError;
+ end;
+ //else
+ //writeln('The process has been started previously because the Sample Keyman Session Flag has been set.');
+
+ //writeln;
+ //writeln('* The Sample Keyman Session Flag atom is: '+IntToStr(atom));
+ //writeln;
+ except
+ on E: Exception do
+ KL.Log(E.ClassName + ': ' + E.Message);
+ end;
+end;
+
+function HasKeymanRun : Boolean;
+var
+ atom: WORD;
+begin
+ Result := False;
+ try
+ atom := GlobalFindAtom(AtomName);
+ if atom <> 0 then
+ begin
+ if GetLastError <> ERROR_SUCCESS then
+ RaiseLastOSError;
+
+ Result := True;
+ end
+ else
+ Result := False;
+ except
+ on E: Exception do
+ KL.log(E.ClassName + ': ' + E.Message);
+ end;
+
+end;
+
+end.
diff --git a/windows/src/desktop/kmshell/kmshell.dpr b/windows/src/desktop/kmshell/kmshell.dpr
index c3a2312f6b2..5eb0ee9013c 100644
--- a/windows/src/desktop/kmshell/kmshell.dpr
+++ b/windows/src/desktop/kmshell/kmshell.dpr
@@ -181,7 +181,8 @@ uses
Keyman.System.UpdateCheckStorage in 'main\Keyman.System.UpdateCheckStorage.pas',
Keyman.System.RemoteUpdateCheck in 'main\Keyman.System.RemoteUpdateCheck.pas',
BackgroundUpdate in 'main\BackgroundUpdate.pas',
- Keyman.System.DownloadUpdate in 'main\Keyman.System.DownloadUpdate.pas';
+ Keyman.System.DownloadUpdate in 'main\Keyman.System.DownloadUpdate.pas',
+ Keyman.System.ExecuteHistory in '..\..\..\..\common\windows\delphi\general\Keyman.System.ExecuteHistory.pas';
{$R VERSION.RES}
{$R manifest.res}
diff --git a/windows/src/desktop/kmshell/kmshell.dproj b/windows/src/desktop/kmshell/kmshell.dproj
index d09d24cdc19..159a3fe88d2 100644
--- a/windows/src/desktop/kmshell/kmshell.dproj
+++ b/windows/src/desktop/kmshell/kmshell.dproj
@@ -358,6 +358,7 @@
+
Cfg_2
@@ -419,12 +420,6 @@
False
-
-
- .\
- true
-
-
kmshell.rsm
@@ -437,6 +432,12 @@
true
+
+
+ .\
+ true
+
+
1
diff --git a/windows/src/desktop/kmshell/main/BackgroundUpdate.pas b/windows/src/desktop/kmshell/main/BackgroundUpdate.pas
index cd545045c4e..4809d9587ca 100644
--- a/windows/src/desktop/kmshell/main/BackgroundUpdate.pas
+++ b/windows/src/desktop/kmshell/main/BackgroundUpdate.pas
@@ -32,6 +32,7 @@ interface
httpuploader,
Keyman.System.UpdateCheckResponse,
+ Keyman.System.ExecuteHistory,
UfrmDownloadProgress;
type
@@ -132,47 +133,8 @@ UpdateAvailableState = class(TState)
DownloadingState = class(TState)
private
- { These function could become members of the state objects
- or at the very least controlled by the state objects }
-
- { TODO: make a comment clear when we are in a elevate process}
-
- {
- Performs updates download in the background, without displaying a GUI
- progress bar. This function is similar to DownloadUpdates, but it runs in
- the background.
-
- @returns True if all updates were successfully downloaded, False if any
- download failed.
- }
function DownloadUpdatesBackground: Boolean;
- {
- Performs updates download in the background, without displaying a GUI
- progress bar. This procedure is similar to DownloadUpdates, but it runs in
- the background.
-
- @params SavePath The path where the downloaded files will be saved.
- Result A Boolean value indicating the overall result of the
- download process.
- }
- procedure DoDownloadUpdatesBackground(SavePath: string; var Result: Boolean);
- {
- Performs an online update check, including package retrieval and version
- query.
-
- This function checks if a week has passed since the last update check. It
- utilizes the kmcom API to retrieve the current packages. The function then
- performs an HTTP request to query the remote versions of these packages.
- The resulting information is stored in the FParams variable. Additionally,
- the function handles the main Keyman install package.
-
- @returns A TBackgroundUpdateResult indicating the result of the update
- check.
- }
- // This is just for testing only.
- procedure DoDownloadUpdatesBackgroundTest(SavePath: string; var Result: Boolean);
- public
procedure Enter; override;
procedure Exit; override;
procedure HandleCheck; override;
@@ -289,7 +251,7 @@ TBackgroundUpdate = class
tempPath.
}
procedure SavePackageUpgradesToDownloadTempPath;
- function IsKeymanRunning: Boolean;
+ //function IsKeymanRunning: Boolean;
function checkUpdateSchedule : Boolean;
function SetRegistryState (Update : TUpdateState): Boolean;
@@ -353,6 +315,7 @@ implementation
utilsystem,
utiluac,
versioninfo,
+ Keyman.System.RemoteUpdateCheck,
Keyman.System.DownloadUpdate;
const
@@ -501,18 +464,18 @@ function TBackgroundUpdate.CheckRegistryState : TUpdateState; // I2329
-function TBackgroundUpdate.IsKeymanRunning: Boolean; // I2329
-begin
- try
- Result := kmcom.Control.IsKeymanRunning;
- except
- on E:Exception do
- begin
- KL.Log(E.Message);
- Exit(False);
- end;
- end;
-end;
+//function TBackgroundUpdate.IsKeymanRunning: Boolean; // I2329
+//begin
+// try
+// Result := kmcom.Control.IsKeymanRunning;
+// except
+// on E:Exception do
+// begin
+// KL.Log(E.Message);
+// Exit(False);
+// end;
+// end;
+//end;
function TBackgroundUpdate.CheckUpdateSchedule: Boolean;
begin
@@ -682,18 +645,28 @@ procedure IdleState.Exit;
end;
procedure IdleState.HandleCheck;
+var
+ CheckForUpdates: TRemoteUpdateCheck;
+ Result : TRemoteUpdateCheckResult;
begin
- { TODO: Verify that it has been at least 7 days since last update check -
- only if FSilent = TRUE }
{ Make a HTTP request out and see if updates are available for now do
this all in the Idle HandleCheck message. But could be broken into an
seperate state of WaitngCheck RESP }
{ if Response not OK stay in the idle state and return }
+ CheckForUpdates := TRemoteUpdateCheck.Create(False);
+ try
+ Result:= CheckForUpdates.Run;
+ finally
+ CheckForUpdates.Free;
+ end;
{ Response OK and Update is available }
- ChangeState(UpdateAvailableState);
-
+ if Result = wucSuccess then
+ begin
+ ChangeState(UpdateAvailableState);
+ end;
+ // else staty in idle state
end;
procedure IdleState.HandleDownload;
@@ -795,7 +768,7 @@ procedure DownloadingState.Enter;
DownloadResult := DownloadUpdatesBackground;
if DownloadResult then
begin
- if bucStateContext.IsKeymanRunning then
+ if HasKeymanRun then
ChangeState(WaitingRestartState)
else
ChangeState(InstallingState);
@@ -860,70 +833,31 @@ function DownloadingState.StateName;
Result := 'DownloadingState';
end;
-procedure DownloadingState.DoDownloadUpdatesBackground(SavePath: string; var Result: Boolean);
-begin
-end;
-
-// Test installing only
-procedure DownloadingState.DoDownloadUpdatesBackgroundTest(SavePath: string; var Result: Boolean);
-var
- i, downloadCount: Integer;
- UpdateDir : string;
-
-begin
- try
- Result := False;
-
- UpdateDir := 'C:\Projects\rcswag\testCache';
- KL.Log('DoDownloadUpdatesBackgroundTest SavePath:'+ SavePath);
- // Check if the update source directory exists
- if DirectoryExists(UpdateDir) then
- begin
- // Create the update cached directory if it doesn't exist
- if not DirectoryExists(SavePath) then
- ForceDirectories(SavePath);
-
- // Copy all files from the updatedir to savepath
- TDirectory.Copy(UpdateDir, SavePath);
- Result:= True;
- KL.Log('All files copied successfully.');
- end
- else
- KL.Log('Source directory does not exist.');
- except
- on E: Exception do
- KL.Log('Error: ' + E.Message);
- end;
-
-end;
-
function DownloadingState.DownloadUpdatesBackground: Boolean;
var
i: Integer;
DownloadBackGroundSavePath : String;
DownloadResult : Boolean;
+ DownloadUpdate: TDownloadUpdate;
begin
- //DownloadTempPath := IncludeTrailingPathDelimiter(CreateTempPath);
- DownloadBackGroundSavePath := IncludeTrailingPathDelimiter(GetFolderPath(CSIDL_COMMON_APPDATA) + SFolder_CachedUpdateFiles);
- //DownloadBackGroundSavePath := DownloadBackGroundSavePath + 'RCTest.txt';
- { For now lets download all the updates. We need to take these from the user via check box form }
-
- if bucStateContext.FParams.Keyman.DownloadURL <> '' then
- bucStateContext.FParams.Keyman.Install := True;
-
- for i := 0 to High(bucStateContext.FParams.Packages) do
- bucStateContext.FParams.Packages[i].Install := True;
-
- // Download files
- // DoDownloadUpdatesBackground(DownloadBackGroundSavePath, DownloadResult);
- // For development by passing the DoDownloadUpdatesBackground and just add
- // the cached file on my local disk as a simulation
- DoDownloadUpdatesBackgroundTest(DownloadBackGroundSavePath, DownloadResult);
+ DownloadUpdate := TDownloadUpdate.Create;
+ try
+ DownloadResult := DownloadUpdate.DownloadUpdates;
+ KL.Log('TBackgroundUpdate.DownloadUpdatesBackground: DownloadResult = '+IntToStr(Ord(DownloadResult)));
+ Result := DownloadResult;
+// TODO: workout when we need to refresh kmcom keyboards
+
+// if Result in [ wucSuccess] then
+// begin
+// kmcom.Keyboards.Refresh;
+// kmcom.Keyboards.Apply;
+// kmcom.Packages.Refresh;
+// end;
- KL.Log('TBackgroundUpdate.DownloadUpdatesBackground: DownloadResult = '+IntToStr(Ord(DownloadResult)));
- //ShowMessage('TBackgroundUpdate.DownloadUpdatesBackground: DownloadResult = '+IntToStr(Ord(DownloadResult)));
- Result := DownloadResult;
+ finally
+ DownloadUpdate.Free;
+ end;
end;
{ WaitingRestartState }
@@ -1216,7 +1150,7 @@ procedure WaitingPostInstallState.HandleMSIInstallComplete;
begin
KL.Log('WaitingPostInstallState.HandleMSIInstallComplete');
// TODO Remove cached files. Do any loging updating of files etc and then set back to idle
- SavePath := IncludeTrailingPathDelimiter(GetFolderPath(CSIDL_COMMON_APPDATA) + SFolder_CachedUpdateFiles);
+ //SavePath := IncludeTrailingPathDelimiter(GetFolderPath(CSIDL_COMMON_APPDATA) + SFolder_CachedUpdateFiles);
/// For testing using local user area cache
SavePath := 'C:\Projects\rcswag\testCache';
KL.Log('WaitingPostInstallState.HandleMSIInstallComplete remove SavePath:'+ SavePath);
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
index b5f570579b5..d216f2f4634 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
@@ -39,19 +39,34 @@ TDownloadUpdateParams = record
TDownloadUpdate = class
private
-
FShowErrors: Boolean;
FDownload: TDownloadUpdateParams;
- FCheckOnly: Boolean;
-
- function DownloadUpdates(Params: TUpdateCheckResponse) : Boolean;
+ FErrorMessage: string;
+ {
+ Performs updates download in the background, without displaying a GUI
+ progress bar.
+ @params SavePath The path where the downloaded files will be saved.
+ Result A Boolean value indicating the overall result of the
+ download process.
+ }
procedure DoDownloadUpdates(SavePath: string; Params: TUpdateCheckResponse; var Result: Boolean);
public
- constructor Create(AForce : Boolean; ACheckOnly: Boolean = False);
+ constructor Create;
destructor Destroy; override;
+ {
+ Performs updates download in the background, without displaying a GUI
+ progress bar. This function is similar to DownloadUpdates, but it runs in
+ the background.
+
+ @returns True if all updates were successfully downloaded, False if any
+ download failed.
+ }
+
+ function DownloadUpdates : Boolean;
property ShowErrors: Boolean read FShowErrors write FShowErrors;
+
end;
implementation
@@ -76,6 +91,23 @@ implementation
KL.Log(LogMessage);
end;
+constructor TDownloadUpdate.Create;
+begin
+ inherited Create;
+
+ FShowErrors := True;
+ KL.Log('TDownloadUpdate.Create');
+end;
+
+destructor TDownloadUpdate.Destroy;
+begin
+ if (FErrorMessage <> '') and FShowErrors then
+ LogMessage(FErrorMessage);
+
+ KL.Log('TDownloadUpdate.Destroy: FErrorMessage = '+FErrorMessage);
+ inherited Destroy;
+end;
+
procedure TDownloadUpdate.DoDownloadUpdates(SavePath: string; Params: TUpdateCheckResponse; var Result: Boolean);
var
i, downloadCount: Integer;
@@ -171,19 +203,22 @@ procedure TDownloadUpdate.DoDownloadUpdates(SavePath: string; Params: TUpdateChe
Result := True;
end;
-function TDownloadUpdate.DownloadUpdates(Params: TUpdateCheckResponse): Boolean;
+function TDownloadUpdate.DownloadUpdates: Boolean;
var
DownloadBackGroundSavePath : String;
DownloadResult : Boolean;
+ ucr: TUpdateCheckResponse;
begin
// DownloadBackGroundSavePath := IncludeTrailingPathDelimiter(GetFolderPath(CSIDL_COMMON_APPDATA) + SFolder_CachedUpdateFiles);
DownloadBackGroundSavePath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
-
- DoDownloadUpdates(DownloadBackGroundSavePath, Params, DownloadResult);
- KL.Log('TRemoteUpdateCheck.DownloadUpdatesBackground: DownloadResult = '+IntToStr(Ord(DownloadResult)));
- Result := DownloadResult;
+ if TUpdateCheckStorage.LoadUpdateCacheData(ucr) then
+ begin
+ DoDownloadUpdates(DownloadBackGroundSavePath, ucr, DownloadResult);
+ KL.Log('DownloadUpdates.DownloadUpdatesBackground: DownloadResult = '+IntToStr(Ord(DownloadResult)));
+ Result := DownloadResult;
+ end;
+ Result := False;
end;
-
end.
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas b/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas
index 63510b946e5..b1738bad936 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas
@@ -33,26 +33,32 @@ TRemoteUpdateCheck = class
private
FForce: Boolean;
FRemoteResult: TRemoteUpdateCheckResult;
-
FErrorMessage: string;
-
FShowErrors: Boolean;
- FDownload: TRemoteUpdateCheckDownloadParams;
- FCheckOnly: Boolean;
-
- function DownloadUpdates(Params: TUpdateCheckResponse) : Boolean;
- procedure DoDownloadUpdates(SavePath: string; Params: TUpdateCheckResponse; var Result: Boolean);
+ {
+ Performs an online update check, including package retrieval and version
+ query.
+
+ This function checks if a week has passed since the last update check. It
+ utilizes the kmcom API to retrieve the current packages. The function then
+ performs an HTTP request to query the remote versions of these packages.
+ The resulting information is stored in the FParams variable. Additionally,
+ the function handles the main Keyman install package.
+
+ @returns A TBackgroundUpdateResult indicating the result of the update
+ check.
+ }
function DoRun: TRemoteUpdateCheckResult;
public
- constructor Create(AForce : Boolean; ACheckOnly: Boolean = False);
+ constructor Create(AForce : Boolean);
destructor Destroy; override;
function Run: TRemoteUpdateCheckResult;
property ShowErrors: Boolean read FShowErrors write FShowErrors;
end;
procedure LogMessage(LogMessage: string);
-function CheckForUpdates: Boolean;
+function ConfigCheckContinue: Boolean;
implementation
@@ -75,7 +81,7 @@ implementation
{ TRemoteUpdateCheck }
-constructor TRemoteUpdateCheck.Create(AForce, ACheckOnly: Boolean);
+constructor TRemoteUpdateCheck.Create(AForce: Boolean);
begin
inherited Create;
@@ -83,7 +89,6 @@ constructor TRemoteUpdateCheck.Create(AForce, ACheckOnly: Boolean);
FRemoteResult := wucUnknown;
FForce := AForce;
- FCheckOnly := ACheckOnly;
KL.Log('TRemoteUpdateCheck.Create');
end;
@@ -102,135 +107,18 @@ destructor TRemoteUpdateCheck.Destroy;
function TRemoteUpdateCheck.Run: TRemoteUpdateCheckResult;
begin
Result := DoRun;
-
- if Result in [ wucSuccess] then
- begin
- kmcom.Keyboards.Refresh;
- kmcom.Keyboards.Apply;
- kmcom.Packages.Refresh;
- end;
-
FRemoteResult := Result;
end;
-
-procedure TRemoteUpdateCheck.DoDownloadUpdates(SavePath: string; Params: TUpdateCheckResponse; var Result: Boolean);
-var
- i, downloadCount: Integer;
- http: THttpUploader;
- fs: TFileStream;
-
- function DownloadFile(const url, savepath: string): Boolean;
- begin
- try
- http := THttpUploader.Create(nil);
- try
- http.Proxy.Server := GetProxySettings.Server;
- http.Proxy.Port := GetProxySettings.Port;
- http.Proxy.Username := GetProxySettings.Username;
- http.Proxy.Password := GetProxySettings.Password;
- http.Request.Agent := API_UserAgent;
-
- http.Request.SetURL(url);
- http.Upload;
- if http.Response.StatusCode = 200 then
- begin
- fs := TFileStream.Create(savepath, fmCreate);
- try
- fs.Write(http.Response.PMessageBody^, http.Response.MessageBodyLength);
- finally
- fs.Free;
- end;
- Result := True;
- end
- else // I2742
- // If it fails we set to false but will try the other files
- Result := False;
- Exit;
- finally
- http.Free;
- end;
- except
- on E:EHTTPUploader do
- begin
- if (E.ErrorCode = 12007) or (E.ErrorCode = 12029)
- then LogMessage(S_OnlineUpdate_UnableToContact)
- else LogMessage(WideFormat(S_OnlineUpdate_UnableToContact_Error, [E.Message]));
- Result := False;
- end;
- end;
- end;
-
-begin
- Result := False;
-
- FDownload.TotalSize := 0;
- FDownload.TotalDownloads := 0;
- downloadCount := 0;
-
- // Keyboard Packages
- for i := 0 to High(Params.Packages) do
- begin
- Inc(FDownload.TotalDownloads);
- Inc(FDownload.TotalSize, Params.Packages[i].DownloadSize);
- Params.Packages[i].SavePath := SavePath + Params.Packages[i].FileName;
- end;
-
- // Add the Keyman installer
- Inc(FDownload.TotalDownloads);
- Inc(FDownload.TotalSize, Params.InstallSize);
-
- // Keyboard Packages
- FDownload.StartPosition := 0;
- for i := 0 to High(Params.Packages) do
- begin
- if not DownloadFile(Params.Packages[i].DownloadURL, Params.Packages[i].SavePath) then // I2742
- begin
- Params.Packages[i].Install := False; // Download failed but install other files
- end
- else
- Inc(downloadCount);
- FDownload.StartPosition := FDownload.StartPosition + Params.Packages[i].DownloadSize;
- end;
-
- // Keyman Installer
- if not DownloadFile(Params.InstallURL, SavePath + Params.FileName) then // I2742
- begin
- // TODO: #10210record fail? and log // Download failed but user wants to install other files
- end
- else
- begin
- Inc(downloadCount)
- end;
-
- // There needs to be at least one file successfully downloaded to return
- // True that files were downloaded
- if downloadCount > 0 then
- Result := True;
-end;
-
-function TRemoteUpdateCheck.DownloadUpdates(Params: TUpdateCheckResponse): Boolean;
-var
- DownloadBackGroundSavePath : String;
- DownloadResult : Boolean;
-begin
- DownloadBackGroundSavePath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
-
- DoDownloadUpdates(DownloadBackGroundSavePath, Params, DownloadResult);
- KL.Log('TRemoteUpdateCheck.DownloadUpdatesBackground: DownloadResult = '+IntToStr(Ord(DownloadResult)));
- Result := DownloadResult;
-
-end;
-
function TRemoteUpdateCheck.DoRun: TRemoteUpdateCheckResult;
var
flags: DWord;
i: Integer;
ucr: TUpdateCheckResponse;
pkg: IKeymanPackage;
- downloadResult: boolean;
registry: TRegistryErrorControlled;
http: THttpUploader;
+ proceed : boolean;
begin
{FProxyHost := '';
FProxyPort := 0;}
@@ -242,44 +130,13 @@ function TRemoteUpdateCheck.DoRun: TRemoteUpdateCheckResult;
Exit;
end;
- { Verify that it has been at least CheckPeriod days since last update check }
- try
- registry := TRegistryErrorControlled.Create; // I2890
- try
- if registry.OpenKeyReadOnly(SRegKey_KeymanDesktop_CU) then
- begin
- if registry.ValueExists(SRegValue_CheckForUpdates) and not registry.ReadBool(SRegValue_CheckForUpdates) and not FForce then
- begin
- Result := wucNoUpdates;
- Exit;
- end;
- if registry.ValueExists(SRegValue_LastUpdateCheckTime) and (Now - registry.ReadDateTime(SRegValue_LastUpdateCheckTime) < CheckPeriod) and not FForce then
- begin
- Result := wucNoUpdates;
- // TODO: #10210 This exit is just to remove the time check for testing.
- //Exit;
- end;
-
- {if ValueExists(SRegValue_UpdateCheck_UseProxy) and ReadBool(SRegValue_UpdateCheck_UseProxy) then
- begin
- FProxyHost := ReadString(SRegValue_UpdateCheck_ProxyHost);
- FProxyPort := StrToIntDef(ReadString(SRegValue_UpdateCheck_ProxyPort), 80);
- end;}
- end;
- finally
- registry.Free;
- end;
- except
- { we will not run the check if an error occurs reading the settings }
- on E:Exception do
+ proceed := ConfigCheckContinue;
+ if not proceed and not FForce then
begin
- Result := wucFailure;
- FErrorMessage := E.Message;
+ Result := wucNoUpdates;
Exit;
end;
- end;
- Result := wucNoUpdates;
try
http := THTTPUploader.Create(nil);
@@ -316,25 +173,8 @@ function TRemoteUpdateCheck.DoRun: TRemoteUpdateCheckResult;
begin
if ucr.Parse(http.Response.MessageBodyAsString, 'bundle', CKeymanVersionInfo.Version) then
begin
- //ResponseToParams(ucr);
-
- if FCheckOnly then
- begin
- TUpdateCheckStorage.SaveUpdateCacheData(ucr);
- Result := FRemoteResult;
- end
- // TODO: ##10210
- // Integerate into state machine. in the download state
- // the process can call LoadUpdateCacheData if needed to get the
- // response result.
- else if (Length(ucr.Packages) > 0) or (ucr.InstallURL <> '') then
- begin
- downloadResult := DownloadUpdates(ucr);
- if DownloadResult then
- begin
- Result := wucSuccess;
- end;
- end;
+ TUpdateCheckStorage.SaveUpdateCacheData(ucr);
+ Result := wucSuccess;
end
else
begin
@@ -378,20 +218,23 @@ function TRemoteUpdateCheck.DoRun: TRemoteUpdateCheckResult;
KL.Log(LogMessage);
end;
-function CheckForUpdates: Boolean;
+function ConfigCheckContinue: Boolean;
+var
+ registry: TRegistryErrorControlled;
begin
{ Verify that it has been at least CheckPeriod days since last update check }
+ Result := False;
try
- with TRegistryErrorControlled.Create do // I2890
+ registry := TRegistryErrorControlled.Create; // I2890
try
- if OpenKeyReadOnly(SRegKey_KeymanDesktop_CU) then
+ if registry.OpenKeyReadOnly(SRegKey_KeymanDesktop_CU) then
begin
- if ValueExists(SRegValue_CheckForUpdates) and not ReadBool(SRegValue_CheckForUpdates) then
+ if registry.ValueExists(SRegValue_CheckForUpdates) and not registry.ReadBool(SRegValue_CheckForUpdates) then
begin
Result := False;
Exit;
end;
- if ValueExists(SRegValue_LastUpdateCheckTime) and (Now - ReadDateTime(SRegValue_LastUpdateCheckTime) > CheckPeriod) then
+ if registry.ValueExists(SRegValue_LastUpdateCheckTime) and (Now - registry.ReadDateTime(SRegValue_LastUpdateCheckTime) > CheckPeriod) then
begin
Result := True;
end
@@ -402,7 +245,7 @@ function CheckForUpdates: Boolean;
Exit;
end;
finally
- Free;
+ registry.Free;
end;
except
{ we will not run the check if an error occurs reading the settings }
diff --git a/windows/src/desktop/kmshell/main/initprog.pas b/windows/src/desktop/kmshell/main/initprog.pas
index 4076ab21015..7a2e05200d9 100644
--- a/windows/src/desktop/kmshell/main/initprog.pas
+++ b/windows/src/desktop/kmshell/main/initprog.pas
@@ -433,7 +433,7 @@ procedure RunKMCOM(FMode: TKMShellMode; KeyboardFileNames: TStrings; FSilent, FF
end;
// TODO: #10038 Will add this as part of the background update state machine
// for now just verifing the download happens via -buc switch.
- RemoteUpdateCheck := TRemoteUpdateCheck.Create(False, False);
+ RemoteUpdateCheck := TRemoteUpdateCheck.Create(False);
try
if (FMode = fmBackgroundUpdateCheck) then
begin
diff --git a/windows/src/desktop/kmshell/util/utilkmshell.pas b/windows/src/desktop/kmshell/util/utilkmshell.pas
index 74857532839..76eb4929dd1 100644
--- a/windows/src/desktop/kmshell/util/utilkmshell.pas
+++ b/windows/src/desktop/kmshell/util/utilkmshell.pas
@@ -33,7 +33,7 @@
interface
uses
- System.UITypes,
+ System.UITypes, System.IOUtils, System.Types,
Dialogs, Windows, ComObj, shlobj, controls, sysutils, classes;
const
@@ -96,6 +96,7 @@ procedure SplitString(const instr: string; var outstr1, outstr2: string; const s
function ValidDirectory(const dir: string): string;
function GetLongFile(APath:String):String;
+procedure GetFileNamesInDirectory(const directoryPath: string; var fileNames: TStringDynArray);
function TSFInstalled: Boolean;
@@ -540,6 +541,19 @@ function GetLongFile(APath:String):String;
until Length(APath)=0;
end; {Peter Haas}
+procedure GetFileNamesInDirectory(const directoryPath: string; var fileNames: TStringDynArray);
+
+begin
+ // Check if the directory exists
+ if TDirectory.Exists(directoryPath) then
+ begin
+ // Retrieve file names within the directory
+ fileNames := TDirectory.GetFiles(directoryPath);
+ end
+ else
+ KL.Log('Directory does not exist.');
+end;
+
{ TString }
constructor TString.Create(const AString: string);
diff --git a/windows/src/engine/keyman/keyman.dpr b/windows/src/engine/keyman/keyman.dpr
index 9f8a3fba8e9..d995ca24c90 100644
--- a/windows/src/engine/keyman/keyman.dpr
+++ b/windows/src/engine/keyman/keyman.dpr
@@ -32,7 +32,7 @@ uses
UfrmOSKPlugInBase in 'viskbd\UfrmOSKPlugInBase.pas' {frmOSKPlugInBase},
UfrmOSKCharacterMap in 'viskbd\UfrmOSKCharacterMap.pas' {frmOSKCharacterMap},
UfrmOSKEntryHelper in 'viskbd\UfrmOSKEntryHelper.pas' {frmOSKEntryHelper},
- TTInfo in '..\..\..\..\common\windows\delphi\general\TTInfo.pas',
+ ttinfo in '..\..\..\..\common\windows\delphi\general\ttinfo.pas',
UnicodeData in '..\..\..\..\common\windows\delphi\charmap\UnicodeData.pas',
CharacterMapSettings in '..\..\..\..\common\windows\delphi\charmap\CharacterMapSettings.pas',
CharacterRanges in '..\..\..\..\common\windows\delphi\charmap\CharacterRanges.pas',
@@ -112,7 +112,8 @@ uses
Sentry.Client.Vcl in '..\..\..\..\common\windows\delphi\ext\sentry\Sentry.Client.Vcl.pas',
sentry in '..\..\..\..\common\windows\delphi\ext\sentry\sentry.pas',
Keyman.System.KeymanSentryClient in '..\..\..\..\common\windows\delphi\general\Keyman.System.KeymanSentryClient.pas',
- Keyman.System.LocaleStrings in '..\..\global\delphi\cust\Keyman.System.LocaleStrings.pas';
+ Keyman.System.LocaleStrings in '..\..\global\delphi\cust\Keyman.System.LocaleStrings.pas',
+ Keyman.System.ExecuteHistory in '..\..\..\..\common\windows\delphi\general\Keyman.System.ExecuteHistory.pas';
{$R ICONS.RES}
{$R VERSION.RES}
diff --git a/windows/src/engine/keyman/keyman.dproj b/windows/src/engine/keyman/keyman.dproj
index 44959854d5a..71cdb756d38 100644
--- a/windows/src/engine/keyman/keyman.dproj
+++ b/windows/src/engine/keyman/keyman.dproj
@@ -141,7 +141,7 @@
-
+
@@ -238,6 +238,7 @@
+
Cfg_2
@@ -299,21 +300,21 @@
False
-
+
- keyman.exe
+ .\
true
-
+
keyman.rsm
true
-
+
- .\
+ keyman.exe
true
diff --git a/windows/src/engine/keyman/main.pas b/windows/src/engine/keyman/main.pas
index 2c019150d7c..223c1385582 100644
--- a/windows/src/engine/keyman/main.pas
+++ b/windows/src/engine/keyman/main.pas
@@ -45,7 +45,8 @@ implementation
KeymanVersion,
RegistryKeys,
UfrmKeyman7Main,
- UserMessages;
+ UserMessages,
+ Keyman.System.ExecuteHistory;
function ValidateParameters(var FCommand: Integer): Boolean; forward;
function PassParametersToRunningInstance(FCommand: Integer): Boolean; forward;
@@ -79,6 +80,9 @@ procedure RunProgram;
if not ValidateParameters(FCommand) then Exit;
+ // TODO set atom application running
+ RecordKeymanStarted;
+
hProgramMutex := CreateMutex(nil, False, 'KeymanEXE70');
if hProgramMutex = 0 then
begin
From dd5767a372a52102b198dfe755b3c0b8f378ecdf Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Fri, 22 Dec 2023 10:11:32 +1000
Subject: [PATCH 05/43] feat(windows): integrate sm states and handlekmshell
---
.../general/Keyman.System.ExecuteHistory.pas | 5 +
.../desktop/kmshell/main/BackgroundUpdate.pas | 102 +++++++++++-------
.../main/Keyman.System.DownloadUpdate.pas | 59 +++++++++-
windows/src/desktop/kmshell/main/initprog.pas | 14 ++-
...eyman.System.Install.EnginePostInstall.pas | 36 +++++++
windows/src/engine/keyman/main.pas | 4 +-
6 files changed, 177 insertions(+), 43 deletions(-)
diff --git a/common/windows/delphi/general/Keyman.System.ExecuteHistory.pas b/common/windows/delphi/general/Keyman.System.ExecuteHistory.pas
index 558defbdec9..97e9b2bfb1b 100644
--- a/common/windows/delphi/general/Keyman.System.ExecuteHistory.pas
+++ b/common/windows/delphi/general/Keyman.System.ExecuteHistory.pas
@@ -18,14 +18,18 @@ function RecordKeymanStarted : Boolean;
atom: WORD;
begin
Result := False;
+ KL.Log('RecordKeymanStarted: Enter');
try
atom := GlobalFindAtom(AtomName);
+ KL.Log('Keyman Session Flag atom is: '+IntToStr(atom));
if atom = 0 then
begin
+ KL.Log('RecordKeymanStarted: if atom = 0');
if GetLastError <> ERROR_SUCCESS then
RaiseLastOSError;
//writeln('The Sample Keyman Session Flag has not been set, so the process have never been started in this session.');
atom := GlobalAddAtom(AtomName);
+ KL.Log('RecordKeymanStarted: True');
Result := True;
if atom = 0 then
RaiseLastOSError;
@@ -54,6 +58,7 @@ function HasKeymanRun : Boolean;
if GetLastError <> ERROR_SUCCESS then
RaiseLastOSError;
+ KL.Log('HasKeymanRun: Keyman Has Run');
Result := True;
end
else
diff --git a/windows/src/desktop/kmshell/main/BackgroundUpdate.pas b/windows/src/desktop/kmshell/main/BackgroundUpdate.pas
index 4809d9587ca..fe2eb22a6c4 100644
--- a/windows/src/desktop/kmshell/main/BackgroundUpdate.pas
+++ b/windows/src/desktop/kmshell/main/BackgroundUpdate.pas
@@ -213,8 +213,6 @@ WaitingPostInstallState = class(TState)
{ This class also controls the state flow see }
TBackgroundUpdate = class
private
- FOwner: TCustomForm;
- FSilent: Boolean;
FForce: Boolean;
FAuto: Boolean;
FParams: TBackgroundUpdateParams;
@@ -240,6 +238,7 @@ TBackgroundUpdate = class
FWaitingPostInstall: WaitingPostInstallState;
function GetState: TStateClass;
procedure SetState(const Value: TStateClass);
+ procedure SetStateOnly(const Value: TStateClass);
function ConvertEnumState(const TEnumState: TUpdateState): TStateClass;
procedure ShutDown;
@@ -260,7 +259,7 @@ TBackgroundUpdate = class
property State: TStateClass read GetState write SetState;
public
- constructor Create(AOwner: TCustomForm; AForce, ASilent: Boolean);
+ constructor Create(AForce: Boolean);
destructor Destroy; override;
procedure HandleCheck;
@@ -325,17 +324,15 @@ implementation
{ TBackgroundUpdate }
-constructor TBackgroundUpdate.Create(AOwner: TCustomForm; AForce, ASilent: Boolean);
+constructor TBackgroundUpdate.Create(AForce : Boolean);
var TSerailsedState : TUpdateState;
begin
inherited Create;
- FOwner := AOwner;
FShowErrors := True;
FParams.Result := oucUnknown;
- FSilent := ASilent;
FForce := AForce;
FAuto := True; // Default to automatically check, download, and install
FIdle := IdleState.Create(Self);
@@ -346,14 +343,14 @@ constructor TBackgroundUpdate.Create(AOwner: TCustomForm; AForce, ASilent: Boole
FRetry := RetryState.Create(Self);
FWaitingPostInstall := WaitingPostInstallState.Create(Self);
// Check the Registry setting.
- state := ConvertEnumState(CheckRegistryState);
+ SetStateOnly(ConvertEnumState(CheckRegistryState));
KL.Log('TBackgroundUpdate.Create');
end;
destructor TBackgroundUpdate.Destroy;
begin
- if (FErrorMessage <> '') and not FSilent and FShowErrors then
- ShowMessage(FErrorMessage);
+ if (FErrorMessage <> '') and FShowErrors then
+ KL.Log(FErrorMessage);
if FParams.Result = oucShutDown then
ShutDown;
@@ -524,6 +521,21 @@ procedure TBackgroundUpdate.SetState(const Value: TStateClass);
CurrentState.Exit;
end;
+ SetStateOnly(Value);
+
+ if Assigned(CurrentState) then
+ begin
+ CurrentState.Enter;
+ end
+ else
+ begin
+ // TODO: Unable to set state for Value []
+ end;
+
+end;
+
+procedure TBackgroundUpdate.SetStateOnly(const Value: TStateClass);
+begin
if Value = IdleState then
begin
CurrentState := FIdle;
@@ -552,16 +564,6 @@ procedure TBackgroundUpdate.SetState(const Value: TStateClass);
begin
CurrentState := FWaitingPostInstall;
end;
-
- if Assigned(CurrentState) then
- begin
- CurrentState.Enter;
- end
- else
- begin
- // TODO: Unable to set state for Value []
- end;
-
end;
function TBackgroundUpdate.ConvertEnumState(const TEnumState: TUpdateState) : TStateClass;
@@ -654,7 +656,9 @@ procedure IdleState.HandleCheck;
this all in the Idle HandleCheck message. But could be broken into an
seperate state of WaitngCheck RESP }
{ if Response not OK stay in the idle state and return }
- CheckForUpdates := TRemoteUpdateCheck.Create(False);
+ //CheckForUpdates := TRemoteUpdateCheck.Create(False);
+ // should be false but forcing check for testing
+ CheckForUpdates := TRemoteUpdateCheck.Create(True);
try
Result:= CheckForUpdates.Run;
finally
@@ -884,19 +888,40 @@ procedure WaitingRestartState.HandleDownload;
end;
function WaitingRestartState.HandleKmShell;
+var
+ SavedPath : String;
+ Filenames : TStringDynArray;
begin
- // Check downloaded cache if available then
- // change state intalling
- // else then change to idle and handle checkupdates state
- // if bucStateContext.IsKeymanRunning then
- // stay as waiting
- // log this is unexpected
- //ChangeState(WaitingRestartState)
- // else
- //
-
- ChangeState(UpdateAvailableState);
- Result := kmShellExit;
+ KL.Log('WaitingRestartState.HandleKmShell Enter');
+ // Still can't go if keyman has run
+ if HasKeymanRun then
+ begin
+ KL.Log('WaitingRestartState.HandleKmShell Keyman Has Run');
+ Result := kmShellExit;
+ // Exit; // Exit is not wokring for some reason.
+ // this else is only here because the exit is not working.
+ end
+ else
+ begin
+ // Check downloaded cache if available then
+ SavedPath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
+ GetFileNamesInDirectory(SavedPath, FileNames);
+ if Length(FileNames) = 0 then
+ begin
+ KL.Log('WaitingRestartState.HandleKmShell No Files in Download Cache');
+ // Return to Idle state and check for Updates state
+ ChangeState(IdleState);
+ bucStateContext.CurrentState.HandleCheck;
+ Result := kmShellExit;
+ // Exit; // again exit was not working
+ end
+ else
+ begin
+ KL.Log('WaitingRestartState.HandleKmShell is good to install');
+ ChangeState(InstallingState);
+ Result := kmShellExit;
+ end;
+ end;
end;
procedure WaitingRestartState.HandleInstall;
@@ -962,10 +987,12 @@ function InstallingState.DoInstallKeyman(SavePath: string) : Boolean;
if s = '.msi' then
FResult := TUtilExecute.Shell(0, 'msiexec.exe', '', '/qb /i "'+SavePath+'" AUTOLAUNCHPRODUCT=1') // I3349
else if s = '.exe' then
+ begin
+ KL.Log('TBackgroundUpdate.InstallingState.DoInstallKeyman SavePath:"'+ SavePath+'"');
FResult := TUtilExecute.Shell(0, SavePath, '', '-au') // I3349
+ end
else
- Exit;
- //Exit(False);
+ FResult := False;
if not FResult then
begin
@@ -1033,7 +1060,9 @@ procedure InstallingState.HandleDownload;
function InstallingState.HandleKmShell;
begin
// Result = exit straight away as we are installing (MSI installer)
- Result := kmShellExit;
+ // need to just do a no-op keyman will it maybe using kmshell to install
+ // packages.
+ Result := kmShellContinue;
end;
procedure InstallingState.HandleInstall;
@@ -1152,7 +1181,8 @@ procedure WaitingPostInstallState.HandleMSIInstallComplete;
// TODO Remove cached files. Do any loging updating of files etc and then set back to idle
//SavePath := IncludeTrailingPathDelimiter(GetFolderPath(CSIDL_COMMON_APPDATA) + SFolder_CachedUpdateFiles);
/// For testing using local user area cache
- SavePath := 'C:\Projects\rcswag\testCache';
+ //SavePath := 'C:\Projects\rcswag\testCache';
+ SavePath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
KL.Log('WaitingPostInstallState.HandleMSIInstallComplete remove SavePath:'+ SavePath);
GetFileNamesInDirectory(SavePath, FileNames);
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
index d216f2f4634..bee733e4b7d 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
@@ -65,6 +65,7 @@ TDownloadUpdate = class
}
function DownloadUpdates : Boolean;
+ function CheckAllFilesDownloaded : Boolean;
property ShowErrors: Boolean read FShowErrors write FShowErrors;
end;
@@ -82,7 +83,10 @@ implementation
ErrorControlledRegistry,
RegistryKeys,
Upload_Settings,
- OnlineUpdateCheckMessages;
+ OnlineUpdateCheckMessages,
+ utilkmshell,
+ System.Types,
+ System.StrUtils;
// temp wrapper for converting showmessage to logs don't know where
// if nt using klog
@@ -216,8 +220,59 @@ function TDownloadUpdate.DownloadUpdates: Boolean;
DoDownloadUpdates(DownloadBackGroundSavePath, ucr, DownloadResult);
KL.Log('DownloadUpdates.DownloadUpdatesBackground: DownloadResult = '+IntToStr(Ord(DownloadResult)));
Result := DownloadResult;
+ end
+ else
+ Result := False;
+end;
+
+function TDownloadUpdate.CheckAllFilesDownloaded: Boolean;
+var
+ i : Integer;
+ SavedPath : String;
+ DownloadResult : Boolean;
+ Params: TUpdateCheckResponse;
+ VerifyDownloads : TDownloadUpdateParams;
+ FileNames : TStringDynArray;
+
+begin
+ // DownloadBackGroundSavePath := IncludeTrailingPathDelimiter(GetFolderPath(CSIDL_COMMON_APPDATA) + SFolder_CachedUpdateFiles);
+ SavedPath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
+ GetFileNamesInDirectory(SavedPath, FileNames);
+ if Length(FileNames) = 0 then
+ begin
+ Result := False;
+ Exit;
+ end;
+
+ if TUpdateCheckStorage.LoadUpdateCacheData(Params) then
+ begin
+ for i := 0 to High(Params.Packages) do
+ begin
+ Inc(VerifyDownloads.TotalDownloads);
+ Inc(VerifyDownloads.TotalSize, Params.Packages[i].DownloadSize);
+ if Not MatchStr(Params.Packages[i].FileName, FileNames) then
+ begin
+ Result := False;
+ Exit;
+ end;
+ Params.Packages[i].SavePath := SavedPath + Params.Packages[i].FileName;
+ end;
+ // Add the Keyman installer
+ Inc(FDownload.TotalDownloads);
+ Inc(FDownload.TotalSize, Params.InstallSize);
+ // Check if the Keyman installer downloaded
+ if Not MatchStr(Params.FileName, FileNames) then
+ begin
+ Result := False;
+ Exit;
+ end;
+ // TODO verify filesizes match so we know we don't have partical downloades.
+ Result := True;
+ end
+ else
+ begin
+ Result := False;
end;
- Result := False;
end;
diff --git a/windows/src/desktop/kmshell/main/initprog.pas b/windows/src/desktop/kmshell/main/initprog.pas
index 7a2e05200d9..94e47a32680 100644
--- a/windows/src/desktop/kmshell/main/initprog.pas
+++ b/windows/src/desktop/kmshell/main/initprog.pas
@@ -143,6 +143,7 @@ implementation
UpgradeMnemonicLayout,
utilfocusappwnd,
utilkmshell,
+ BackgroundUpdate,
KeyboardTIPCheck,
@@ -384,7 +385,7 @@ procedure RunKMCOM(FMode: TKMShellMode; KeyboardFileNames: TStrings; FSilent, FF
kdl: IKeymanDefaultLanguage;
FIcon: string;
FMutex: TKeymanMutex; // I2720
- RemoteUpdateCheck: TRemoteUpdateCheck;
+ BUpdateSM : TBackgroundUpdate;
function FirstKeyboardFileName: WideString;
begin
if KeyboardFileNames.Count = 0
@@ -433,15 +434,20 @@ procedure RunKMCOM(FMode: TKMShellMode; KeyboardFileNames: TStrings; FSilent, FF
end;
// TODO: #10038 Will add this as part of the background update state machine
// for now just verifing the download happens via -buc switch.
- RemoteUpdateCheck := TRemoteUpdateCheck.Create(False);
+ BUpdateSM := TBackgroundUpdate.Create(False);
try
if (FMode = fmBackgroundUpdateCheck) then
begin
- RemoteUpdateCheck.Run;
+ BUpdateSM.HandleCheck;
Exit;
end
+ else
+ begin
+ if BUpdateSM.HandleKmShell = 1 then
+ Exit;
+ end;
finally
- RemoteUpdateCheck.Free;
+ BUpdateSM.Free;
end;
diff --git a/windows/src/engine/inst/insthelper/Keyman.System.Install.EnginePostInstall.pas b/windows/src/engine/inst/insthelper/Keyman.System.Install.EnginePostInstall.pas
index bdf602ae056..5e61ff1f4d9 100644
--- a/windows/src/engine/inst/insthelper/Keyman.System.Install.EnginePostInstall.pas
+++ b/windows/src/engine/inst/insthelper/Keyman.System.Install.EnginePostInstall.pas
@@ -25,6 +25,40 @@ function ReportFailure(hInstall: MSIHANDLE; const func: string; code: UINT): UIN
Result := code;
end;
+
+function UpdateState: Boolean;
+var
+ UpdateStr : UnicodeString;
+ UpdatePBytes : PByte;
+ hk: Winapi.Windows.HKEY;
+ updateData: Cardinal;
+begin
+
+ Result := False;
+ UpdateStr := 'usWaitingPostInstall';
+ //KL.Log('SetBackgroundState State Entry');
+ if RegOpenKeyEx(HKEY_LOCAL_MACHINE, PChar(SRegKey_KeymanEngine_LM), 0, KEY_ALL_ACCESS, hk) = ERROR_SUCCESS then
+ begin
+ try
+ if RegSetValueEx(hk, PChar(SRegValue_Update_State), 0, REG_SZ, PWideChar(UpdateStr), Length(UpdateStr) * SizeOf(Char)) = ERROR_SUCCESS then
+ begin
+ Result := True;
+ end
+ else
+ begin
+ // error log
+ end;
+ finally
+ RegCloseKey(hk);
+ end;
+ end
+ else
+ begin
+ // couldn't open registry key
+ end;
+end;
+
+
{
Add permission for ALL APPLICATION PACKAGES to read %ProgramData%\Keyman folder
}
@@ -61,6 +95,8 @@ function EnginePostInstall(hInstall: MSIHANDLE): UINT;
end;
Result := ERROR_SUCCESS;
+ // TODO better error checking on the registry key update
+ UpdateState;
finally
if not CloseHandle(hFile) then
diff --git a/windows/src/engine/keyman/main.pas b/windows/src/engine/keyman/main.pas
index 223c1385582..9f1dbc9b3f0 100644
--- a/windows/src/engine/keyman/main.pas
+++ b/windows/src/engine/keyman/main.pas
@@ -46,6 +46,7 @@ implementation
RegistryKeys,
UfrmKeyman7Main,
UserMessages,
+ Klog,
Keyman.System.ExecuteHistory;
function ValidateParameters(var FCommand: Integer): Boolean; forward;
@@ -77,10 +78,11 @@ procedure RunProgram;
hMutex: Cardinal;
begin
-
+ KL.Log('Keyman RunProgram');
if not ValidateParameters(FCommand) then Exit;
// TODO set atom application running
+ KL.Log('Calling RecordKeymanStarted');
RecordKeymanStarted;
hProgramMutex := CreateMutex(nil, False, 'KeymanEXE70');
From 486586dc8d5339d25c120317344c1dd07211a2b1 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Fri, 22 Dec 2023 17:27:40 +1000
Subject: [PATCH 06/43] feat(windows): apply now install for state machine
The event handling needs to be added for all the states
---
windows/src/desktop/kmshell/main/UfrmMain.pas | 28 +++++++++++++++----
windows/src/desktop/kmshell/main/initprog.pas | 7 +++++
2 files changed, 30 insertions(+), 5 deletions(-)
diff --git a/windows/src/desktop/kmshell/main/UfrmMain.pas b/windows/src/desktop/kmshell/main/UfrmMain.pas
index a779fdcdd2b..5df7c2aa6a7 100644
--- a/windows/src/desktop/kmshell/main/UfrmMain.pas
+++ b/windows/src/desktop/kmshell/main/UfrmMain.pas
@@ -149,6 +149,7 @@ TfrmMain = class(TfrmWebContainer)
procedure DoApply;
procedure DoRefresh;
procedure Update_CheckNow;
+ procedure Update_ApplyNow;
protected
procedure FireCommand(const command: WideString; params: TStringList); override;
@@ -185,6 +186,7 @@ implementation
LanguagesXMLRenderer,
MessageIdentifierConsts,
MessageIdentifiers,
+ Keyman.System.RemoteUpdateCheck,
OnlineUpdateCheck,
OptionsXMLRenderer,
Keyman.Configuration.System.UmodWebHttpServer,
@@ -209,7 +211,8 @@ implementation
utilkmshell,
utilhttp,
utiluac,
- utilxml;
+ utilxml,
+ KeymanPaths;
type
PHKL = ^HKL;
@@ -349,6 +352,7 @@ procedure TfrmMain.FireCommand(const command: WideString; params: TStringList);
else if command = 'support_proxyconfig' then Support_ProxyConfig
else if command = 'update_checknow' then Update_CheckNow
+ else if command = 'update_applynow' then Update_ApplyNow
else if command = 'contact_support' then Support_ContactSupport(params) // I4390
@@ -793,7 +797,7 @@ procedure TfrmMain.Support_ProxyConfig;
Free;
end;
end;
-
+// TODO: #10210 Remove Update
procedure TfrmMain.Support_UpdateCheck;
begin
with TOnlineUpdateCheck.Create(Self, True, False) do
@@ -821,16 +825,30 @@ procedure TfrmMain.Support_UpdateCheck;
end;
procedure TfrmMain.Update_CheckNow;
+var UpdateCheck : TRemoteUpdateCheck;
begin
- with TOnlineUpdateCheck.Create(Self, True, True, True) do
+ UpdateCheck := TRemoteUpdateCheck.Create(True);
try
- Run;
+ UpdateCheck.Run;
finally
- Free;
+ UpdateCheck.Free;
end;
DoRefresh;
end;
+procedure TfrmMain.Update_ApplyNow;
+var
+ ShellPath, s: WideString;
+ FResult: Boolean;
+begin
+ ShellPath := TKeymanPaths.KeymanDesktopInstallPath(TKeymanPaths.S_KMShell);
+ FResult := TUtilExecute.Shell(0, ShellPath, '', '-an');
+ if not FResult then
+ KL.Log('TrmfMain: Executing Update_ApplyNow Failed');
+end;
+
+
+
procedure TfrmMain.TntFormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
inherited;
diff --git a/windows/src/desktop/kmshell/main/initprog.pas b/windows/src/desktop/kmshell/main/initprog.pas
index 94e47a32680..d4d43874cc3 100644
--- a/windows/src/desktop/kmshell/main/initprog.pas
+++ b/windows/src/desktop/kmshell/main/initprog.pas
@@ -83,6 +83,7 @@ procedure Main(Owner: TComponent = nil);
fmUpgradeKeyboards, fmOnlineUpdateCheck,// I2548
fmOnlineUpdateAdmin, fmTextEditor,
fmBackgroundUpdateCheck,
+ fmApplyInstallNow,
fmFirstRun, // I2562
fmKeyboardWelcome, // I2569
fmKeyboardPrint, // I2329
@@ -253,6 +254,7 @@ function Init(var FMode: TKMShellMode; KeyboardFileNames: TStrings; var FSilent,
else if s = '-t' then FMode := fmTextEditor
else if s = '-ouc' then FMode := fmOnlineUpdateCheck
else if s = '-buc' then FMode := fmBackgroundUpdateCheck
+ else if s = '-an' then FMode := fmApplyInstallNow
else if s = '-basekeyboard' then FMode := fmBaseKeyboard // I4169
else if s = '-nowelcome' then FNoWelcome := True
else if s = '-kw' then FMode := fmKeyboardWelcome // I2569
@@ -441,6 +443,11 @@ procedure RunKMCOM(FMode: TKMShellMode; KeyboardFileNames: TStrings; FSilent, FF
BUpdateSM.HandleCheck;
Exit;
end
+ else if (FMode = fmApplyInstallNow) then
+ begin
+ BUpdateSM.HandleInstall;
+ Exit;
+ end
else
begin
if BUpdateSM.HandleKmShell = 1 then
From 2eab485c76def8894c9d82c56ee5996a07d94271 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Wed, 10 Jan 2024 16:23:54 +1000
Subject: [PATCH 07/43] feat(windows): Add a installNow event to SM
Added an event for install now when the user wants to manual
start the install process straight away.
---
.../windows/delphi/general/RegistryKeys.pas | 1 +
.../desktop/kmshell/main/BackgroundUpdate.pas | 116 +++++++++++++++++-
2 files changed, 114 insertions(+), 3 deletions(-)
diff --git a/common/windows/delphi/general/RegistryKeys.pas b/common/windows/delphi/general/RegistryKeys.pas
index 7404a2af4fc..6de89094233 100644
--- a/common/windows/delphi/general/RegistryKeys.pas
+++ b/common/windows/delphi/general/RegistryKeys.pas
@@ -180,6 +180,7 @@ interface
SRegValue_Install_Update = 'install update';
SRegValue_Update_State = 'update state';
+ SRegValue_Install_Mode = 'install mode';
{ Privacy }
diff --git a/windows/src/desktop/kmshell/main/BackgroundUpdate.pas b/windows/src/desktop/kmshell/main/BackgroundUpdate.pas
index fe2eb22a6c4..a816987822d 100644
--- a/windows/src/desktop/kmshell/main/BackgroundUpdate.pas
+++ b/windows/src/desktop/kmshell/main/BackgroundUpdate.pas
@@ -98,6 +98,7 @@ TState = class abstract
procedure HandleInstall; virtual; abstract;
procedure HandleMSIInstallComplete; virtual; abstract;
procedure HandleAbort; virtual; abstract;
+ procedure HandleInstallNow; virtual; abstract;
// For convenience
function StateName: string; virtual; abstract;
@@ -111,10 +112,11 @@ IdleState = class(TState)
procedure Exit; override;
procedure HandleCheck; override;
procedure HandleDownload; override;
- function HandleKmShell : Integer; override;
+ function HandleKmShell : Integer; override;
procedure HandleInstall; override;
procedure HandleMSIInstallComplete; override;
procedure HandleAbort; override;
+ procedure HandleInstallNow; override;
function StateName: string; override;
end;
@@ -124,10 +126,11 @@ UpdateAvailableState = class(TState)
procedure Exit; override;
procedure HandleCheck; override;
procedure HandleDownload; override;
- function HandleKmShell : Integer; override;
+ function HandleKmShell : Integer; override;
procedure HandleInstall; override;
procedure HandleMSIInstallComplete; override;
procedure HandleAbort; override;
+ procedure HandleInstallNow; override;
function StateName: string; override;
end;
@@ -143,6 +146,7 @@ DownloadingState = class(TState)
procedure HandleInstall; override;
procedure HandleMSIInstallComplete; override;
procedure HandleAbort; override;
+ procedure HandleInstallNow; override;
function StateName: string; override;
end;
@@ -156,6 +160,7 @@ WaitingRestartState = class(TState)
procedure HandleInstall; override;
procedure HandleMSIInstallComplete; override;
procedure HandleAbort; override;
+ procedure HandleInstallNow; override;
function StateName: string; override;
end;
@@ -181,6 +186,7 @@ InstallingState = class(TState)
procedure HandleInstall; override;
procedure HandleMSIInstallComplete; override;
procedure HandleAbort; override;
+ procedure HandleInstallNow; override;
function StateName: string; override;
end;
@@ -194,6 +200,7 @@ RetryState = class(TState)
procedure HandleInstall; override;
procedure HandleMSIInstallComplete; override;
procedure HandleAbort; override;
+ procedure HandleInstallNow; override;
function StateName: string; override;
end;
@@ -207,6 +214,7 @@ WaitingPostInstallState = class(TState)
procedure HandleInstall; override;
procedure HandleMSIInstallComplete; override;
procedure HandleAbort; override;
+ procedure HandleInstallNow; override;
function StateName: string; override;
end;
@@ -254,6 +262,7 @@ TBackgroundUpdate = class
function checkUpdateSchedule : Boolean;
function SetRegistryState (Update : TUpdateState): Boolean;
+ function SetRegistryInstallMode (InstallMode : Boolean): Boolean;
protected
property State: TStateClass read GetState write SetState;
@@ -268,10 +277,12 @@ TBackgroundUpdate = class
procedure HandleInstall;
procedure HandleMSIInstallComplete;
procedure HandleAbort;
+ procedure HandleInstallNow;
function CurrentStateName: string;
property ShowErrors: Boolean read FShowErrors write FShowErrors;
function CheckRegistryState : TUpdateState;
+ function CheckRegistryInstallMode : Boolean;
end;
@@ -459,6 +470,57 @@ function TBackgroundUpdate.CheckRegistryState : TUpdateState; // I2329
Result := UpdateState;
end;
+function TBackgroundUpdate.SetRegistryInstallMode (InstallMode : Boolean): Boolean;
+var
+ InstallModeStr : string;
+begin
+
+ Result := False;
+ with TRegistryErrorControlled.Create do
+ try
+ RootKey := HKEY_LOCAL_MACHINE;
+ KL.Log('SetRegistryState State Entry');
+ if OpenKey(SRegKey_KeymanEngine_LM, True) then
+ begin
+ InstallModeStr := BoolToStr(InstallMode, True);
+ WriteString(SRegValue_Install_Mode, InstallModeStr);
+ KL.Log('SetRegistryInstallMode is:[' + InstallModeStr + ']');
+ end;
+ Result := True;
+ finally
+ Free;
+ end;
+
+end;
+
+function TBackgroundUpdate.CheckRegistryInstallMode : Boolean;
+var
+ InstallMode : Boolean;
+
+begin
+ // We will use a registry flag to maintain the install mode background/foreground
+
+ InstallMode := False;
+ // check the registry value
+ with TRegistryErrorControlled.Create do // I2890
+ try
+ RootKey := HKEY_LOCAL_MACHINE;
+ if OpenKeyReadOnly(SRegKey_KeymanEngine_LM) and ValueExists(SRegValue_Install_Mode) then
+ begin
+ InstallMode := StrToBool(ReadString(SRegValue_Install_Mode));
+ KL.Log('CheckRegistryState State is:[' + ReadString(SRegValue_Update_State) + ']');
+ end
+ else
+ begin
+ InstallMode := False; // default to background
+ KL.Log('CheckRegistryInstallMode reg value not found default:[ False ]');
+ end
+ finally
+ Free;
+ end;
+ Result := InstallMode;
+end;
+
//function TBackgroundUpdate.IsKeymanRunning: Boolean; // I2329
@@ -612,6 +674,11 @@ procedure TBackgroundUpdate.HandleAbort;
CurrentState.HandleAbort;
end;
+procedure TBackgroundUpdate.HandleInstallNow;
+begin
+ CurrentState.HandleInstallNow;
+end;
+
function TBackgroundUpdate.CurrentStateName: string;
begin
// Implement your logic here
@@ -699,6 +766,12 @@ procedure IdleState.HandleAbort;
// Implement your logic here
end;
+procedure IdleState.HandleInstallNow;
+begin
+ bucStateContext.SetRegistryInstallMode(True);
+ bucStateContext.CurrentState.HandleCheck;
+end;
+
function IdleState.StateName;
begin
// Implement your logic here
@@ -713,7 +786,7 @@ procedure UpdateAvailableState.Enter;
bucStateContext.SetRegistryState(usUpdateAvailable);
if bucStateContext.FAuto then
begin
- bucStateContext.CurrentState.HandleDownload ;
+ bucStateContext.CurrentState.HandleDownload;
end;
end;
@@ -756,6 +829,12 @@ procedure UpdateAvailableState.HandleAbort;
// Implement your logic here
end;
+procedure UpdateAvailableState.HandleInstallNow;
+begin
+ bucStateContext.SetRegistryInstallMode(True);
+ ChangeState(DownloadingState);
+end;
+
function UpdateAvailableState.StateName;
begin
// Implement your logic here
@@ -831,6 +910,12 @@ procedure DownloadingState.HandleAbort;
// Implement your logic here
end;
+procedure DownloadingState.HandleInstallNow;
+begin
+ bucStateContext.SetRegistryInstallMode(True);
+ // Continue downloading
+end;
+
function DownloadingState.StateName;
begin
// Implement your logic here
@@ -939,6 +1024,14 @@ procedure WaitingRestartState.HandleAbort;
// Implement your logic here
end;
+procedure WaitingRestartState.HandleInstallNow;
+begin
+ bucStateContext.SetRegistryInstallMode(True);
+ // Notify User to install
+ ChangeState(InstallingState);
+
+end;
+
function WaitingRestartState.StateName;
begin
// Implement your logic here
@@ -1080,6 +1173,11 @@ procedure InstallingState.HandleAbort;
ChangeState(IdleState);
end;
+procedure InstallingState.HandleInstallNow;
+begin
+ // Do Nothing. Need the UI to let user know installation in progress OR
+end;
+
function InstallingState.StateName;
begin
// Implement your logic here
@@ -1130,6 +1228,13 @@ procedure RetryState.HandleAbort;
// Implement your logic here
end;
+procedure RetryState.HandleInstallNow;
+begin
+ bucStateContext.SetRegistryInstallMode(True);
+ // TODO: #10038 handle retry counts
+ ChangeState(InstallingState);
+end;
+
function RetryState.StateName;
begin
// Implement your logic here
@@ -1198,6 +1303,11 @@ procedure WaitingPostInstallState.HandleAbort;
// Handle Abort
end;
+procedure WaitingPostInstallState.HandleInstallNow;
+begin
+ // Do nothing as files will be cleaned via HandleKmShell
+end;
+
function WaitingPostInstallState.StateName;
begin
// Implement your logic here
From 3dc3a250eca20009d550146430f607c5ec9844d2 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Wed, 10 Jan 2024 16:42:46 +1000
Subject: [PATCH 08/43] feat(windows): remove stub comments
---
.../desktop/kmshell/main/BackgroundUpdate.pas | 64 +++++++++----------
1 file changed, 30 insertions(+), 34 deletions(-)
diff --git a/windows/src/desktop/kmshell/main/BackgroundUpdate.pas b/windows/src/desktop/kmshell/main/BackgroundUpdate.pas
index fe2eb22a6c4..881ce6a7ebd 100644
--- a/windows/src/desktop/kmshell/main/BackgroundUpdate.pas
+++ b/windows/src/desktop/kmshell/main/BackgroundUpdate.pas
@@ -614,7 +614,6 @@ procedure TBackgroundUpdate.HandleAbort;
function TBackgroundUpdate.CurrentStateName: string;
begin
- // Implement your logic here
Result := CurrentState.StateName;
end;
@@ -675,33 +674,33 @@ procedure IdleState.HandleCheck;
procedure IdleState.HandleDownload;
begin
- // Implement your logic here
+
end;
function IdleState.HandleKmShell;
begin
- // Implement your logic here
+
Result := kmShellContinue;
end;
procedure IdleState.HandleInstall;
begin
- // Implement your logic here
+
end;
procedure IdleState.HandleMSIInstallComplete;
begin
- // Implement your logic here
+
end;
procedure IdleState.HandleAbort;
begin
- // Implement your logic here
+
end;
function IdleState.StateName;
begin
- // Implement your logic here
+
Result := 'IdleState';
end;
@@ -724,7 +723,7 @@ procedure UpdateAvailableState.Exit;
procedure UpdateAvailableState.HandleCheck;
begin
- // Implement your logic here
+
end;
procedure UpdateAvailableState.HandleDownload;
@@ -743,22 +742,22 @@ function UpdateAvailableState.HandleKmShell;
procedure UpdateAvailableState.HandleInstall;
begin
- // Implement your logic here
+
end;
procedure UpdateAvailableState.HandleMSIInstallComplete;
begin
- // Implement your logic here
+
end;
procedure UpdateAvailableState.HandleAbort;
begin
- // Implement your logic here
+
end;
function UpdateAvailableState.StateName;
begin
- // Implement your logic here
+
Result := 'UpdateAvailableState';
end;
@@ -817,23 +816,20 @@ function DownloadingState.HandleKmShell;
procedure DownloadingState.HandleInstall;
begin
- // Implement your logic here
ChangeState(InstallingState);
end;
procedure DownloadingState.HandleMSIInstallComplete;
begin
- // Implement your logic here
+
end;
procedure DownloadingState.HandleAbort;
begin
- // Implement your logic here
end;
function DownloadingState.StateName;
begin
- // Implement your logic here
Result := 'DownloadingState';
end;
@@ -879,12 +875,12 @@ procedure WaitingRestartState.Exit;
procedure WaitingRestartState.HandleCheck;
begin
- // Implement your logic here
+
end;
procedure WaitingRestartState.HandleDownload;
begin
- // Implement your logic here
+
end;
function WaitingRestartState.HandleKmShell;
@@ -926,22 +922,22 @@ function WaitingRestartState.HandleKmShell;
procedure WaitingRestartState.HandleInstall;
begin
- // Implement your logic here
+
end;
procedure WaitingRestartState.HandleMSIInstallComplete;
begin
- // Implement your logic here
+
end;
procedure WaitingRestartState.HandleAbort;
begin
- // Implement your logic here
+
end;
function WaitingRestartState.StateName;
begin
- // Implement your logic here
+
Result := 'WaitingRestartState';
end;
@@ -1049,12 +1045,12 @@ procedure InstallingState.Exit;
procedure InstallingState.HandleCheck;
begin
- // Implement your logic here
+
end;
procedure InstallingState.HandleDownload;
begin
- // Implement your logic here
+
end;
function InstallingState.HandleKmShell;
@@ -1067,12 +1063,12 @@ function InstallingState.HandleKmShell;
procedure InstallingState.HandleInstall;
begin
- // Implement your logic here
+
end;
procedure InstallingState.HandleMSIInstallComplete;
begin
- // Implement your logic here
+
end;
procedure InstallingState.HandleAbort;
@@ -1082,7 +1078,7 @@ procedure InstallingState.HandleAbort;
function InstallingState.StateName;
begin
- // Implement your logic here
+
Result := 'InstallingState';
end;
@@ -1101,12 +1097,12 @@ procedure RetryState.Exit;
procedure RetryState.HandleCheck;
begin
- // Implement your logic here
+
end;
procedure RetryState.HandleDownload;
begin
- // Implement your logic here
+
end;
function RetryState.HandleKmShell;
@@ -1117,22 +1113,22 @@ function RetryState.HandleKmShell;
procedure RetryState.HandleInstall;
begin
- // Implement your logic here
+
end;
procedure RetryState.HandleMSIInstallComplete;
begin
- // Implement your logic here
+
end;
procedure RetryState.HandleAbort;
begin
- // Implement your logic here
+
end;
function RetryState.StateName;
begin
- // Implement your logic here
+
Result := 'RetryState';
end;
@@ -1200,7 +1196,7 @@ procedure WaitingPostInstallState.HandleAbort;
function WaitingPostInstallState.StateName;
begin
- // Implement your logic here
+
Result := 'WaitingPostInstallState';
end;
From f7744f9a491101f3ec18a0bed17f73ac0abcb066 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Thu, 11 Jan 2024 10:41:56 +1000
Subject: [PATCH 09/43] feat(windows): mark outstanding issues with feature
---
.../general/Keyman.System.ExecuteHistory.pas | 10 ---
.../desktop/kmshell/main/BackgroundUpdate.pas | 89 ++++++++-----------
.../main/Keyman.System.DownloadUpdate.pas | 9 +-
3 files changed, 42 insertions(+), 66 deletions(-)
diff --git a/common/windows/delphi/general/Keyman.System.ExecuteHistory.pas b/common/windows/delphi/general/Keyman.System.ExecuteHistory.pas
index 97e9b2bfb1b..2ed824ec0c5 100644
--- a/common/windows/delphi/general/Keyman.System.ExecuteHistory.pas
+++ b/common/windows/delphi/general/Keyman.System.ExecuteHistory.pas
@@ -18,28 +18,18 @@ function RecordKeymanStarted : Boolean;
atom: WORD;
begin
Result := False;
- KL.Log('RecordKeymanStarted: Enter');
try
atom := GlobalFindAtom(AtomName);
- KL.Log('Keyman Session Flag atom is: '+IntToStr(atom));
if atom = 0 then
begin
- KL.Log('RecordKeymanStarted: if atom = 0');
if GetLastError <> ERROR_SUCCESS then
RaiseLastOSError;
- //writeln('The Sample Keyman Session Flag has not been set, so the process have never been started in this session.');
atom := GlobalAddAtom(AtomName);
KL.Log('RecordKeymanStarted: True');
Result := True;
if atom = 0 then
RaiseLastOSError;
end;
- //else
- //writeln('The process has been started previously because the Sample Keyman Session Flag has been set.');
-
- //writeln;
- //writeln('* The Sample Keyman Session Flag atom is: '+IntToStr(atom));
- //writeln;
except
on E: Exception do
KL.Log(E.ClassName + ': ' + E.Message);
diff --git a/windows/src/desktop/kmshell/main/BackgroundUpdate.pas b/windows/src/desktop/kmshell/main/BackgroundUpdate.pas
index 881ce6a7ebd..2e96b6528d0 100644
--- a/windows/src/desktop/kmshell/main/BackgroundUpdate.pas
+++ b/windows/src/desktop/kmshell/main/BackgroundUpdate.pas
@@ -94,7 +94,7 @@ TState = class abstract
procedure Exit; virtual; abstract;
procedure HandleCheck; virtual; abstract;
procedure HandleDownload; virtual; abstract;
- function HandleKmShell : Integer; virtual; abstract;
+ function HandleKmShell : Integer; virtual; abstract;
procedure HandleInstall; virtual; abstract;
procedure HandleMSIInstallComplete; virtual; abstract;
procedure HandleAbort; virtual; abstract;
@@ -111,11 +111,11 @@ IdleState = class(TState)
procedure Exit; override;
procedure HandleCheck; override;
procedure HandleDownload; override;
- function HandleKmShell : Integer; override;
+ function HandleKmShell : Integer; override;
procedure HandleInstall; override;
procedure HandleMSIInstallComplete; override;
procedure HandleAbort; override;
- function StateName: string; override;
+ function StateName: string; override;
end;
UpdateAvailableState = class(TState)
@@ -124,11 +124,11 @@ UpdateAvailableState = class(TState)
procedure Exit; override;
procedure HandleCheck; override;
procedure HandleDownload; override;
- function HandleKmShell : Integer; override;
+ function HandleKmShell : Integer; override;
procedure HandleInstall; override;
procedure HandleMSIInstallComplete; override;
procedure HandleAbort; override;
- function StateName: string; override;
+ function StateName: string; override;
end;
DownloadingState = class(TState)
@@ -143,7 +143,7 @@ DownloadingState = class(TState)
procedure HandleInstall; override;
procedure HandleMSIInstallComplete; override;
procedure HandleAbort; override;
- function StateName: string; override;
+ function StateName: string; override;
end;
WaitingRestartState = class(TState)
@@ -156,7 +156,7 @@ WaitingRestartState = class(TState)
procedure HandleInstall; override;
procedure HandleMSIInstallComplete; override;
procedure HandleAbort; override;
- function StateName: string; override;
+ function StateName: string; override;
end;
InstallingState = class(TState)
@@ -181,7 +181,7 @@ InstallingState = class(TState)
procedure HandleInstall; override;
procedure HandleMSIInstallComplete; override;
procedure HandleAbort; override;
- function StateName: string; override;
+ function StateName: string; override;
end;
RetryState = class(TState)
@@ -194,7 +194,7 @@ RetryState = class(TState)
procedure HandleInstall; override;
procedure HandleMSIInstallComplete; override;
procedure HandleAbort; override;
- function StateName: string; override;
+ function StateName: string; override;
end;
WaitingPostInstallState = class(TState)
@@ -207,7 +207,7 @@ WaitingPostInstallState = class(TState)
procedure HandleInstall; override;
procedure HandleMSIInstallComplete; override;
procedure HandleAbort; override;
- function StateName: string; override;
+ function StateName: string; override;
end;
{ This class also controls the state flow see }
@@ -250,7 +250,6 @@ TBackgroundUpdate = class
tempPath.
}
procedure SavePackageUpgradesToDownloadTempPath;
- //function IsKeymanRunning: Boolean;
function checkUpdateSchedule : Boolean;
function SetRegistryState (Update : TUpdateState): Boolean;
@@ -460,20 +459,6 @@ function TBackgroundUpdate.CheckRegistryState : TUpdateState; // I2329
end;
-
-//function TBackgroundUpdate.IsKeymanRunning: Boolean; // I2329
-//begin
-// try
-// Result := kmcom.Control.IsKeymanRunning;
-// except
-// on E:Exception do
-// begin
-// KL.Log(E.Message);
-// Exit(False);
-// end;
-// end;
-//end;
-
function TBackgroundUpdate.CheckUpdateSchedule: Boolean;
begin
try
@@ -529,7 +514,7 @@ procedure TBackgroundUpdate.SetState(const Value: TStateClass);
end
else
begin
- // TODO: Unable to set state for Value []
+ // TODO: #10210 Error log for Unable to set state for Value
end;
end;
@@ -577,7 +562,7 @@ function TBackgroundUpdate.ConvertEnumState(const TEnumState: TUpdateState) : TS
usRetry: Result := RetryState;
usWaitingPostInstall: Result := WaitingPostInstallState;
else
- // Log error unknown state setting to idle
+ // TODO: #10210 Log error unknown state setting to idle
Result := IdleState;
end;
end;
@@ -636,13 +621,12 @@ procedure TState.ChangeState(NewState: TStateClass);
procedure IdleState.Enter;
begin
// Enter UpdateAvailableState
- // register name
bucStateContext.SetRegistryState(usIdle);
end;
procedure IdleState.Exit;
begin
- // Exit UpdateAvailableState
+
end;
procedure IdleState.HandleCheck;
@@ -655,9 +639,11 @@ procedure IdleState.HandleCheck;
this all in the Idle HandleCheck message. But could be broken into an
seperate state of WaitngCheck RESP }
{ if Response not OK stay in the idle state and return }
- //CheckForUpdates := TRemoteUpdateCheck.Create(False);
+
+
// should be false but forcing check for testing
- CheckForUpdates := TRemoteUpdateCheck.Create(True);
+ //CheckForUpdates := TRemoteUpdateCheck.Create(True);
+ CheckForUpdates := TRemoteUpdateCheck.Create(False);
try
Result:= CheckForUpdates.Run;
finally
@@ -789,7 +775,7 @@ procedure DownloadingState.Exit;
procedure DownloadingState.HandleCheck;
begin
- // For now just pretend updated found
+
end;
procedure DownloadingState.HandleDownload;
@@ -805,13 +791,23 @@ function DownloadingState.HandleKmShell;
// TODO check if keyman is running then send to Waiting Restart
if DownloadResult then
begin
- ChangeState(InstallingState);
+ if HasKeymanRun then
+ begin
+ ChangeState(WaitingRestartState);
+ Result := kmShellContinue;
+ end
+ else
+ begin
+ ChangeState(InstallingState);
+ Result := kmShellExit;
+ end;
end
else
begin
ChangeState(RetryState);
+ Result := kmShellContinue;
end;
- Result := kmShellContinue;
+
end;
procedure DownloadingState.HandleInstall;
@@ -846,7 +842,7 @@ function DownloadingState.DownloadUpdatesBackground: Boolean;
DownloadResult := DownloadUpdate.DownloadUpdates;
KL.Log('TBackgroundUpdate.DownloadUpdatesBackground: DownloadResult = '+IntToStr(Ord(DownloadResult)));
Result := DownloadResult;
-// TODO: workout when we need to refresh kmcom keyboards
+// #TODO: #10210 workout when we need to refresh kmcom keyboards
// if Result in [ wucSuccess] then
// begin
@@ -963,6 +959,7 @@ procedure InstallingState.DoInstallKeyman;
s: string;
FResult: Boolean;
begin
+ FResult := False;
s := LowerCase(ExtractFileExt(bucStateContext.FParams.Keyman.SavePath));
if s = '.msi' then
FResult := TUtilExecute.Shell(0, 'msiexec.exe', '', '/qb /i "'+bucStateContext.FParams.Keyman.SavePath+'" AUTOLAUNCHPRODUCT=1') // I3349
@@ -1007,9 +1004,6 @@ procedure InstallingState.Enter;
fileNames: TStringDynArray;
begin
bucStateContext.SetRegistryState(usInstalling);
- // Needs to be desing discusion about the correct location for the cache
- //SavePath := IncludeTrailingPathDelimiter(GetFolderPath(CSIDL_COMMON_APPDATA) + SFolder_CachedUpdateFiles);
- // For testing
SavePath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
GetFileNamesInDirectory(SavePath, fileNames);
@@ -1021,18 +1015,15 @@ procedure InstallingState.Enter;
if fileExt = '.exe' then
break;
end;
- // ExecuteInstall(SavePath + ExtractFileName(fileName));
- // TODO DoInstallPackages ( this may need to be as state in the enum seperate
- // to installing the main keyman executable.
+
if DoInstallKeyman(SavePath + ExtractFileName(fileName)) then
begin
KL.Log('TBackgroundUpdate.InstallingState.Enter: DoInstall OK');
end
else
begin
- // TODO: clean failed download
- // TODO: Do we do a retry on install? probably not
- // install error log the error.
+ // TODO: #10210 clean failed download
+ // TODO: #10210 Do we do a retry on install? probably not
KL.Log('TBackgroundUpdate.InstallingState.Enter: DoInstall fail');
ChangeState(IdleState);
end
@@ -1107,7 +1098,7 @@ procedure RetryState.HandleDownload;
function RetryState.HandleKmShell;
begin
- // TODO Implement retry
+ // #TODO: #10210 Implement retry
Result := kmShellContinue
end;
@@ -1157,8 +1148,8 @@ procedure WaitingPostInstallState.HandleDownload;
function WaitingPostInstallState.HandleKmShell;
begin
- // TODO maybe have a counter if we get called in this state
- // to many time we need
+ // TODO: #10210 have a counter if we get called in this state
+ // too many time abort.
HandleMSIInstallComplete;
Result := kmShellContinue;
end;
@@ -1174,10 +1165,6 @@ procedure WaitingPostInstallState.HandleMSIInstallComplete;
FileNames: TStringDynArray;
begin
KL.Log('WaitingPostInstallState.HandleMSIInstallComplete');
- // TODO Remove cached files. Do any loging updating of files etc and then set back to idle
- //SavePath := IncludeTrailingPathDelimiter(GetFolderPath(CSIDL_COMMON_APPDATA) + SFolder_CachedUpdateFiles);
- /// For testing using local user area cache
- //SavePath := 'C:\Projects\rcswag\testCache';
SavePath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
KL.Log('WaitingPostInstallState.HandleMSIInstallComplete remove SavePath:'+ SavePath);
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
index bee733e4b7d..f095341c822 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
@@ -89,7 +89,7 @@ implementation
System.StrUtils;
// temp wrapper for converting showmessage to logs don't know where
- // if nt using klog
+ // if not using klog
procedure LogMessage(LogMessage: string);
begin
KL.Log(LogMessage);
@@ -194,7 +194,8 @@ procedure TDownloadUpdate.DoDownloadUpdates(SavePath: string; Params: TUpdateChe
// Keyman Installer
if not DownloadFile(Params.InstallURL, SavePath + Params.FileName) then // I2742
begin
- // TODO: #10210record fail? and log // Download failed but user wants to install other files
+ // TODO: #10210 convert to error log.
+ LogMessage('DoDownloadUpdates Failed to download' + Params.InstallURL);
end
else
begin
@@ -213,7 +214,6 @@ function TDownloadUpdate.DownloadUpdates: Boolean;
DownloadResult : Boolean;
ucr: TUpdateCheckResponse;
begin
- // DownloadBackGroundSavePath := IncludeTrailingPathDelimiter(GetFolderPath(CSIDL_COMMON_APPDATA) + SFolder_CachedUpdateFiles);
DownloadBackGroundSavePath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
if TUpdateCheckStorage.LoadUpdateCacheData(ucr) then
begin
@@ -235,7 +235,6 @@ function TDownloadUpdate.CheckAllFilesDownloaded: Boolean;
FileNames : TStringDynArray;
begin
- // DownloadBackGroundSavePath := IncludeTrailingPathDelimiter(GetFolderPath(CSIDL_COMMON_APPDATA) + SFolder_CachedUpdateFiles);
SavedPath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
GetFileNamesInDirectory(SavedPath, FileNames);
if Length(FileNames) = 0 then
@@ -266,7 +265,7 @@ function TDownloadUpdate.CheckAllFilesDownloaded: Boolean;
Result := False;
Exit;
end;
- // TODO verify filesizes match so we know we don't have partical downloades.
+ // TODO verify filesizes match so we know we don't have partial downloades.
Result := True;
end
else
From e29e212eafbed4c61bc59a9ca3e4aafe2bd3318f Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Wed, 24 Jan 2024 07:58:45 +1000
Subject: [PATCH 10/43] feat(windows): WIP - leave commit so work not lost
---
.../desktop/kmshell/main/UfrmStartInstall.dfm | 16 ++++++++
.../desktop/kmshell/main/UfrmStartInstall.pas | 38 +++++++++++++++++++
windows/src/desktop/kmshell/xml/strings.xml | 10 +++++
3 files changed, 64 insertions(+)
create mode 100644 windows/src/desktop/kmshell/main/UfrmStartInstall.dfm
create mode 100644 windows/src/desktop/kmshell/main/UfrmStartInstall.pas
diff --git a/windows/src/desktop/kmshell/main/UfrmStartInstall.dfm b/windows/src/desktop/kmshell/main/UfrmStartInstall.dfm
new file mode 100644
index 00000000000..4414a07c101
--- /dev/null
+++ b/windows/src/desktop/kmshell/main/UfrmStartInstall.dfm
@@ -0,0 +1,16 @@
+object frmStartInstall: TfrmStartInstall
+ Left = 0
+ Top = 0
+ Caption = 'frmStartInstall'
+ ClientHeight = 299
+ ClientWidth = 635
+ Color = clBtnFace
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -11
+ Font.Name = 'Tahoma'
+ Font.Style = []
+ OldCreateOrder = False
+ PixelsPerInch = 96
+ TextHeight = 13
+end
diff --git a/windows/src/desktop/kmshell/main/UfrmStartInstall.pas b/windows/src/desktop/kmshell/main/UfrmStartInstall.pas
new file mode 100644
index 00000000000..dce20b6ccef
--- /dev/null
+++ b/windows/src/desktop/kmshell/main/UfrmStartInstall.pas
@@ -0,0 +1,38 @@
+unit UfrmStartInstall;
+
+interface
+
+uses
+
+ Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
+ Dialogs, UserMessages, StdCtrls, ExtCtrls, UfrmKeymanBase;
+
+type
+ TfrmStartInstall = class(TfrmKeymanBase)
+ LabelMessage: TLabel;
+ InstallButton: TButton;
+ CancelButton: TButton;
+ procedure InstallButtonClick(Sender: TObject);
+ procedure CancelButtonClick(Sender: TObject);
+ private
+ public
+ end;
+
+var
+ frmStartInstall: TfrmStartInstall;
+
+implementation
+
+{$R *.dfm}
+
+procedure TfrmStartInstall.InstallButtonClick(Sender: TObject);
+begin
+ ModalResult := mrOk;
+end;
+
+procedure TfrmStartInstall.CancelButtonClick(Sender: TObject);
+begin
+ ModalResult := mrCancel;
+end;
+
+end.
diff --git a/windows/src/desktop/kmshell/xml/strings.xml b/windows/src/desktop/kmshell/xml/strings.xml
index 2fa2307a339..c4cd8d6f222 100644
--- a/windows/src/desktop/kmshell/xml/strings.xml
+++ b/windows/src/desktop/kmshell/xml/strings.xml
@@ -788,6 +788,16 @@ keyboard that you use in Windows. Keyman keyboards will adapt automatically to
+
+
+
+ Apply update now
+
+
+
+
+ Check for new updates
+
From db18917a631d8a4bbfce6f2f08e0a9397b27fdff Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Wed, 24 Jan 2024 08:00:10 +1000
Subject: [PATCH 11/43] feat(windows): project file listing updated
---
windows/src/desktop/kmshell/kmshell.dpr | 5 ++++-
windows/src/desktop/kmshell/kmshell.dproj | 16 ++++++++++------
2 files changed, 14 insertions(+), 7 deletions(-)
diff --git a/windows/src/desktop/kmshell/kmshell.dpr b/windows/src/desktop/kmshell/kmshell.dpr
index 5eb0ee9013c..eb4cbaa4c2e 100644
--- a/windows/src/desktop/kmshell/kmshell.dpr
+++ b/windows/src/desktop/kmshell/kmshell.dpr
@@ -182,7 +182,8 @@ uses
Keyman.System.RemoteUpdateCheck in 'main\Keyman.System.RemoteUpdateCheck.pas',
BackgroundUpdate in 'main\BackgroundUpdate.pas',
Keyman.System.DownloadUpdate in 'main\Keyman.System.DownloadUpdate.pas',
- Keyman.System.ExecuteHistory in '..\..\..\..\common\windows\delphi\general\Keyman.System.ExecuteHistory.pas';
+ Keyman.System.ExecuteHistory in '..\..\..\..\common\windows\delphi\general\Keyman.System.ExecuteHistory.pas',
+ UfrmStartInstall in 'main\UfrmStartInstall.pas' {Form1};
{$R VERSION.RES}
{$R manifest.res}
@@ -204,6 +205,8 @@ begin
Application.Initialize;
Application.Title := 'Keyman Configuration';
Application.CreateForm(TmodWebHttpServer, modWebHttpServer);
+ Application.CreateForm(TForm1, Form1);
+ Application.CreateForm(TForm1, Form1);
try
Run;
finally
diff --git a/windows/src/desktop/kmshell/kmshell.dproj b/windows/src/desktop/kmshell/kmshell.dproj
index 159a3fe88d2..64a6b83be17 100644
--- a/windows/src/desktop/kmshell/kmshell.dproj
+++ b/windows/src/desktop/kmshell/kmshell.dproj
@@ -359,6 +359,10 @@
+
+
+ dfm
+
Cfg_2
@@ -420,12 +424,6 @@
False
-
-
- kmshell.rsm
- true
-
-
kmshell.exe
@@ -438,6 +436,12 @@
true
+
+
+ kmshell.rsm
+ true
+
+
1
From 14310a118626fcfa70a02ba1449a7144c37febeb Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Tue, 2 Jul 2024 15:39:45 +1000
Subject: [PATCH 12/43] feat(windows): rename files to new delphi pattern
---
windows/src/desktop/kmshell/kmshell.dpr | 2 +-
windows/src/desktop/kmshell/kmshell.dproj | 12 ++++++------
.../kmshell/main/BackgroundUpdateStateDiagram.md | 11 +++++++++++
...BackgroundUpdate.pas => Keyman.System.Update.pas} | 2 +-
windows/src/desktop/kmshell/main/initprog.pas | 2 +-
5 files changed, 20 insertions(+), 9 deletions(-)
create mode 100644 windows/src/desktop/kmshell/main/BackgroundUpdateStateDiagram.md
rename windows/src/desktop/kmshell/main/{BackgroundUpdate.pas => Keyman.System.Update.pas} (99%)
diff --git a/windows/src/desktop/kmshell/kmshell.dpr b/windows/src/desktop/kmshell/kmshell.dpr
index 5d19e25509b..1f1f618ec6c 100644
--- a/windows/src/desktop/kmshell/kmshell.dpr
+++ b/windows/src/desktop/kmshell/kmshell.dpr
@@ -181,7 +181,7 @@ uses
UpdateXMLRenderer in 'render\UpdateXMLRenderer.pas',
Keyman.System.UpdateCheckStorage in 'main\Keyman.System.UpdateCheckStorage.pas',
Keyman.System.RemoteUpdateCheck in 'main\Keyman.System.RemoteUpdateCheck.pas',
- BackgroundUpdate in 'main\BackgroundUpdate.pas',
+ Keyman.System.Update in 'main\Keyman.System.Update.pas',
Keyman.System.DownloadUpdate in 'main\Keyman.System.DownloadUpdate.pas',
Keyman.System.ExecuteHistory in '..\..\..\..\common\windows\delphi\general\Keyman.System.ExecuteHistory.pas',
UfrmStartInstall in 'main\UfrmStartInstall.pas' {Form1};
diff --git a/windows/src/desktop/kmshell/kmshell.dproj b/windows/src/desktop/kmshell/kmshell.dproj
index d1e73fb3bd8..83a0827efb3 100644
--- a/windows/src/desktop/kmshell/kmshell.dproj
+++ b/windows/src/desktop/kmshell/kmshell.dproj
@@ -357,7 +357,7 @@
-
+
@@ -425,21 +425,21 @@
False
-
+
kmshell.exe
true
-
+
- kmshell.exe
+ kmshell.rsm
true
-
+
- kmshell.rsm
+ kmshell.exe
true
diff --git a/windows/src/desktop/kmshell/main/BackgroundUpdateStateDiagram.md b/windows/src/desktop/kmshell/main/BackgroundUpdateStateDiagram.md
new file mode 100644
index 00000000000..59c735eeb2a
--- /dev/null
+++ b/windows/src/desktop/kmshell/main/BackgroundUpdateStateDiagram.md
@@ -0,0 +1,11 @@
+``` mermaid
+stateDiagram
+ [*] --> Idle
+ Idle --> UpdateAvailable
+ UpdateAvailable --> Downloading
+ Downloading --> Installing
+ Downloading --> WaitingRestart
+ WaitingRestart --> Installing
+ Installing --> WaitingPostInstall
+ WaitingPostInstall --> Idle
+```
diff --git a/windows/src/desktop/kmshell/main/BackgroundUpdate.pas b/windows/src/desktop/kmshell/main/Keyman.System.Update.pas
similarity index 99%
rename from windows/src/desktop/kmshell/main/BackgroundUpdate.pas
rename to windows/src/desktop/kmshell/main/Keyman.System.Update.pas
index eafa8194ffe..fb878347830 100644
--- a/windows/src/desktop/kmshell/main/BackgroundUpdate.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.Update.pas
@@ -15,7 +15,7 @@
Notes: For the state diagram in mermaid ../BackgroundUpdateStateDiagram.md
History:
*)
-unit BackgroundUpdate;
+unit Keyman.System.Update;
interface
diff --git a/windows/src/desktop/kmshell/main/initprog.pas b/windows/src/desktop/kmshell/main/initprog.pas
index d4d43874cc3..763cf11f990 100644
--- a/windows/src/desktop/kmshell/main/initprog.pas
+++ b/windows/src/desktop/kmshell/main/initprog.pas
@@ -144,7 +144,7 @@ implementation
UpgradeMnemonicLayout,
utilfocusappwnd,
utilkmshell,
- BackgroundUpdate,
+ Keyman.System.Update,
KeyboardTIPCheck,
From a9fd916c8ac6d345ccce9468a64140b734aada56 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Thu, 18 Jul 2024 09:58:22 +1000
Subject: [PATCH 13/43] feat(windows): small refactor of sm wip
---
windows/src/desktop/kmshell/kmshell.dpr | 2 +-
windows/src/desktop/kmshell/kmshell.dproj | 12 +-
.../main/Keyman.System.UpdateStateMachine.pas | 1302 +++++++++++++++++
windows/src/desktop/kmshell/main/initprog.pas | 6 +-
4 files changed, 1312 insertions(+), 10 deletions(-)
create mode 100644 windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
diff --git a/windows/src/desktop/kmshell/kmshell.dpr b/windows/src/desktop/kmshell/kmshell.dpr
index 1f1f618ec6c..805a79aa58a 100644
--- a/windows/src/desktop/kmshell/kmshell.dpr
+++ b/windows/src/desktop/kmshell/kmshell.dpr
@@ -181,7 +181,7 @@ uses
UpdateXMLRenderer in 'render\UpdateXMLRenderer.pas',
Keyman.System.UpdateCheckStorage in 'main\Keyman.System.UpdateCheckStorage.pas',
Keyman.System.RemoteUpdateCheck in 'main\Keyman.System.RemoteUpdateCheck.pas',
- Keyman.System.Update in 'main\Keyman.System.Update.pas',
+ Keyman.System.UpdateStateMachine in 'main\Keyman.System.UpdateStateMachine.pas',
Keyman.System.DownloadUpdate in 'main\Keyman.System.DownloadUpdate.pas',
Keyman.System.ExecuteHistory in '..\..\..\..\common\windows\delphi\general\Keyman.System.ExecuteHistory.pas',
UfrmStartInstall in 'main\UfrmStartInstall.pas' {Form1};
diff --git a/windows/src/desktop/kmshell/kmshell.dproj b/windows/src/desktop/kmshell/kmshell.dproj
index 83a0827efb3..c999a12d422 100644
--- a/windows/src/desktop/kmshell/kmshell.dproj
+++ b/windows/src/desktop/kmshell/kmshell.dproj
@@ -357,7 +357,7 @@
-
+
@@ -425,19 +425,19 @@
False
-
+
- kmshell.exe
+ kmshell.rsm
true
-
+
- kmshell.rsm
+ kmshell.exe
true
-
+
kmshell.exe
true
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
new file mode 100644
index 00000000000..9ae3df31956
--- /dev/null
+++ b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
@@ -0,0 +1,1302 @@
+(*
+ Name: UpdateStateMachine
+ Copyright: Copyright (C) SIL International.
+ Documentation:
+ Description:
+ Create Date: 2 Nov 2023
+
+ Modified Date: 2 Nov 2023
+ Authors: rcruickshank
+ Related Files:
+ Dependencies:
+
+ Bugs:
+ Todo:
+ Notes: For the state diagram in mermaid ../BackgroundUpdateStateDiagram.md
+ History:
+*)
+unit Keyman.System.UpdateStateMachine;
+
+interface
+
+uses
+ System.Classes,
+ System.SysUtils,
+ System.UITypes,
+ System.IOUtils,
+ System.Types,
+ Vcl.Forms,
+ TypInfo,
+ KeymanPaths,
+ utilkmshell,
+
+ httpuploader,
+ Keyman.System.UpdateCheckResponse,
+ Keyman.System.ExecuteHistory,
+ UfrmDownloadProgress;
+
+type
+ EUpdateStateMachine = class(Exception);
+
+ TUpdateStateMachineResult = (oucUnknown, oucShutDown, oucSuccess, oucNoUpdates, oucUpdatesAvailable, oucFailure, oucOffline);
+
+ TUpdateState = (usIdle, usUpdateAvailable, usDownloading, usWaitingRestart, usInstalling, usRetry, usPostInstall);
+
+ { Keyboard Package Params }
+ TUpdateStateMachineParamsPackage = record
+ ID: string;
+ NewID: string;
+ Description: string;
+ OldVersion, NewVersion: string;
+ DownloadURL: string;
+ SavePath: string;
+ FileName: string;
+ DownloadSize: Integer;
+ Install: Boolean;
+ end;
+ { Main Keyman Program }
+ TUpdateStateMachineParamsKeyman = record
+ OldVersion, NewVersion: string;
+ DownloadURL: string;
+ SavePath: string;
+ FileName: string;
+ DownloadSize: Integer;
+ Install: Boolean;
+ end;
+
+ TUpdateStateMachineParams = record
+ Keyman: TUpdateStateMachineParamsKeyman;
+ Packages: array of TUpdateStateMachineParamsPackage;
+ Result: TUpdateStateMachineResult;
+ end;
+
+ TUpdateStateMachineDownloadParams = record
+ Owner: TfrmDownloadProgress;
+ TotalSize: Integer;
+ TotalDownloads: Integer;
+ StartPosition: Integer;
+ end;
+
+ // Forward declaration
+ TUpdateStateMachine = class;
+ { State Classes Update }
+
+ TStateClass = class of TState;
+
+ TState = class abstract
+ private
+ bucStateContext: TUpdateStateMachine;
+ procedure ChangeState(newState: TStateClass);
+
+ public
+ constructor Create(Context: TUpdateStateMachine);
+ procedure Enter; virtual; abstract;
+ procedure Exit; virtual; abstract;
+ procedure HandleCheck; virtual; abstract;
+ procedure HandleDownload; virtual; abstract;
+ function HandleKmShell : Integer; virtual; abstract;
+ procedure HandleInstall; virtual; abstract;
+ procedure HandleMSIInstallComplete; virtual; abstract;
+ procedure HandleAbort; virtual; abstract;
+ procedure HandleInstallNow; virtual; abstract;
+
+ // For convenience
+ function StateName: string; virtual; abstract;
+
+ end;
+
+ // Derived classes for each state
+ IdleState = class(TState)
+ public
+ procedure Enter; override;
+ procedure Exit; override;
+ procedure HandleCheck; override;
+ procedure HandleDownload; override;
+ function HandleKmShell : Integer; override;
+ procedure HandleInstall; override;
+ procedure HandleMSIInstallComplete; override;
+ procedure HandleAbort; override;
+ procedure HandleInstallNow; override;
+ function StateName: string; override;
+ end;
+
+ UpdateAvailableState = class(TState)
+ public
+ procedure Enter; override;
+ procedure Exit; override;
+ procedure HandleCheck; override;
+ procedure HandleDownload; override;
+ function HandleKmShell : Integer; override;
+ procedure HandleInstall; override;
+ procedure HandleMSIInstallComplete; override;
+ procedure HandleAbort; override;
+ procedure HandleInstallNow; override;
+ function StateName: string; override;
+ end;
+
+ DownloadingState = class(TState)
+ private
+
+ function DownloadUpdatesBackground: Boolean;
+ procedure Enter; override;
+ procedure Exit; override;
+ procedure HandleCheck; override;
+ procedure HandleDownload; override;
+ function HandleKmShell : Integer; override;
+ procedure HandleInstall; override;
+ procedure HandleMSIInstallComplete; override;
+ procedure HandleAbort; override;
+ procedure HandleInstallNow; override;
+ function StateName: string; override;
+ end;
+
+ WaitingRestartState = class(TState)
+ public
+ procedure Enter; override;
+ procedure Exit; override;
+ procedure HandleCheck; override;
+ procedure HandleDownload; override;
+ function HandleKmShell : Integer; override;
+ procedure HandleInstall; override;
+ procedure HandleMSIInstallComplete; override;
+ procedure HandleAbort; override;
+ procedure HandleInstallNow; override;
+ function StateName: string; override;
+ end;
+
+ InstallingState = class(TState)
+ private
+ procedure DoInstallKeyman; overload;
+ function DoInstallKeyman(SavePath: string) : Boolean; overload;
+ {
+ Installs the Keyman file using either msiexec.exe or the setup launched in
+ a separate shell.
+
+ @params Package The package to be installed.
+
+ @returns True if the installation is successful, False otherwise.
+ }
+ function DoInstallPackage(Package: TUpdateStateMachineParamsPackage): Boolean;
+ public
+ procedure Enter; override;
+ procedure Exit; override;
+ procedure HandleCheck; override;
+ procedure HandleDownload; override;
+ function HandleKmShell : Integer; override;
+ procedure HandleInstall; override;
+ procedure HandleMSIInstallComplete; override;
+ procedure HandleAbort; override;
+ procedure HandleInstallNow; override;
+ function StateName: string; override;
+ end;
+
+ RetryState = class(TState)
+ public
+ procedure Enter; override;
+ procedure Exit; override;
+ procedure HandleCheck; override;
+ procedure HandleDownload; override;
+ function HandleKmShell : Integer; override;
+ procedure HandleInstall; override;
+ procedure HandleMSIInstallComplete; override;
+ procedure HandleAbort; override;
+ procedure HandleInstallNow; override;
+ function StateName: string; override;
+ end;
+
+ PostInstallState = class(TState)
+ public
+ procedure Enter; override;
+ procedure Exit; override;
+ procedure HandleCheck; override;
+ procedure HandleDownload; override;
+ function HandleKmShell : Integer; override;
+ procedure HandleInstall; override;
+ procedure HandleMSIInstallComplete; override;
+ procedure HandleAbort; override;
+ procedure HandleInstallNow; override;
+ function StateName: string; override;
+ end;
+
+ { This class also controls the state flow see }
+ TUpdateStateMachine = class
+ private
+ FForce: Boolean;
+ FAuto: Boolean;
+ FParams: TUpdateStateMachineParams;
+
+ FErrorMessage: string;
+
+ DownloadTempPath: string;
+
+ FShowErrors: Boolean;
+
+
+
+ FDownload: TUpdateStateMachineDownloadParams;
+
+ CurrentState: TState;
+ // State object for performance (could lazy create?)
+ FIdle: IdleState;
+ FUpdateAvailable: UpdateAvailableState;
+ FDownloading: DownloadingState;
+ FWaitingRestart: WaitingRestartState;
+ FInstalling: InstallingState;
+ FRetry: RetryState;
+ FPostInstall: PostInstallState;
+ function GetState: TStateClass;
+ procedure SetState(const Value: TStateClass);
+ procedure SetStateOnly(const Value: TStateClass);
+ function ConvertEnumState(const TEnumState: TUpdateState): TStateClass;
+
+ procedure ShutDown;
+
+ {
+ SavePackageUpgradesToDownloadTempPath saves any new package IDs to a
+ single file in the download tempPath. This procedure saves the IDs of any
+ new packages to a file named "upgrade_packages.inf" in the download
+ tempPath.
+ }
+ procedure SavePackageUpgradesToDownloadTempPath;
+ function checkUpdateSchedule : Boolean;
+
+ function SetRegistryState (Update : TUpdateState): Boolean;
+ function SetRegistryInstallMode (InstallMode : Boolean): Boolean;
+
+ protected
+ property State: TStateClass read GetState write SetState;
+
+ public
+ constructor Create(AForce: Boolean);
+ destructor Destroy; override;
+
+ procedure HandleCheck;
+ function HandleKmShell : Integer;
+ procedure HandleDownload;
+ procedure HandleInstall;
+ procedure HandleMSIInstallComplete;
+ procedure HandleAbort;
+ procedure HandleInstallNow;
+ function CurrentStateName: string;
+
+ property ShowErrors: Boolean read FShowErrors write FShowErrors;
+ function CheckRegistryState : TUpdateState;
+ function CheckRegistryInstallMode : Boolean;
+
+ end;
+
+ IOnlineUpdateSharedData = interface
+ ['{7442A323-C1E3-404B-BEEA-5B24A52BBB0E}']
+ function Params: TUpdateStateMachineParams;
+ end;
+
+ TOnlineUpdateSharedData = class(TInterfacedObject, IOnlineUpdateSharedData)
+ private
+ FParams: TUpdateStateMachineParams;
+ public
+ constructor Create(AParams: TUpdateStateMachineParams);
+ function Params: TUpdateStateMachineParams;
+ end;
+
+implementation
+
+uses
+ Winapi.Shlobj,
+ System.WideStrUtils,
+ Vcl.Dialogs,
+ Winapi.ShellApi,
+ Winapi.Windows,
+ Winapi.WinINet,
+
+ GlobalProxySettings,
+ KLog,
+ keymanapi_TLB,
+ KeymanVersion,
+ kmint,
+ ErrorControlledRegistry,
+ RegistryKeys,
+ Upload_Settings,
+ utildir,
+ utilexecute,
+ OnlineUpdateCheckMessages, // todo create own messages
+ UfrmOnlineUpdateIcon,
+ UfrmOnlineUpdateNewVersion,
+ utilsystem,
+ utiluac,
+ versioninfo,
+ Keyman.System.RemoteUpdateCheck,
+ Keyman.System.DownloadUpdate;
+
+const
+ SPackageUpgradeFilename = 'upgrade_packages.inf';
+ kmShellContinue = 0;
+ kmShellExit = 1;
+
+{ TUpdateStateMachine }
+
+constructor TUpdateStateMachine.Create(AForce : Boolean);
+var TSerailsedState : TUpdateState;
+begin
+ inherited Create;
+
+
+ FShowErrors := True;
+ FParams.Result := oucUnknown;
+
+ FForce := AForce;
+ FAuto := True; // Default to automatically check, download, and install
+ FIdle := IdleState.Create(Self);
+ FUpdateAvailable := UpdateAvailableState.Create(Self);
+ FDownloading := DownloadingState.Create(Self);
+ FWaitingRestart := WaitingRestartState.Create(Self);
+ FInstalling := InstallingState.Create(Self);
+ FRetry := RetryState.Create(Self);
+ FPostInstall := PostInstallState.Create(Self);
+ // Check the Registry setting.
+ SetStateOnly(ConvertEnumState(CheckRegistryState));
+ KL.Log('TUpdateStateMachine.Create');
+end;
+
+destructor TUpdateStateMachine.Destroy;
+begin
+ if (FErrorMessage <> '') and FShowErrors then
+ KL.Log(FErrorMessage);
+
+ if FParams.Result = oucShutDown then
+ ShutDown;
+
+ FIdle.Free;
+ FUpdateAvailable.Free;
+ FDownloading.Free;
+ FWaitingRestart.Free;
+ FInstalling.Free;
+ FRetry.Free;
+ FPostInstall.Free;
+
+ KL.Log('TUpdateStateMachine.Destroy: FErrorMessage = '+FErrorMessage);
+ KL.Log('TUpdateStateMachine.Destroy: FParams.Result = '+IntToStr(Ord(FParams.Result)));
+
+ inherited Destroy;
+end;
+
+
+procedure TUpdateStateMachine.SavePackageUpgradesToDownloadTempPath;
+var
+ i: Integer;
+begin
+ with TStringList.Create do
+ try
+ for i := 0 to High(FParams.Packages) do
+ if FParams.Packages[i].NewID <> '' then
+ Add(FParams.Packages[i].NewID+'='+FParams.Packages[i].ID);
+ if Count > 0 then
+ SaveToFile(DownloadTempPath + SPackageUpgradeFileName);
+ finally
+ Free;
+ end;
+end;
+
+procedure TUpdateStateMachine.ShutDown;
+begin
+ if Assigned(Application) then
+ Application.Terminate;
+end;
+
+
+{ TOnlineUpdateSharedData }
+
+constructor TOnlineUpdateSharedData.Create(AParams: TUpdateStateMachineParams);
+begin
+ inherited Create;
+ FParams := AParams;
+end;
+
+function TOnlineUpdateSharedData.Params: TUpdateStateMachineParams;
+begin
+ Result := FParams;
+end;
+
+
+function TUpdateStateMachine.SetRegistryState(Update : TUpdateState): Boolean;
+var
+ UpdateStr : string;
+begin
+
+ Result := False;
+ with TRegistryErrorControlled.Create do
+ try
+ RootKey := HKEY_LOCAL_MACHINE;
+ KL.Log('SetRegistryState State Entry');
+ if OpenKey(SRegKey_KeymanEngine_LM, True) then
+ begin
+ UpdateStr := GetEnumName(TypeInfo(TUpdateState), Ord(Update));
+ WriteString(SRegValue_Update_State, UpdateStr);
+ KL.Log('SetRegistryState State is:[' + UpdateStr + ']');
+ end;
+ Result := True;
+ finally
+ Free;
+ end;
+
+end;
+
+
+function TUpdateStateMachine.CheckRegistryState : TUpdateState; // I2329
+var
+ UpdateState : TUpdateState;
+
+begin
+ // We will use a registry flag to maintain the state of the background update
+
+ UpdateState := usIdle; // do we need a unknown state ?
+ // check the registry value
+ with TRegistryErrorControlled.Create do // I2890
+ try
+ RootKey := HKEY_LOCAL_MACHINE;
+ if OpenKeyReadOnly(SRegKey_KeymanEngine_LM) and ValueExists(SRegValue_Update_State) then
+ begin
+ UpdateState := TUpdateState(GetEnumValue(TypeInfo(TUpdateState), ReadString(SRegValue_Update_State)));
+ KL.Log('CheckRegistryState State is:[' + ReadString(SRegValue_Update_State) + ']');
+ end
+ else
+ begin
+ UpdateState := usIdle; // do we need a unknown state ?
+ KL.Log('CheckRegistryState State reg value not found default:[' + ReadString(SRegValue_Update_State) + ']');
+ end
+ finally
+ Free;
+ end;
+ Result := UpdateState;
+end;
+
+function TUpdateStateMachine.SetRegistryInstallMode (InstallMode : Boolean): Boolean;
+var
+ InstallModeStr : string;
+begin
+
+ Result := False;
+ with TRegistryErrorControlled.Create do
+ try
+ RootKey := HKEY_LOCAL_MACHINE;
+ KL.Log('SetRegistryState State Entry');
+ if OpenKey(SRegKey_KeymanEngine_LM, True) then
+ begin
+ InstallModeStr := BoolToStr(InstallMode, True);
+ WriteString(SRegValue_Install_Mode, InstallModeStr);
+ KL.Log('SetRegistryInstallMode is:[' + InstallModeStr + ']');
+ end;
+ Result := True;
+ finally
+ Free;
+ end;
+
+end;
+
+function TUpdateStateMachine.CheckRegistryInstallMode : Boolean;
+var
+ InstallMode : Boolean;
+
+begin
+ // We will use a registry flag to maintain the install mode background/foreground
+
+ InstallMode := False;
+ // check the registry value
+ with TRegistryErrorControlled.Create do // I2890
+ try
+ RootKey := HKEY_LOCAL_MACHINE;
+ if OpenKeyReadOnly(SRegKey_KeymanEngine_LM) and ValueExists(SRegValue_Install_Mode) then
+ begin
+ InstallMode := StrToBool(ReadString(SRegValue_Install_Mode));
+ KL.Log('CheckRegistryState State is:[' + ReadString(SRegValue_Update_State) + ']');
+ end
+ else
+ begin
+ InstallMode := False; // default to background
+ KL.Log('CheckRegistryInstallMode reg value not found default:[ False ]');
+ end
+ finally
+ Free;
+ end;
+ Result := InstallMode;
+end;
+
+
+function TUpdateStateMachine.CheckUpdateSchedule: Boolean;
+begin
+ try
+ Result := False;
+ with TRegistryErrorControlled.Create do
+ try
+ if OpenKeyReadOnly(SRegKey_KeymanDesktop_CU) then
+ begin
+ if ValueExists(SRegValue_CheckForUpdates) and not ReadBool(SRegValue_CheckForUpdates) and not FForce then
+ begin
+ Result := False;
+ Exit;
+ end;
+ if ValueExists(SRegValue_LastUpdateCheckTime) and (Now - ReadDateTime(SRegValue_LastUpdateCheckTime) < 1) and not FForce then
+ begin
+ Result := False;
+ Exit;
+ end;
+ // Else Time to check for updates
+ Result := True;
+ end;
+ finally
+ Free;
+ end;
+ except
+ { we will not run the check if an error occurs reading the settings }
+ on E:Exception do
+ begin
+ Result := False;
+ FErrorMessage := E.Message;
+ Exit;
+ end;
+ end;
+end;
+
+function TUpdateStateMachine.GetState: TStateClass;
+begin
+ Result := TStateClass(CurrentState.ClassType);
+end;
+
+procedure TUpdateStateMachine.SetState(const Value: TStateClass);
+begin
+ if Assigned(CurrentState) then
+ begin
+ CurrentState.Exit;
+ end;
+
+ SetStateOnly(Value);
+
+ if Assigned(CurrentState) then
+ begin
+ CurrentState.Enter;
+ end
+ else
+ begin
+ // TODO: #10210 Error log for Unable to set state for Value
+ end;
+
+end;
+
+procedure TUpdateStateMachine.SetStateOnly(const Value: TStateClass);
+begin
+ if Value = IdleState then
+ begin
+ CurrentState := FIdle;
+ end
+ else if Value = UpdateAvailableState then
+ begin
+ CurrentState := FUpdateAvailable;
+ end
+ else if Value = DownloadingState then
+ begin
+ CurrentState := FDownloading;
+ end
+ else if Value = WaitingRestartState then
+ begin
+ CurrentState := FWaitingRestart;
+ end
+ else if Value = InstallingState then
+ begin
+ CurrentState := FInstalling;
+ end
+ else if Value = RetryState then
+ begin
+ CurrentState := FRetry;
+ end
+ else if Value = PostInstallState then
+ begin
+ CurrentState := FPostInstall;
+ end;
+end;
+
+function TUpdateStateMachine.ConvertEnumState(const TEnumState: TUpdateState) : TStateClass;
+begin
+ case TEnumState of
+ usIdle: Result := IdleState;
+ usUpdateAvailable: Result := UpdateAvailableState;
+ usDownloading: Result := DownloadingState;
+ usWaitingRestart: Result := WaitingRestartState;
+ usInstalling: Result := InstallingState;
+ usRetry: Result := RetryState;
+ usPostInstall: Result := PostInstallState;
+ else
+ // TODO: #10210 Log error unknown state setting to idle
+ Result := IdleState;
+ end;
+end;
+
+procedure TUpdateStateMachine.HandleCheck;
+begin
+ CurrentState.HandleCheck;
+end;
+
+function TUpdateStateMachine.HandleKmShell;
+begin
+ Result := CurrentState.HandleKmShell;
+end;
+
+procedure TUpdateStateMachine.HandleDownload;
+begin
+ CurrentState.HandleDownload;
+end;
+
+procedure TUpdateStateMachine.HandleInstall;
+begin
+ CurrentState.HandleInstall;
+end;
+
+procedure TUpdateStateMachine.HandleMSIInstallComplete;
+begin
+ CurrentState.HandleMSIInstallComplete;
+end;
+
+procedure TUpdateStateMachine.HandleAbort;
+begin
+ CurrentState.HandleAbort;
+end;
+
+procedure TUpdateStateMachine.HandleInstallNow;
+begin
+ CurrentState.HandleInstallNow;
+end;
+
+function TUpdateStateMachine.CurrentStateName: string;
+begin
+ Result := CurrentState.StateName;
+end;
+
+
+
+{ State Class Memebers }
+constructor TState.Create(Context: TUpdateStateMachine);
+begin
+ bucStateContext := Context;
+end;
+
+procedure TState.ChangeState(NewState: TStateClass);
+begin
+ bucStateContext.State := NewState;
+end;
+
+
+{ IdleState }
+
+procedure IdleState.Enter;
+begin
+ // Enter UpdateAvailableState
+ bucStateContext.SetRegistryState(usIdle);
+end;
+
+procedure IdleState.Exit;
+begin
+
+end;
+
+procedure IdleState.HandleCheck;
+var
+ CheckForUpdates: TRemoteUpdateCheck;
+ Result : TRemoteUpdateCheckResult;
+begin
+
+ { Make a HTTP request out and see if updates are available for now do
+ this all in the Idle HandleCheck message. But could be broken into an
+ seperate state of WaitngCheck RESP }
+ { if Response not OK stay in the idle state and return }
+
+
+ // should be false but forcing check for testing
+ //CheckForUpdates := TRemoteUpdateCheck.Create(True);
+ CheckForUpdates := TRemoteUpdateCheck.Create(False);
+ try
+ Result:= CheckForUpdates.Run;
+ finally
+ CheckForUpdates.Free;
+ end;
+
+ { Response OK and Update is available }
+ if Result = wucSuccess then
+ begin
+ ChangeState(UpdateAvailableState);
+ end;
+ // else staty in idle state
+end;
+
+procedure IdleState.HandleDownload;
+begin
+
+end;
+
+function IdleState.HandleKmShell;
+begin
+
+ Result := kmShellContinue;
+end;
+
+procedure IdleState.HandleInstall;
+begin
+
+end;
+
+procedure IdleState.HandleMSIInstallComplete;
+begin
+
+end;
+
+procedure IdleState.HandleAbort;
+begin
+
+end;
+
+procedure IdleState.HandleInstallNow;
+begin
+ bucStateContext.SetRegistryInstallMode(True);
+ bucStateContext.CurrentState.HandleCheck;
+end;
+
+function IdleState.StateName;
+begin
+
+ Result := 'IdleState';
+end;
+
+{ UpdateAvailableState }
+
+procedure UpdateAvailableState.Enter;
+begin
+ // Enter UpdateAvailableState
+ bucStateContext.SetRegistryState(usUpdateAvailable);
+ if bucStateContext.FAuto then
+ begin
+ bucStateContext.CurrentState.HandleDownload;
+ end;
+end;
+
+procedure UpdateAvailableState.Exit;
+begin
+ // Exit UpdateAvailableState
+end;
+
+procedure UpdateAvailableState.HandleCheck;
+begin
+
+end;
+
+procedure UpdateAvailableState.HandleDownload;
+begin
+ ChangeState(DownloadingState);
+end;
+
+function UpdateAvailableState.HandleKmShell;
+begin
+ if bucStateContext.FAuto then
+ begin
+ bucStateContext.CurrentState.HandleDownload ;
+ end;
+ Result := kmShellContinue;
+end;
+
+procedure UpdateAvailableState.HandleInstall;
+begin
+
+end;
+
+procedure UpdateAvailableState.HandleMSIInstallComplete;
+begin
+
+end;
+
+procedure UpdateAvailableState.HandleAbort;
+begin
+
+end;
+
+procedure UpdateAvailableState.HandleInstallNow;
+begin
+ bucStateContext.SetRegistryInstallMode(True);
+ ChangeState(DownloadingState);
+end;
+
+function UpdateAvailableState.StateName;
+begin
+
+ Result := 'UpdateAvailableState';
+end;
+
+{ DownloadingState }
+
+procedure DownloadingState.Enter;
+var DownloadResult : Boolean;
+begin
+ // Enter DownloadingState
+ bucStateContext.SetRegistryState(usDownloading);
+ DownloadResult := DownloadUpdatesBackground;
+ if DownloadResult then
+ begin
+ if HasKeymanRun then
+ ChangeState(WaitingRestartState)
+ else
+ ChangeState(InstallingState);
+ end
+ else
+ begin
+ ChangeState(RetryState);
+ end
+end;
+
+procedure DownloadingState.Exit;
+begin
+ // Exit DownloadingState
+end;
+
+procedure DownloadingState.HandleCheck;
+begin
+
+end;
+
+procedure DownloadingState.HandleDownload;
+var DownloadResult : Boolean;
+begin
+ // We are already downloading do nothing
+end;
+
+function DownloadingState.HandleKmShell;
+var DownloadResult : Boolean;
+begin
+ DownloadResult := DownloadUpdatesBackground;
+ // TODO check if keyman is running then send to Waiting Restart
+ if DownloadResult then
+ begin
+ if HasKeymanRun then
+ begin
+ ChangeState(WaitingRestartState);
+ Result := kmShellContinue;
+ end
+ else
+ begin
+ ChangeState(InstallingState);
+ Result := kmShellExit;
+ end;
+ end
+ else
+ begin
+ ChangeState(RetryState);
+ Result := kmShellContinue;
+ end;
+
+end;
+
+procedure DownloadingState.HandleInstall;
+begin
+ ChangeState(InstallingState);
+end;
+
+procedure DownloadingState.HandleMSIInstallComplete;
+begin
+
+end;
+
+procedure DownloadingState.HandleAbort;
+begin
+end;
+
+procedure DownloadingState.HandleInstallNow;
+begin
+ bucStateContext.SetRegistryInstallMode(True);
+ // Continue downloading
+end;
+
+function DownloadingState.StateName;
+begin
+ Result := 'DownloadingState';
+end;
+
+
+function DownloadingState.DownloadUpdatesBackground: Boolean;
+var
+ i: Integer;
+ DownloadBackGroundSavePath : String;
+ DownloadResult : Boolean;
+ DownloadUpdate: TDownloadUpdate;
+begin
+ DownloadUpdate := TDownloadUpdate.Create;
+ try
+ DownloadResult := DownloadUpdate.DownloadUpdates;
+ KL.Log('TUpdateStateMachine.DownloadUpdatesBackground: DownloadResult = '+IntToStr(Ord(DownloadResult)));
+ Result := DownloadResult;
+// #TODO: #10210 workout when we need to refresh kmcom keyboards
+
+// if Result in [ wucSuccess] then
+// begin
+// kmcom.Keyboards.Refresh;
+// kmcom.Keyboards.Apply;
+// kmcom.Packages.Refresh;
+// end;
+
+ finally
+ DownloadUpdate.Free;
+ end;
+end;
+
+{ WaitingRestartState }
+
+procedure WaitingRestartState.Enter;
+begin
+ // Enter DownloadingState
+ bucStateContext.SetRegistryState(usWaitingRestart);
+end;
+
+procedure WaitingRestartState.Exit;
+begin
+ // Exit DownloadingState
+end;
+
+procedure WaitingRestartState.HandleCheck;
+begin
+
+end;
+
+procedure WaitingRestartState.HandleDownload;
+begin
+
+end;
+
+function WaitingRestartState.HandleKmShell;
+var
+ SavedPath : String;
+ Filenames : TStringDynArray;
+begin
+ KL.Log('WaitingRestartState.HandleKmShell Enter');
+ // Still can't go if keyman has run
+ if HasKeymanRun then
+ begin
+ KL.Log('WaitingRestartState.HandleKmShell Keyman Has Run');
+ Result := kmShellExit;
+ // Exit; // Exit is not wokring for some reason.
+ // this else is only here because the exit is not working.
+ end
+ else
+ begin
+ // Check downloaded cache if available then
+ SavedPath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
+ GetFileNamesInDirectory(SavedPath, FileNames);
+ if Length(FileNames) = 0 then
+ begin
+ KL.Log('WaitingRestartState.HandleKmShell No Files in Download Cache');
+ // Return to Idle state and check for Updates state
+ ChangeState(IdleState);
+ bucStateContext.CurrentState.HandleCheck;
+ Result := kmShellExit;
+ // Exit; // again exit was not working
+ end
+ else
+ begin
+ KL.Log('WaitingRestartState.HandleKmShell is good to install');
+ ChangeState(InstallingState);
+ Result := kmShellExit;
+ end;
+ end;
+end;
+
+procedure WaitingRestartState.HandleInstall;
+begin
+
+end;
+
+procedure WaitingRestartState.HandleMSIInstallComplete;
+begin
+
+end;
+
+procedure WaitingRestartState.HandleAbort;
+begin
+
+end;
+
+procedure WaitingRestartState.HandleInstallNow;
+begin
+ bucStateContext.SetRegistryInstallMode(True);
+ // Notify User to install
+ ChangeState(InstallingState);
+
+end;
+
+function WaitingRestartState.StateName;
+begin
+
+ Result := 'WaitingRestartState';
+end;
+
+{ InstallingState }
+
+function InstallingState.DoInstallPackage(Package: TUpdateStateMachineParamsPackage): Boolean;
+var
+ FPackage: IKeymanPackageFile2;
+begin
+ Result := True;
+
+ FPackage := kmcom.Packages.GetPackageFromFile(Package.SavePath) as IKeymanPackageFile2;
+ FPackage.Install2(True); // Force overwrites existing package and leaves most settings for it intact
+ FPackage := nil;
+
+ kmcom.Refresh;
+ kmcom.Apply;
+ System.SysUtils.DeleteFile(Package.SavePath);
+end;
+
+procedure InstallingState.DoInstallKeyman;
+var
+ s: string;
+ FResult: Boolean;
+begin
+ FResult := False;
+ s := LowerCase(ExtractFileExt(bucStateContext.FParams.Keyman.SavePath));
+ if s = '.msi' then
+ FResult := TUtilExecute.Shell(0, 'msiexec.exe', '', '/qb /i "'+bucStateContext.FParams.Keyman.SavePath+'" AUTOLAUNCHPRODUCT=1') // I3349
+ else if s = '.exe' then
+ FResult := TUtilExecute.Shell(0, bucStateContext.FParams.Keyman.SavePath, '', '-au') // I3349
+ else
+ Exit;
+ if not FResult then
+ ShowMessage(SysErrorMessage(GetLastError));
+end;
+
+function InstallingState.DoInstallKeyman(SavePath: string) : Boolean;
+var
+ s: string;
+ FResult: Boolean;
+begin
+ s := LowerCase(ExtractFileExt(SavePath));
+ if s = '.msi' then
+ FResult := TUtilExecute.Shell(0, 'msiexec.exe', '', '/qb /i "'+SavePath+'" AUTOLAUNCHPRODUCT=1') // I3349
+ else if s = '.exe' then
+ begin
+ KL.Log('TUpdateStateMachine.InstallingState.DoInstallKeyman SavePath:"'+ SavePath+'"');
+ FResult := TUtilExecute.Shell(0, SavePath, '', '-au') // I3349
+ end
+ else
+ FResult := False;
+
+ if not FResult then
+ begin
+ KL.Log('TUpdateStateMachine.InstallingState.DoInstall: Result = '+IntToStr(Ord(FResult)));
+ // Log messageShowMessage(SysErrorMessage(GetLastError));
+ end;
+
+ Result := FResult;
+end;
+
+procedure InstallingState.Enter;
+var
+ SavePath: String;
+ fileExt : String;
+ fileName: String;
+ fileNames: TStringDynArray;
+begin
+ bucStateContext.SetRegistryState(usInstalling);
+ SavePath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
+
+ GetFileNamesInDirectory(SavePath, fileNames);
+ // for now we only want the exe although excute install can
+ // handle msi
+ for fileName in fileNames do
+ begin
+ fileExt := LowerCase(ExtractFileExt(fileName));
+ if fileExt = '.exe' then
+ break;
+ end;
+
+ if DoInstallKeyman(SavePath + ExtractFileName(fileName)) then
+ begin
+ KL.Log('TUpdateStateMachine.InstallingState.Enter: DoInstall OK');
+ end
+ else
+ begin
+ // TODO: #10210 clean failed download
+ // TODO: #10210 Do we do a retry on install? probably not
+ KL.Log('TUpdateStateMachine.InstallingState.Enter: DoInstall fail');
+ ChangeState(IdleState);
+ end
+end;
+
+procedure InstallingState.Exit;
+begin
+ // Exit DownloadingState
+end;
+
+procedure InstallingState.HandleCheck;
+begin
+
+end;
+
+procedure InstallingState.HandleDownload;
+begin
+
+end;
+
+function InstallingState.HandleKmShell;
+begin
+ // Result = exit straight away as we are installing (MSI installer)
+ // need to just do a no-op keyman will it maybe using kmshell to install
+ // packages.
+ Result := kmShellContinue;
+end;
+
+procedure InstallingState.HandleInstall;
+begin
+
+end;
+
+procedure InstallingState.HandleMSIInstallComplete;
+begin
+
+end;
+
+procedure InstallingState.HandleAbort;
+begin
+ ChangeState(IdleState);
+end;
+
+procedure InstallingState.HandleInstallNow;
+begin
+ // Do Nothing. Need the UI to let user know installation in progress OR
+end;
+
+function InstallingState.StateName;
+begin
+
+ Result := 'InstallingState';
+end;
+
+{ RetryState }
+
+procedure RetryState.Enter;
+begin
+ // Enter DownloadingState
+ bucStateContext.SetRegistryState(usRetry);
+end;
+
+procedure RetryState.Exit;
+begin
+ // Exit DownloadingState
+end;
+
+procedure RetryState.HandleCheck;
+begin
+
+end;
+
+procedure RetryState.HandleDownload;
+begin
+
+end;
+
+function RetryState.HandleKmShell;
+begin
+ // #TODO: #10210 Implement retry
+ Result := kmShellContinue
+end;
+
+procedure RetryState.HandleInstall;
+begin
+
+end;
+
+procedure RetryState.HandleMSIInstallComplete;
+begin
+
+end;
+
+procedure RetryState.HandleAbort;
+begin
+
+end;
+
+procedure RetryState.HandleInstallNow;
+begin
+ bucStateContext.SetRegistryInstallMode(True);
+ // TODO: #10038 handle retry counts
+ ChangeState(InstallingState);
+end;
+
+function RetryState.StateName;
+begin
+
+ Result := 'RetryState';
+end;
+
+{ PostInstallState }
+
+procedure PostInstallState.Enter;
+begin
+ // Enter downloading state
+ bucStateContext.SetRegistryState(usPostInstall);
+end;
+
+procedure PostInstallState.Exit;
+begin
+ // Exit downloading state
+end;
+
+procedure PostInstallState.HandleCheck;
+begin
+ // Handle Check
+end;
+
+procedure PostInstallState.HandleDownload;
+begin
+ // Handle Download
+end;
+
+function PostInstallState.HandleKmShell;
+begin
+ // TODO: #10210 have a counter if we get called in this state
+ // too many time abort.
+ HandleMSIInstallComplete;
+ Result := kmShellContinue;
+end;
+
+procedure PostInstallState.HandleInstall;
+begin
+ // Handle Install
+end;
+
+procedure PostInstallState.HandleMSIInstallComplete;
+var SavePath: string;
+ FileName: String;
+ FileNames: TStringDynArray;
+begin
+ KL.Log('PostInstallState.HandleMSIInstallComplete');
+ SavePath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
+ KL.Log('PostInstallState.HandleMSIInstallComplete remove SavePath:'+ SavePath);
+
+ GetFileNamesInDirectory(SavePath, FileNames);
+ for FileName in FileNames do
+ begin
+ System.SysUtils.DeleteFile(FileName);
+ end;
+ ChangeState(IdleState);
+end;
+
+procedure PostInstallState.HandleAbort;
+begin
+ // Handle Abort
+end;
+
+procedure PostInstallState.HandleInstallNow;
+begin
+ // Do nothing as files will be cleaned via HandleKmShell
+end;
+
+function PostInstallState.StateName;
+begin
+
+ Result := 'PostInstallState';
+end;
+
+
+
+end.
diff --git a/windows/src/desktop/kmshell/main/initprog.pas b/windows/src/desktop/kmshell/main/initprog.pas
index 763cf11f990..284695bfd48 100644
--- a/windows/src/desktop/kmshell/main/initprog.pas
+++ b/windows/src/desktop/kmshell/main/initprog.pas
@@ -144,7 +144,7 @@ implementation
UpgradeMnemonicLayout,
utilfocusappwnd,
utilkmshell,
- Keyman.System.Update,
+ Keyman.System.UpdateStateMachine,
KeyboardTIPCheck,
@@ -387,7 +387,7 @@ procedure RunKMCOM(FMode: TKMShellMode; KeyboardFileNames: TStrings; FSilent, FF
kdl: IKeymanDefaultLanguage;
FIcon: string;
FMutex: TKeymanMutex; // I2720
- BUpdateSM : TBackgroundUpdate;
+ BUpdateSM : TUpdateStateMachine;
function FirstKeyboardFileName: WideString;
begin
if KeyboardFileNames.Count = 0
@@ -436,7 +436,7 @@ procedure RunKMCOM(FMode: TKMShellMode; KeyboardFileNames: TStrings; FSilent, FF
end;
// TODO: #10038 Will add this as part of the background update state machine
// for now just verifing the download happens via -buc switch.
- BUpdateSM := TBackgroundUpdate.Create(False);
+ BUpdateSM := TUpdateStateMachine.Create(False);
try
if (FMode = fmBackgroundUpdateCheck) then
begin
From d6258e5135d76d8501ee20b389d30a2dd20884ed Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Tue, 23 Jul 2024 16:52:35 +1000
Subject: [PATCH 14/43] feat(windows): add checkconfig for updated download
---
.../main/Keyman.System.UpdateStateMachine.pas | 105 +++++++++++++-----
...eyman.System.Install.EnginePostInstall.pas | 2 +-
2 files changed, 80 insertions(+), 27 deletions(-)
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
index 9ae3df31956..6d6eb9b4265 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
@@ -297,7 +297,8 @@ TOnlineUpdateSharedData = class(TInterfacedObject, IOnlineUpdateSharedData)
constructor Create(AParams: TUpdateStateMachineParams);
function Params: TUpdateStateMachineParams;
end;
-
+ // Private Utility functions
+ function ConfigCheckContinue: Boolean;
implementation
uses
@@ -335,7 +336,7 @@ implementation
{ TUpdateStateMachine }
constructor TUpdateStateMachine.Create(AForce : Boolean);
-var TSerailsedState : TUpdateState;
+// var TSerailsedState : TUpdateState; // TODO: Remove
begin
inherited Create;
@@ -448,7 +449,6 @@ function TUpdateStateMachine.CheckRegistryState : TUpdateState; // I2329
begin
// We will use a registry flag to maintain the state of the background update
- UpdateState := usIdle; // do we need a unknown state ?
// check the registry value
with TRegistryErrorControlled.Create do // I2890
try
@@ -702,15 +702,15 @@ procedure IdleState.HandleCheck;
Result : TRemoteUpdateCheckResult;
begin
+
{ Make a HTTP request out and see if updates are available for now do
this all in the Idle HandleCheck message. But could be broken into an
seperate state of WaitngCheck RESP }
{ if Response not OK stay in the idle state and return }
- // should be false but forcing check for testing
- //CheckForUpdates := TRemoteUpdateCheck.Create(True);
- CheckForUpdates := TRemoteUpdateCheck.Create(False);
+ // If handle check event force check
+ CheckForUpdates := TRemoteUpdateCheck.Create(True);
try
Result:= CheckForUpdates.Run;
finally
@@ -731,8 +731,26 @@ procedure IdleState.HandleDownload;
end;
function IdleState.HandleKmShell;
+var
+ CheckForUpdates: TRemoteUpdateCheck;
+ UpdateCheckResult : TRemoteUpdateCheckResult;
+const CheckPeriod: Integer = 7; // Days between checking for updates
begin
-
+ // Check if auto updates enable and if scheduled time has expired
+ if ConfigCheckContinue then
+ begin
+ CheckForUpdates := TRemoteUpdateCheck.Create(True);
+ try
+ UpdateCheckResult:= CheckForUpdates.Run;
+ finally
+ CheckForUpdates.Free;
+ end;
+ { Response OK and Update is available }
+ if UpdateCheckResult = wucSuccess then
+ begin
+ ChangeState(UpdateAvailableState);
+ end;
+ end;
Result := kmShellContinue;
end;
@@ -755,6 +773,7 @@ procedure IdleState.HandleInstallNow;
begin
bucStateContext.SetRegistryInstallMode(True);
bucStateContext.CurrentState.HandleCheck;
+ // TODO: How do we notify the command line no update available
end;
function IdleState.StateName;
@@ -771,7 +790,7 @@ procedure UpdateAvailableState.Enter;
bucStateContext.SetRegistryState(usUpdateAvailable);
if bucStateContext.FAuto then
begin
- bucStateContext.CurrentState.HandleDownload;
+ ChangeState(DownloadingState);
end;
end;
@@ -787,14 +806,14 @@ procedure UpdateAvailableState.HandleCheck;
procedure UpdateAvailableState.HandleDownload;
begin
- ChangeState(DownloadingState);
+
end;
function UpdateAvailableState.HandleKmShell;
begin
if bucStateContext.FAuto then
begin
- bucStateContext.CurrentState.HandleDownload ;
+ ChangeState(DownloadingState);;
end;
Result := kmShellContinue;
end;
@@ -822,29 +841,22 @@ procedure UpdateAvailableState.HandleInstallNow;
function UpdateAvailableState.StateName;
begin
-
Result := 'UpdateAvailableState';
end;
{ DownloadingState }
procedure DownloadingState.Enter;
-var DownloadResult : Boolean;
+var DownloadResult, FResult : Boolean;
+RootPath: string;
begin
// Enter DownloadingState
bucStateContext.SetRegistryState(usDownloading);
- DownloadResult := DownloadUpdatesBackground;
- if DownloadResult then
- begin
- if HasKeymanRun then
- ChangeState(WaitingRestartState)
- else
- ChangeState(InstallingState);
- end
- else
- begin
- ChangeState(RetryState);
- end
+ // call seperate process
+ RootPath := ExtractFilePath(ParamStr(0));
+ FResult := TUtilExecute.ShellCurrentUser(0, ParamStr(0), IncludeTrailingPathDelimiter(RootPath), '');
+ if not FResult then
+ KL.Log('TrmfMain: Executing KMshell for download updated Failed');
end;
procedure DownloadingState.Exit;
@@ -917,7 +929,6 @@ function DownloadingState.StateName;
function DownloadingState.DownloadUpdatesBackground: Boolean;
var
- i: Integer;
DownloadBackGroundSavePath : String;
DownloadResult : Boolean;
DownloadUpdate: TDownloadUpdate;
@@ -988,7 +999,7 @@ function WaitingRestartState.HandleKmShell;
KL.Log('WaitingRestartState.HandleKmShell No Files in Download Cache');
// Return to Idle state and check for Updates state
ChangeState(IdleState);
- bucStateContext.CurrentState.HandleCheck;
+ bucStateContext.CurrentState.HandleCheck; // TODO no event here
Result := kmShellExit;
// Exit; // again exit was not working
end
@@ -1075,6 +1086,8 @@ function InstallingState.DoInstallKeyman(SavePath: string) : Boolean;
else if s = '.exe' then
begin
KL.Log('TUpdateStateMachine.InstallingState.DoInstallKeyman SavePath:"'+ SavePath+'"');
+ // switch -au for auto update in silent mode.
+ // We will need to add the pop up that says install update now yes/no
FResult := TUtilExecute.Shell(0, SavePath, '', '-au') // I3349
end
else
@@ -1297,6 +1310,46 @@ function PostInstallState.StateName;
Result := 'PostInstallState';
end;
+// Private Functions:
+function ConfigCheckContinue: Boolean;
+var
+ registry: TRegistryErrorControlled;
+begin
+{ Verify that it has been at least CheckPeriod days since last update check }
+ Result := False;
+ try
+ registry := TRegistryErrorControlled.Create; // I2890
+ try
+ if registry.OpenKeyReadOnly(SRegKey_KeymanDesktop_CU) then
+ begin
+ if registry.ValueExists(SRegValue_CheckForUpdates) and not registry.ReadBool(SRegValue_CheckForUpdates) then
+ begin
+ Result := False;
+ Exit;
+ end;
+ if registry.ValueExists(SRegValue_LastUpdateCheckTime) and (Now - registry.ReadDateTime(SRegValue_LastUpdateCheckTime) > CheckPeriod) then
+ begin
+ Result := True;
+ end
+ else
+ begin
+ Result := False;
+ end;
+ Exit;
+ end;
+ finally
+ registry.Free;
+ end;
+ except
+ { we will not run the check if an error occurs reading the settings }
+ on E:Exception do
+ begin
+ Result := False;
+ LogMessage(E.Message);
+ Exit;
+ end;
+ end;
+end;
end.
diff --git a/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas b/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas
index 5e61ff1f4d9..77fe071786d 100644
--- a/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas
+++ b/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas
@@ -35,7 +35,7 @@ function UpdateState: Boolean;
begin
Result := False;
- UpdateStr := 'usWaitingPostInstall';
+ UpdateStr := 'usPostInstall';
//KL.Log('SetBackgroundState State Entry');
if RegOpenKeyEx(HKEY_LOCAL_MACHINE, PChar(SRegKey_KeymanEngine_LM), 0, KEY_ALL_ACCESS, hk) = ERROR_SUCCESS then
begin
From 9f72febff844ef9f4c3c9830c72b306365e35073 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Wed, 24 Jul 2024 16:59:21 +1000
Subject: [PATCH 15/43] feat(windows): merge conflicts wip
---
VERSION.md | 2 +-
common/windows/delphi/general/klog.pas | 2 +-
core/tests/unit/kmx/fixtures/meson.build | 4 ++--
core/tests/unit/meson.build | 6 +++---
windows/src/desktop/insthelp/insthelp.dpr | 7 ++++++-
windows/src/desktop/insthelp/insthelp.dproj | 5 +++++
windows/src/desktop/kmshell/kmshell.dproj | 9 +++++----
windows/src/desktop/kmshell/kmshell.res | Bin 7036 -> 7036 bytes
.../kmshell/util/UfrmDownloadProgress.pas | 14 +++++++-------
windows/src/desktop/kmshell/xml/strings.xml | 6 ++++++
10 files changed, 36 insertions(+), 19 deletions(-)
diff --git a/VERSION.md b/VERSION.md
index 70957c1c68a..a9a1646b488 100644
--- a/VERSION.md
+++ b/VERSION.md
@@ -1 +1 @@
-18.0.73
\ No newline at end of file
+18.0.70
diff --git a/common/windows/delphi/general/klog.pas b/common/windows/delphi/general/klog.pas
index 2787f8b3472..2756c63893d 100644
--- a/common/windows/delphi/general/klog.pas
+++ b/common/windows/delphi/general/klog.pas
@@ -23,7 +23,7 @@
interface
-{DEFINE KLOGGING}
+{$DEFINE KLOGGING}
{$IFDEF KLOGGING}
uses
diff --git a/core/tests/unit/kmx/fixtures/meson.build b/core/tests/unit/kmx/fixtures/meson.build
index e40c30ad462..711bae6f806 100644
--- a/core/tests/unit/kmx/fixtures/meson.build
+++ b/core/tests/unit/kmx/fixtures/meson.build
@@ -2,5 +2,5 @@ if node.found()
# Note: if node is not available, we cannot build the keyboards; build.sh
# emits a warning that the 'ldml' keyboard tests will be skipped; that
# includes these tests for now
- subdir('binary')
-endif
\ No newline at end of file
+ #subdir('binary')
+endif
diff --git a/core/tests/unit/meson.build b/core/tests/unit/meson.build
index ee141cc12d3..4b0bb489e52 100644
--- a/core/tests/unit/meson.build
+++ b/core/tests/unit/meson.build
@@ -10,6 +10,6 @@ hextobin_cmd = [node, hextobin_root]
subdir('json')
subdir('utftest')
-subdir('kmnkbd')
-subdir('kmx')
-subdir('ldml')
+#subdir('kmnkbd')
+#subdir('kmx')
+#subdir('ldml')
diff --git a/windows/src/desktop/insthelp/insthelp.dpr b/windows/src/desktop/insthelp/insthelp.dpr
index ab0e47ff7db..905e4ac8eb6 100644
--- a/windows/src/desktop/insthelp/insthelp.dpr
+++ b/windows/src/desktop/insthelp/insthelp.dpr
@@ -8,7 +8,12 @@ uses
KeymanVersion in '..\..\..\..\common\windows\delphi\general\KeymanVersion.pas',
Keyman.System.InstHelp.KeymanStartTaskUninstall in 'Keyman.System.InstHelp.KeymanStartTaskUninstall.pas',
TaskScheduler_TLB in '..\..\global\delphi\winapi\TaskScheduler_TLB.pas',
- UserMessages in '..\..\..\..\common\windows\delphi\general\UserMessages.pas';
+ ErrorControlledRegistry in '..\..\..\..\common\windows\delphi\vcl\ErrorControlledRegistry.pas',
+ UserMessages in '..\..\..\..\common\windows\delphi\general\UserMessages.pas',
+ DebugPaths in '..\..\..\..\common\windows\delphi\general\DebugPaths.pas',
+ VersionInfo in '..\..\..\..\common\windows\delphi\general\VersionInfo.pas',
+ Unicode in '..\..\..\..\common\windows\delphi\general\Unicode.pas',
+ KeymanPaths in '..\..\..\..\common\windows\delphi\general\KeymanPaths.pas';
{$R version.res}
{-R manifest.res}
diff --git a/windows/src/desktop/insthelp/insthelp.dproj b/windows/src/desktop/insthelp/insthelp.dproj
index 45913bd470b..cfb0ad37fcf 100644
--- a/windows/src/desktop/insthelp/insthelp.dproj
+++ b/windows/src/desktop/insthelp/insthelp.dproj
@@ -98,6 +98,11 @@
+
+
+
+
+
Cfg_2
Base
diff --git a/windows/src/desktop/kmshell/kmshell.dproj b/windows/src/desktop/kmshell/kmshell.dproj
index c999a12d422..79061ffe4af 100644
--- a/windows/src/desktop/kmshell/kmshell.dproj
+++ b/windows/src/desktop/kmshell/kmshell.dproj
@@ -425,19 +425,19 @@
False
-
+
- kmshell.rsm
+ kmshell.exe
true
-
+
kmshell.exe
true
-
+
kmshell.exe
true
@@ -1238,6 +1238,7 @@
+ False
12
diff --git a/windows/src/desktop/kmshell/kmshell.res b/windows/src/desktop/kmshell/kmshell.res
index cfcbd309f137d6a5f5221aba48a1c759c0d56682..5154f0aa063b63d100be466835ba2a09bbe223bd 100644
GIT binary patch
delta 15
Wcmexk_Qz~O3CqPx?n)agSfl|y;0D0}
delta 15
Xcmexk_Qz~O3Cr{S4;406ut);{LY)U(
diff --git a/windows/src/desktop/kmshell/util/UfrmDownloadProgress.pas b/windows/src/desktop/kmshell/util/UfrmDownloadProgress.pas
index 2edcfbbcf70..9b5f779c8f1 100644
--- a/windows/src/desktop/kmshell/util/UfrmDownloadProgress.pas
+++ b/windows/src/desktop/kmshell/util/UfrmDownloadProgress.pas
@@ -1,18 +1,18 @@
(*
Name: UfrmDownloadProgress
Copyright: Copyright (C) SIL International.
- Documentation:
- Description:
+ Documentation:
+ Description:
Create Date: 4 Dec 2006
Modified Date: 18 May 2012
Authors: mcdurdin
- Related Files:
- Dependencies:
+ Related Files:
+ Dependencies:
- Bugs:
- Todo:
- Notes:
+ Bugs:
+ Todo:
+ Notes:
History: 04 Dec 2006 - mcdurdin - Initial version
05 Dec 2006 - mcdurdin - Localize caption
15 Jan 2007 - mcdurdin - Use font from locale.xml
diff --git a/windows/src/desktop/kmshell/xml/strings.xml b/windows/src/desktop/kmshell/xml/strings.xml
index b4e5e833502..487a818ddfc 100644
--- a/windows/src/desktop/kmshell/xml/strings.xml
+++ b/windows/src/desktop/kmshell/xml/strings.xml
@@ -571,6 +571,12 @@
Diagnostics
+
+
+
+ Check for Updates
+
+
From b0cdcabaaa192a1acc8e439146b71c36354d0c48 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Tue, 13 Aug 2024 18:29:16 +1000
Subject: [PATCH 16/43] feat(windows): correct error checking for has keyman
run
---
.../windows/delphi/general/Keyman.System.ExecuteHistory.pas | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/common/windows/delphi/general/Keyman.System.ExecuteHistory.pas b/common/windows/delphi/general/Keyman.System.ExecuteHistory.pas
index 2ed824ec0c5..e146f8fa691 100644
--- a/common/windows/delphi/general/Keyman.System.ExecuteHistory.pas
+++ b/common/windows/delphi/general/Keyman.System.ExecuteHistory.pas
@@ -22,7 +22,7 @@ function RecordKeymanStarted : Boolean;
atom := GlobalFindAtom(AtomName);
if atom = 0 then
begin
- if GetLastError <> ERROR_SUCCESS then
+ if GetLastError <> ERROR_FILE_NOT_FOUND then
RaiseLastOSError;
atom := GlobalAddAtom(AtomName);
KL.Log('RecordKeymanStarted: True');
@@ -52,7 +52,11 @@ function HasKeymanRun : Boolean;
Result := True;
end
else
+ begin
+ KL.Log('HasKeymanRun: Keyman Has Run');
Result := False;
+ end;
+
except
on E: Exception do
KL.log(E.ClassName + ': ' + E.Message);
From c86ecfd6f3716079fb727f9ff09babbca00ab1da Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Wed, 14 Aug 2024 13:51:58 +1000
Subject: [PATCH 17/43] feat(windows): use CU registry key for statemachine
state
Use Current User registry key for state machine state. Also remove
events that are no longer used. Like start download.
---
.../kmshell/main/Keyman.System.Update.pas | 1302 -----------------
.../main/Keyman.System.UpdateStateMachine.pas | 209 +--
...eyman.System.Install.EnginePostInstall.pas | 2 +-
3 files changed, 63 insertions(+), 1450 deletions(-)
delete mode 100644 windows/src/desktop/kmshell/main/Keyman.System.Update.pas
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.Update.pas b/windows/src/desktop/kmshell/main/Keyman.System.Update.pas
deleted file mode 100644
index fb878347830..00000000000
--- a/windows/src/desktop/kmshell/main/Keyman.System.Update.pas
+++ /dev/null
@@ -1,1302 +0,0 @@
-(*
- Name: BackgroundUpdate
- Copyright: Copyright (C) SIL International.
- Documentation:
- Description:
- Create Date: 2 Nov 2023
-
- Modified Date: 2 Nov 2023
- Authors: rcruickshank
- Related Files:
- Dependencies:
-
- Bugs:
- Todo:
- Notes: For the state diagram in mermaid ../BackgroundUpdateStateDiagram.md
- History:
-*)
-unit Keyman.System.Update;
-
-interface
-
-uses
- System.Classes,
- System.SysUtils,
- System.UITypes,
- System.IOUtils,
- System.Types,
- Vcl.Forms,
- TypInfo,
- KeymanPaths,
- utilkmshell,
-
- httpuploader,
- Keyman.System.UpdateCheckResponse,
- Keyman.System.ExecuteHistory,
- UfrmDownloadProgress;
-
-type
- EBackgroundUpdate = class(Exception);
-
- TBackgroundUpdateResult = (oucUnknown, oucShutDown, oucSuccess, oucNoUpdates, oucUpdatesAvailable, oucFailure, oucOffline);
-
- TUpdateState = (usIdle, usUpdateAvailable, usDownloading, usWaitingRestart, usInstalling, usRetry, usWaitingPostInstall);
-
- { Keyboard Package Params }
- TBackgroundUpdateParamsPackage = record
- ID: string;
- NewID: string;
- Description: string;
- OldVersion, NewVersion: string;
- DownloadURL: string;
- SavePath: string;
- FileName: string;
- DownloadSize: Integer;
- Install: Boolean;
- end;
- { Main Keyman Program }
- TBackgroundUpdateParamsKeyman = record
- OldVersion, NewVersion: string;
- DownloadURL: string;
- SavePath: string;
- FileName: string;
- DownloadSize: Integer;
- Install: Boolean;
- end;
-
- TBackgroundUpdateParams = record
- Keyman: TBackgroundUpdateParamsKeyman;
- Packages: array of TBackgroundUpdateParamsPackage;
- Result: TBackgroundUpdateResult;
- end;
-
- TBackgroundUpdateDownloadParams = record
- Owner: TfrmDownloadProgress;
- TotalSize: Integer;
- TotalDownloads: Integer;
- StartPosition: Integer;
- end;
-
- // Forward declaration
- TBackgroundUpdate = class;
- { State Classes Update }
-
- TStateClass = class of TState;
-
- TState = class abstract
- private
- bucStateContext: TBackgroundUpdate;
- procedure ChangeState(newState: TStateClass);
-
- public
- constructor Create(Context: TBackgroundUpdate);
- procedure Enter; virtual; abstract;
- procedure Exit; virtual; abstract;
- procedure HandleCheck; virtual; abstract;
- procedure HandleDownload; virtual; abstract;
- function HandleKmShell : Integer; virtual; abstract;
- procedure HandleInstall; virtual; abstract;
- procedure HandleMSIInstallComplete; virtual; abstract;
- procedure HandleAbort; virtual; abstract;
- procedure HandleInstallNow; virtual; abstract;
-
- // For convenience
- function StateName: string; virtual; abstract;
-
- end;
-
- // Derived classes for each state
- IdleState = class(TState)
- public
- procedure Enter; override;
- procedure Exit; override;
- procedure HandleCheck; override;
- procedure HandleDownload; override;
- function HandleKmShell : Integer; override;
- procedure HandleInstall; override;
- procedure HandleMSIInstallComplete; override;
- procedure HandleAbort; override;
- procedure HandleInstallNow; override;
- function StateName: string; override;
- end;
-
- UpdateAvailableState = class(TState)
- public
- procedure Enter; override;
- procedure Exit; override;
- procedure HandleCheck; override;
- procedure HandleDownload; override;
- function HandleKmShell : Integer; override;
- procedure HandleInstall; override;
- procedure HandleMSIInstallComplete; override;
- procedure HandleAbort; override;
- procedure HandleInstallNow; override;
- function StateName: string; override;
- end;
-
- DownloadingState = class(TState)
- private
-
- function DownloadUpdatesBackground: Boolean;
- procedure Enter; override;
- procedure Exit; override;
- procedure HandleCheck; override;
- procedure HandleDownload; override;
- function HandleKmShell : Integer; override;
- procedure HandleInstall; override;
- procedure HandleMSIInstallComplete; override;
- procedure HandleAbort; override;
- procedure HandleInstallNow; override;
- function StateName: string; override;
- end;
-
- WaitingRestartState = class(TState)
- public
- procedure Enter; override;
- procedure Exit; override;
- procedure HandleCheck; override;
- procedure HandleDownload; override;
- function HandleKmShell : Integer; override;
- procedure HandleInstall; override;
- procedure HandleMSIInstallComplete; override;
- procedure HandleAbort; override;
- procedure HandleInstallNow; override;
- function StateName: string; override;
- end;
-
- InstallingState = class(TState)
- private
- procedure DoInstallKeyman; overload;
- function DoInstallKeyman(SavePath: string) : Boolean; overload;
- {
- Installs the Keyman file using either msiexec.exe or the setup launched in
- a separate shell.
-
- @params Package The package to be installed.
-
- @returns True if the installation is successful, False otherwise.
- }
- function DoInstallPackage(Package: TBackgroundUpdateParamsPackage): Boolean;
- public
- procedure Enter; override;
- procedure Exit; override;
- procedure HandleCheck; override;
- procedure HandleDownload; override;
- function HandleKmShell : Integer; override;
- procedure HandleInstall; override;
- procedure HandleMSIInstallComplete; override;
- procedure HandleAbort; override;
- procedure HandleInstallNow; override;
- function StateName: string; override;
- end;
-
- RetryState = class(TState)
- public
- procedure Enter; override;
- procedure Exit; override;
- procedure HandleCheck; override;
- procedure HandleDownload; override;
- function HandleKmShell : Integer; override;
- procedure HandleInstall; override;
- procedure HandleMSIInstallComplete; override;
- procedure HandleAbort; override;
- procedure HandleInstallNow; override;
- function StateName: string; override;
- end;
-
- WaitingPostInstallState = class(TState)
- public
- procedure Enter; override;
- procedure Exit; override;
- procedure HandleCheck; override;
- procedure HandleDownload; override;
- function HandleKmShell : Integer; override;
- procedure HandleInstall; override;
- procedure HandleMSIInstallComplete; override;
- procedure HandleAbort; override;
- procedure HandleInstallNow; override;
- function StateName: string; override;
- end;
-
- { This class also controls the state flow see }
- TBackgroundUpdate = class
- private
- FForce: Boolean;
- FAuto: Boolean;
- FParams: TBackgroundUpdateParams;
-
- FErrorMessage: string;
-
- DownloadTempPath: string;
-
- FShowErrors: Boolean;
-
-
-
- FDownload: TBackgroundUpdateDownloadParams;
-
- CurrentState: TState;
- // State object for performance (could lazy create?)
- FIdle: IdleState;
- FUpdateAvailable: UpdateAvailableState;
- FDownloading: DownloadingState;
- FWaitingRestart: WaitingRestartState;
- FInstalling: InstallingState;
- FRetry: RetryState;
- FWaitingPostInstall: WaitingPostInstallState;
- function GetState: TStateClass;
- procedure SetState(const Value: TStateClass);
- procedure SetStateOnly(const Value: TStateClass);
- function ConvertEnumState(const TEnumState: TUpdateState): TStateClass;
-
- procedure ShutDown;
-
- {
- SavePackageUpgradesToDownloadTempPath saves any new package IDs to a
- single file in the download tempPath. This procedure saves the IDs of any
- new packages to a file named "upgrade_packages.inf" in the download
- tempPath.
- }
- procedure SavePackageUpgradesToDownloadTempPath;
- function checkUpdateSchedule : Boolean;
-
- function SetRegistryState (Update : TUpdateState): Boolean;
- function SetRegistryInstallMode (InstallMode : Boolean): Boolean;
-
- protected
- property State: TStateClass read GetState write SetState;
-
- public
- constructor Create(AForce: Boolean);
- destructor Destroy; override;
-
- procedure HandleCheck;
- function HandleKmShell : Integer;
- procedure HandleDownload;
- procedure HandleInstall;
- procedure HandleMSIInstallComplete;
- procedure HandleAbort;
- procedure HandleInstallNow;
- function CurrentStateName: string;
-
- property ShowErrors: Boolean read FShowErrors write FShowErrors;
- function CheckRegistryState : TUpdateState;
- function CheckRegistryInstallMode : Boolean;
-
- end;
-
- IOnlineUpdateSharedData = interface
- ['{7442A323-C1E3-404B-BEEA-5B24A52BBB0E}']
- function Params: TBackgroundUpdateParams;
- end;
-
- TOnlineUpdateSharedData = class(TInterfacedObject, IOnlineUpdateSharedData)
- private
- FParams: TBackgroundUpdateParams;
- public
- constructor Create(AParams: TBackgroundUpdateParams);
- function Params: TBackgroundUpdateParams;
- end;
-
-implementation
-
-uses
- Winapi.Shlobj,
- System.WideStrUtils,
- Vcl.Dialogs,
- Winapi.ShellApi,
- Winapi.Windows,
- Winapi.WinINet,
-
- GlobalProxySettings,
- KLog,
- keymanapi_TLB,
- KeymanVersion,
- kmint,
- ErrorControlledRegistry,
- RegistryKeys,
- Upload_Settings,
- utildir,
- utilexecute,
- OnlineUpdateCheckMessages, // todo create own messages
- UfrmOnlineUpdateIcon,
- UfrmOnlineUpdateNewVersion,
- utilsystem,
- utiluac,
- versioninfo,
- Keyman.System.RemoteUpdateCheck,
- Keyman.System.DownloadUpdate;
-
-const
- SPackageUpgradeFilename = 'upgrade_packages.inf';
- kmShellContinue = 0;
- kmShellExit = 1;
-
-{ TBackgroundUpdate }
-
-constructor TBackgroundUpdate.Create(AForce : Boolean);
-var TSerailsedState : TUpdateState;
-begin
- inherited Create;
-
-
- FShowErrors := True;
- FParams.Result := oucUnknown;
-
- FForce := AForce;
- FAuto := True; // Default to automatically check, download, and install
- FIdle := IdleState.Create(Self);
- FUpdateAvailable := UpdateAvailableState.Create(Self);
- FDownloading := DownloadingState.Create(Self);
- FWaitingRestart := WaitingRestartState.Create(Self);
- FInstalling := InstallingState.Create(Self);
- FRetry := RetryState.Create(Self);
- FWaitingPostInstall := WaitingPostInstallState.Create(Self);
- // Check the Registry setting.
- SetStateOnly(ConvertEnumState(CheckRegistryState));
- KL.Log('TBackgroundUpdate.Create');
-end;
-
-destructor TBackgroundUpdate.Destroy;
-begin
- if (FErrorMessage <> '') and FShowErrors then
- KL.Log(FErrorMessage);
-
- if FParams.Result = oucShutDown then
- ShutDown;
-
- FIdle.Free;
- FUpdateAvailable.Free;
- FDownloading.Free;
- FWaitingRestart.Free;
- FInstalling.Free;
- FRetry.Free;
- FWaitingPostInstall.Free;
-
- KL.Log('TBackgroundUpdate.Destroy: FErrorMessage = '+FErrorMessage);
- KL.Log('TBackgroundUpdate.Destroy: FParams.Result = '+IntToStr(Ord(FParams.Result)));
-
- inherited Destroy;
-end;
-
-
-procedure TBackgroundUpdate.SavePackageUpgradesToDownloadTempPath;
-var
- i: Integer;
-begin
- with TStringList.Create do
- try
- for i := 0 to High(FParams.Packages) do
- if FParams.Packages[i].NewID <> '' then
- Add(FParams.Packages[i].NewID+'='+FParams.Packages[i].ID);
- if Count > 0 then
- SaveToFile(DownloadTempPath + SPackageUpgradeFileName);
- finally
- Free;
- end;
-end;
-
-procedure TBackgroundUpdate.ShutDown;
-begin
- if Assigned(Application) then
- Application.Terminate;
-end;
-
-
-{ TOnlineUpdateSharedData }
-
-constructor TOnlineUpdateSharedData.Create(AParams: TBackgroundUpdateParams);
-begin
- inherited Create;
- FParams := AParams;
-end;
-
-function TOnlineUpdateSharedData.Params: TBackgroundUpdateParams;
-begin
- Result := FParams;
-end;
-
-
-function TBackgroundUpdate.SetRegistryState(Update : TUpdateState): Boolean;
-var
- UpdateStr : string;
-begin
-
- Result := False;
- with TRegistryErrorControlled.Create do
- try
- RootKey := HKEY_LOCAL_MACHINE;
- KL.Log('SetRegistryState State Entry');
- if OpenKey(SRegKey_KeymanEngine_LM, True) then
- begin
- UpdateStr := GetEnumName(TypeInfo(TUpdateState), Ord(Update));
- WriteString(SRegValue_Update_State, UpdateStr);
- KL.Log('SetRegistryState State is:[' + UpdateStr + ']');
- end;
- Result := True;
- finally
- Free;
- end;
-
-end;
-
-
-function TBackgroundUpdate.CheckRegistryState : TUpdateState; // I2329
-var
- UpdateState : TUpdateState;
-
-begin
- // We will use a registry flag to maintain the state of the background update
-
- UpdateState := usIdle; // do we need a unknown state ?
- // check the registry value
- with TRegistryErrorControlled.Create do // I2890
- try
- RootKey := HKEY_LOCAL_MACHINE;
- if OpenKeyReadOnly(SRegKey_KeymanEngine_LM) and ValueExists(SRegValue_Update_State) then
- begin
- UpdateState := TUpdateState(GetEnumValue(TypeInfo(TUpdateState), ReadString(SRegValue_Update_State)));
- KL.Log('CheckRegistryState State is:[' + ReadString(SRegValue_Update_State) + ']');
- end
- else
- begin
- UpdateState := usIdle; // do we need a unknown state ?
- KL.Log('CheckRegistryState State reg value not found default:[' + ReadString(SRegValue_Update_State) + ']');
- end
- finally
- Free;
- end;
- Result := UpdateState;
-end;
-
-function TBackgroundUpdate.SetRegistryInstallMode (InstallMode : Boolean): Boolean;
-var
- InstallModeStr : string;
-begin
-
- Result := False;
- with TRegistryErrorControlled.Create do
- try
- RootKey := HKEY_LOCAL_MACHINE;
- KL.Log('SetRegistryState State Entry');
- if OpenKey(SRegKey_KeymanEngine_LM, True) then
- begin
- InstallModeStr := BoolToStr(InstallMode, True);
- WriteString(SRegValue_Install_Mode, InstallModeStr);
- KL.Log('SetRegistryInstallMode is:[' + InstallModeStr + ']');
- end;
- Result := True;
- finally
- Free;
- end;
-
-end;
-
-function TBackgroundUpdate.CheckRegistryInstallMode : Boolean;
-var
- InstallMode : Boolean;
-
-begin
- // We will use a registry flag to maintain the install mode background/foreground
-
- InstallMode := False;
- // check the registry value
- with TRegistryErrorControlled.Create do // I2890
- try
- RootKey := HKEY_LOCAL_MACHINE;
- if OpenKeyReadOnly(SRegKey_KeymanEngine_LM) and ValueExists(SRegValue_Install_Mode) then
- begin
- InstallMode := StrToBool(ReadString(SRegValue_Install_Mode));
- KL.Log('CheckRegistryState State is:[' + ReadString(SRegValue_Update_State) + ']');
- end
- else
- begin
- InstallMode := False; // default to background
- KL.Log('CheckRegistryInstallMode reg value not found default:[ False ]');
- end
- finally
- Free;
- end;
- Result := InstallMode;
-end;
-
-
-function TBackgroundUpdate.CheckUpdateSchedule: Boolean;
-begin
- try
- Result := False;
- with TRegistryErrorControlled.Create do
- try
- if OpenKeyReadOnly(SRegKey_KeymanDesktop_CU) then
- begin
- if ValueExists(SRegValue_CheckForUpdates) and not ReadBool(SRegValue_CheckForUpdates) and not FForce then
- begin
- Result := False;
- Exit;
- end;
- if ValueExists(SRegValue_LastUpdateCheckTime) and (Now - ReadDateTime(SRegValue_LastUpdateCheckTime) < 1) and not FForce then
- begin
- Result := False;
- Exit;
- end;
- // Else Time to check for updates
- Result := True;
- end;
- finally
- Free;
- end;
- except
- { we will not run the check if an error occurs reading the settings }
- on E:Exception do
- begin
- Result := False;
- FErrorMessage := E.Message;
- Exit;
- end;
- end;
-end;
-
-function TBackgroundUpdate.GetState: TStateClass;
-begin
- Result := TStateClass(CurrentState.ClassType);
-end;
-
-procedure TBackgroundUpdate.SetState(const Value: TStateClass);
-begin
- if Assigned(CurrentState) then
- begin
- CurrentState.Exit;
- end;
-
- SetStateOnly(Value);
-
- if Assigned(CurrentState) then
- begin
- CurrentState.Enter;
- end
- else
- begin
- // TODO: #10210 Error log for Unable to set state for Value
- end;
-
-end;
-
-procedure TBackgroundUpdate.SetStateOnly(const Value: TStateClass);
-begin
- if Value = IdleState then
- begin
- CurrentState := FIdle;
- end
- else if Value = UpdateAvailableState then
- begin
- CurrentState := FUpdateAvailable;
- end
- else if Value = DownloadingState then
- begin
- CurrentState := FDownloading;
- end
- else if Value = WaitingRestartState then
- begin
- CurrentState := FWaitingRestart;
- end
- else if Value = InstallingState then
- begin
- CurrentState := FInstalling;
- end
- else if Value = RetryState then
- begin
- CurrentState := FRetry;
- end
- else if Value = WaitingPostInstallState then
- begin
- CurrentState := FWaitingPostInstall;
- end;
-end;
-
-function TBackgroundUpdate.ConvertEnumState(const TEnumState: TUpdateState) : TStateClass;
-begin
- case TEnumState of
- usIdle: Result := IdleState;
- usUpdateAvailable: Result := UpdateAvailableState;
- usDownloading: Result := DownloadingState;
- usWaitingRestart: Result := WaitingRestartState;
- usInstalling: Result := InstallingState;
- usRetry: Result := RetryState;
- usWaitingPostInstall: Result := WaitingPostInstallState;
- else
- // TODO: #10210 Log error unknown state setting to idle
- Result := IdleState;
- end;
-end;
-
-procedure TBackgroundUpdate.HandleCheck;
-begin
- CurrentState.HandleCheck;
-end;
-
-function TBackgroundUpdate.HandleKmShell;
-begin
- Result := CurrentState.HandleKmShell;
-end;
-
-procedure TBackgroundUpdate.HandleDownload;
-begin
- CurrentState.HandleDownload;
-end;
-
-procedure TBackgroundUpdate.HandleInstall;
-begin
- CurrentState.HandleInstall;
-end;
-
-procedure TBackgroundUpdate.HandleMSIInstallComplete;
-begin
- CurrentState.HandleMSIInstallComplete;
-end;
-
-procedure TBackgroundUpdate.HandleAbort;
-begin
- CurrentState.HandleAbort;
-end;
-
-procedure TBackgroundUpdate.HandleInstallNow;
-begin
- CurrentState.HandleInstallNow;
-end;
-
-function TBackgroundUpdate.CurrentStateName: string;
-begin
- Result := CurrentState.StateName;
-end;
-
-
-
-{ State Class Memebers }
-constructor TState.Create(Context: TBackgroundUpdate);
-begin
- bucStateContext := Context;
-end;
-
-procedure TState.ChangeState(NewState: TStateClass);
-begin
- bucStateContext.State := NewState;
-end;
-
-
-{ IdleState }
-
-procedure IdleState.Enter;
-begin
- // Enter UpdateAvailableState
- bucStateContext.SetRegistryState(usIdle);
-end;
-
-procedure IdleState.Exit;
-begin
-
-end;
-
-procedure IdleState.HandleCheck;
-var
- CheckForUpdates: TRemoteUpdateCheck;
- Result : TRemoteUpdateCheckResult;
-begin
-
- { Make a HTTP request out and see if updates are available for now do
- this all in the Idle HandleCheck message. But could be broken into an
- seperate state of WaitngCheck RESP }
- { if Response not OK stay in the idle state and return }
-
-
- // should be false but forcing check for testing
- //CheckForUpdates := TRemoteUpdateCheck.Create(True);
- CheckForUpdates := TRemoteUpdateCheck.Create(False);
- try
- Result:= CheckForUpdates.Run;
- finally
- CheckForUpdates.Free;
- end;
-
- { Response OK and Update is available }
- if Result = wucSuccess then
- begin
- ChangeState(UpdateAvailableState);
- end;
- // else staty in idle state
-end;
-
-procedure IdleState.HandleDownload;
-begin
-
-end;
-
-function IdleState.HandleKmShell;
-begin
-
- Result := kmShellContinue;
-end;
-
-procedure IdleState.HandleInstall;
-begin
-
-end;
-
-procedure IdleState.HandleMSIInstallComplete;
-begin
-
-end;
-
-procedure IdleState.HandleAbort;
-begin
-
-end;
-
-procedure IdleState.HandleInstallNow;
-begin
- bucStateContext.SetRegistryInstallMode(True);
- bucStateContext.CurrentState.HandleCheck;
-end;
-
-function IdleState.StateName;
-begin
-
- Result := 'IdleState';
-end;
-
-{ UpdateAvailableState }
-
-procedure UpdateAvailableState.Enter;
-begin
- // Enter UpdateAvailableState
- bucStateContext.SetRegistryState(usUpdateAvailable);
- if bucStateContext.FAuto then
- begin
- bucStateContext.CurrentState.HandleDownload;
- end;
-end;
-
-procedure UpdateAvailableState.Exit;
-begin
- // Exit UpdateAvailableState
-end;
-
-procedure UpdateAvailableState.HandleCheck;
-begin
-
-end;
-
-procedure UpdateAvailableState.HandleDownload;
-begin
- ChangeState(DownloadingState);
-end;
-
-function UpdateAvailableState.HandleKmShell;
-begin
- if bucStateContext.FAuto then
- begin
- bucStateContext.CurrentState.HandleDownload ;
- end;
- Result := kmShellContinue;
-end;
-
-procedure UpdateAvailableState.HandleInstall;
-begin
-
-end;
-
-procedure UpdateAvailableState.HandleMSIInstallComplete;
-begin
-
-end;
-
-procedure UpdateAvailableState.HandleAbort;
-begin
-
-end;
-
-procedure UpdateAvailableState.HandleInstallNow;
-begin
- bucStateContext.SetRegistryInstallMode(True);
- ChangeState(DownloadingState);
-end;
-
-function UpdateAvailableState.StateName;
-begin
-
- Result := 'UpdateAvailableState';
-end;
-
-{ DownloadingState }
-
-procedure DownloadingState.Enter;
-var DownloadResult : Boolean;
-begin
- // Enter DownloadingState
- bucStateContext.SetRegistryState(usDownloading);
- DownloadResult := DownloadUpdatesBackground;
- if DownloadResult then
- begin
- if HasKeymanRun then
- ChangeState(WaitingRestartState)
- else
- ChangeState(InstallingState);
- end
- else
- begin
- ChangeState(RetryState);
- end
-end;
-
-procedure DownloadingState.Exit;
-begin
- // Exit DownloadingState
-end;
-
-procedure DownloadingState.HandleCheck;
-begin
-
-end;
-
-procedure DownloadingState.HandleDownload;
-var DownloadResult : Boolean;
-begin
- // We are already downloading do nothing
-end;
-
-function DownloadingState.HandleKmShell;
-var DownloadResult : Boolean;
-begin
- DownloadResult := DownloadUpdatesBackground;
- // TODO check if keyman is running then send to Waiting Restart
- if DownloadResult then
- begin
- if HasKeymanRun then
- begin
- ChangeState(WaitingRestartState);
- Result := kmShellContinue;
- end
- else
- begin
- ChangeState(InstallingState);
- Result := kmShellExit;
- end;
- end
- else
- begin
- ChangeState(RetryState);
- Result := kmShellContinue;
- end;
-
-end;
-
-procedure DownloadingState.HandleInstall;
-begin
- ChangeState(InstallingState);
-end;
-
-procedure DownloadingState.HandleMSIInstallComplete;
-begin
-
-end;
-
-procedure DownloadingState.HandleAbort;
-begin
-end;
-
-procedure DownloadingState.HandleInstallNow;
-begin
- bucStateContext.SetRegistryInstallMode(True);
- // Continue downloading
-end;
-
-function DownloadingState.StateName;
-begin
- Result := 'DownloadingState';
-end;
-
-
-function DownloadingState.DownloadUpdatesBackground: Boolean;
-var
- i: Integer;
- DownloadBackGroundSavePath : String;
- DownloadResult : Boolean;
- DownloadUpdate: TDownloadUpdate;
-begin
- DownloadUpdate := TDownloadUpdate.Create;
- try
- DownloadResult := DownloadUpdate.DownloadUpdates;
- KL.Log('TBackgroundUpdate.DownloadUpdatesBackground: DownloadResult = '+IntToStr(Ord(DownloadResult)));
- Result := DownloadResult;
-// #TODO: #10210 workout when we need to refresh kmcom keyboards
-
-// if Result in [ wucSuccess] then
-// begin
-// kmcom.Keyboards.Refresh;
-// kmcom.Keyboards.Apply;
-// kmcom.Packages.Refresh;
-// end;
-
- finally
- DownloadUpdate.Free;
- end;
-end;
-
-{ WaitingRestartState }
-
-procedure WaitingRestartState.Enter;
-begin
- // Enter DownloadingState
- bucStateContext.SetRegistryState(usWaitingRestart);
-end;
-
-procedure WaitingRestartState.Exit;
-begin
- // Exit DownloadingState
-end;
-
-procedure WaitingRestartState.HandleCheck;
-begin
-
-end;
-
-procedure WaitingRestartState.HandleDownload;
-begin
-
-end;
-
-function WaitingRestartState.HandleKmShell;
-var
- SavedPath : String;
- Filenames : TStringDynArray;
-begin
- KL.Log('WaitingRestartState.HandleKmShell Enter');
- // Still can't go if keyman has run
- if HasKeymanRun then
- begin
- KL.Log('WaitingRestartState.HandleKmShell Keyman Has Run');
- Result := kmShellExit;
- // Exit; // Exit is not wokring for some reason.
- // this else is only here because the exit is not working.
- end
- else
- begin
- // Check downloaded cache if available then
- SavedPath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
- GetFileNamesInDirectory(SavedPath, FileNames);
- if Length(FileNames) = 0 then
- begin
- KL.Log('WaitingRestartState.HandleKmShell No Files in Download Cache');
- // Return to Idle state and check for Updates state
- ChangeState(IdleState);
- bucStateContext.CurrentState.HandleCheck;
- Result := kmShellExit;
- // Exit; // again exit was not working
- end
- else
- begin
- KL.Log('WaitingRestartState.HandleKmShell is good to install');
- ChangeState(InstallingState);
- Result := kmShellExit;
- end;
- end;
-end;
-
-procedure WaitingRestartState.HandleInstall;
-begin
-
-end;
-
-procedure WaitingRestartState.HandleMSIInstallComplete;
-begin
-
-end;
-
-procedure WaitingRestartState.HandleAbort;
-begin
-
-end;
-
-procedure WaitingRestartState.HandleInstallNow;
-begin
- bucStateContext.SetRegistryInstallMode(True);
- // Notify User to install
- ChangeState(InstallingState);
-
-end;
-
-function WaitingRestartState.StateName;
-begin
-
- Result := 'WaitingRestartState';
-end;
-
-{ InstallingState }
-
-function InstallingState.DoInstallPackage(Package: TBackgroundUpdateParamsPackage): Boolean;
-var
- FPackage: IKeymanPackageFile2;
-begin
- Result := True;
-
- FPackage := kmcom.Packages.GetPackageFromFile(Package.SavePath) as IKeymanPackageFile2;
- FPackage.Install2(True); // Force overwrites existing package and leaves most settings for it intact
- FPackage := nil;
-
- kmcom.Refresh;
- kmcom.Apply;
- System.SysUtils.DeleteFile(Package.SavePath);
-end;
-
-procedure InstallingState.DoInstallKeyman;
-var
- s: string;
- FResult: Boolean;
-begin
- FResult := False;
- s := LowerCase(ExtractFileExt(bucStateContext.FParams.Keyman.SavePath));
- if s = '.msi' then
- FResult := TUtilExecute.Shell(0, 'msiexec.exe', '', '/qb /i "'+bucStateContext.FParams.Keyman.SavePath+'" AUTOLAUNCHPRODUCT=1') // I3349
- else if s = '.exe' then
- FResult := TUtilExecute.Shell(0, bucStateContext.FParams.Keyman.SavePath, '', '-au') // I3349
- else
- Exit;
- if not FResult then
- ShowMessage(SysErrorMessage(GetLastError));
-end;
-
-function InstallingState.DoInstallKeyman(SavePath: string) : Boolean;
-var
- s: string;
- FResult: Boolean;
-begin
- s := LowerCase(ExtractFileExt(SavePath));
- if s = '.msi' then
- FResult := TUtilExecute.Shell(0, 'msiexec.exe', '', '/qb /i "'+SavePath+'" AUTOLAUNCHPRODUCT=1') // I3349
- else if s = '.exe' then
- begin
- KL.Log('TBackgroundUpdate.InstallingState.DoInstallKeyman SavePath:"'+ SavePath+'"');
- FResult := TUtilExecute.Shell(0, SavePath, '', '-au') // I3349
- end
- else
- FResult := False;
-
- if not FResult then
- begin
- KL.Log('TBackgroundUpdate.InstallingState.DoInstall: Result = '+IntToStr(Ord(FResult)));
- // Log messageShowMessage(SysErrorMessage(GetLastError));
- end;
-
- Result := FResult;
-end;
-
-procedure InstallingState.Enter;
-var
- SavePath: String;
- fileExt : String;
- fileName: String;
- fileNames: TStringDynArray;
-begin
- bucStateContext.SetRegistryState(usInstalling);
- SavePath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
-
- GetFileNamesInDirectory(SavePath, fileNames);
- // for now we only want the exe although excute install can
- // handle msi
- for fileName in fileNames do
- begin
- fileExt := LowerCase(ExtractFileExt(fileName));
- if fileExt = '.exe' then
- break;
- end;
-
- if DoInstallKeyman(SavePath + ExtractFileName(fileName)) then
- begin
- KL.Log('TBackgroundUpdate.InstallingState.Enter: DoInstall OK');
- end
- else
- begin
- // TODO: #10210 clean failed download
- // TODO: #10210 Do we do a retry on install? probably not
- KL.Log('TBackgroundUpdate.InstallingState.Enter: DoInstall fail');
- ChangeState(IdleState);
- end
-end;
-
-procedure InstallingState.Exit;
-begin
- // Exit DownloadingState
-end;
-
-procedure InstallingState.HandleCheck;
-begin
-
-end;
-
-procedure InstallingState.HandleDownload;
-begin
-
-end;
-
-function InstallingState.HandleKmShell;
-begin
- // Result = exit straight away as we are installing (MSI installer)
- // need to just do a no-op keyman will it maybe using kmshell to install
- // packages.
- Result := kmShellContinue;
-end;
-
-procedure InstallingState.HandleInstall;
-begin
-
-end;
-
-procedure InstallingState.HandleMSIInstallComplete;
-begin
-
-end;
-
-procedure InstallingState.HandleAbort;
-begin
- ChangeState(IdleState);
-end;
-
-procedure InstallingState.HandleInstallNow;
-begin
- // Do Nothing. Need the UI to let user know installation in progress OR
-end;
-
-function InstallingState.StateName;
-begin
-
- Result := 'InstallingState';
-end;
-
-{ RetryState }
-
-procedure RetryState.Enter;
-begin
- // Enter DownloadingState
- bucStateContext.SetRegistryState(usRetry);
-end;
-
-procedure RetryState.Exit;
-begin
- // Exit DownloadingState
-end;
-
-procedure RetryState.HandleCheck;
-begin
-
-end;
-
-procedure RetryState.HandleDownload;
-begin
-
-end;
-
-function RetryState.HandleKmShell;
-begin
- // #TODO: #10210 Implement retry
- Result := kmShellContinue
-end;
-
-procedure RetryState.HandleInstall;
-begin
-
-end;
-
-procedure RetryState.HandleMSIInstallComplete;
-begin
-
-end;
-
-procedure RetryState.HandleAbort;
-begin
-
-end;
-
-procedure RetryState.HandleInstallNow;
-begin
- bucStateContext.SetRegistryInstallMode(True);
- // TODO: #10038 handle retry counts
- ChangeState(InstallingState);
-end;
-
-function RetryState.StateName;
-begin
-
- Result := 'RetryState';
-end;
-
-{ WaitingPostInstallState }
-
-procedure WaitingPostInstallState.Enter;
-begin
- // Enter downloading state
- bucStateContext.SetRegistryState(usWaitingPostInstall);
-end;
-
-procedure WaitingPostInstallState.Exit;
-begin
- // Exit downloading state
-end;
-
-procedure WaitingPostInstallState.HandleCheck;
-begin
- // Handle Check
-end;
-
-procedure WaitingPostInstallState.HandleDownload;
-begin
- // Handle Download
-end;
-
-function WaitingPostInstallState.HandleKmShell;
-begin
- // TODO: #10210 have a counter if we get called in this state
- // too many time abort.
- HandleMSIInstallComplete;
- Result := kmShellContinue;
-end;
-
-procedure WaitingPostInstallState.HandleInstall;
-begin
- // Handle Install
-end;
-
-procedure WaitingPostInstallState.HandleMSIInstallComplete;
-var SavePath: string;
- FileName: String;
- FileNames: TStringDynArray;
-begin
- KL.Log('WaitingPostInstallState.HandleMSIInstallComplete');
- SavePath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
- KL.Log('WaitingPostInstallState.HandleMSIInstallComplete remove SavePath:'+ SavePath);
-
- GetFileNamesInDirectory(SavePath, FileNames);
- for FileName in FileNames do
- begin
- System.SysUtils.DeleteFile(FileName);
- end;
- ChangeState(IdleState);
-end;
-
-procedure WaitingPostInstallState.HandleAbort;
-begin
- // Handle Abort
-end;
-
-procedure WaitingPostInstallState.HandleInstallNow;
-begin
- // Do nothing as files will be cleaned via HandleKmShell
-end;
-
-function WaitingPostInstallState.StateName;
-begin
-
- Result := 'WaitingPostInstallState';
-end;
-
-
-
-end.
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
index 6d6eb9b4265..589bca7582e 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
@@ -35,6 +35,9 @@ interface
Keyman.System.ExecuteHistory,
UfrmDownloadProgress;
+const
+ CheckPeriod: Integer = 7; // Days between checking for updates
+
type
EUpdateStateMachine = class(Exception);
@@ -93,10 +96,8 @@ TState = class abstract
procedure Enter; virtual; abstract;
procedure Exit; virtual; abstract;
procedure HandleCheck; virtual; abstract;
- procedure HandleDownload; virtual; abstract;
function HandleKmShell : Integer; virtual; abstract;
procedure HandleInstall; virtual; abstract;
- procedure HandleMSIInstallComplete; virtual; abstract;
procedure HandleAbort; virtual; abstract;
procedure HandleInstallNow; virtual; abstract;
@@ -111,10 +112,8 @@ IdleState = class(TState)
procedure Enter; override;
procedure Exit; override;
procedure HandleCheck; override;
- procedure HandleDownload; override;
function HandleKmShell : Integer; override;
procedure HandleInstall; override;
- procedure HandleMSIInstallComplete; override;
procedure HandleAbort; override;
procedure HandleInstallNow; override;
function StateName: string; override;
@@ -125,10 +124,8 @@ UpdateAvailableState = class(TState)
procedure Enter; override;
procedure Exit; override;
procedure HandleCheck; override;
- procedure HandleDownload; override;
function HandleKmShell : Integer; override;
procedure HandleInstall; override;
- procedure HandleMSIInstallComplete; override;
procedure HandleAbort; override;
procedure HandleInstallNow; override;
function StateName: string; override;
@@ -141,10 +138,8 @@ DownloadingState = class(TState)
procedure Enter; override;
procedure Exit; override;
procedure HandleCheck; override;
- procedure HandleDownload; override;
function HandleKmShell : Integer; override;
procedure HandleInstall; override;
- procedure HandleMSIInstallComplete; override;
procedure HandleAbort; override;
procedure HandleInstallNow; override;
function StateName: string; override;
@@ -155,10 +150,8 @@ WaitingRestartState = class(TState)
procedure Enter; override;
procedure Exit; override;
procedure HandleCheck; override;
- procedure HandleDownload; override;
function HandleKmShell : Integer; override;
procedure HandleInstall; override;
- procedure HandleMSIInstallComplete; override;
procedure HandleAbort; override;
procedure HandleInstallNow; override;
function StateName: string; override;
@@ -181,10 +174,8 @@ InstallingState = class(TState)
procedure Enter; override;
procedure Exit; override;
procedure HandleCheck; override;
- procedure HandleDownload; override;
function HandleKmShell : Integer; override;
procedure HandleInstall; override;
- procedure HandleMSIInstallComplete; override;
procedure HandleAbort; override;
procedure HandleInstallNow; override;
function StateName: string; override;
@@ -195,44 +186,37 @@ RetryState = class(TState)
procedure Enter; override;
procedure Exit; override;
procedure HandleCheck; override;
- procedure HandleDownload; override;
function HandleKmShell : Integer; override;
procedure HandleInstall; override;
- procedure HandleMSIInstallComplete; override;
procedure HandleAbort; override;
procedure HandleInstallNow; override;
function StateName: string; override;
end;
PostInstallState = class(TState)
+ private
+ procedure HandleMSIInstallComplete;
public
procedure Enter; override;
procedure Exit; override;
procedure HandleCheck; override;
- procedure HandleDownload; override;
function HandleKmShell : Integer; override;
procedure HandleInstall; override;
- procedure HandleMSIInstallComplete; override;
procedure HandleAbort; override;
procedure HandleInstallNow; override;
function StateName: string; override;
end;
+
{ This class also controls the state flow see }
TUpdateStateMachine = class
private
FForce: Boolean;
FAuto: Boolean;
FParams: TUpdateStateMachineParams;
-
FErrorMessage: string;
-
DownloadTempPath: string;
-
FShowErrors: Boolean;
-
-
-
FDownload: TUpdateStateMachineDownloadParams;
CurrentState: TState;
@@ -250,7 +234,6 @@ TUpdateStateMachine = class
function ConvertEnumState(const TEnumState: TUpdateState): TStateClass;
procedure ShutDown;
-
{
SavePackageUpgradesToDownloadTempPath saves any new package IDs to a
single file in the download tempPath. This procedure saves the IDs of any
@@ -272,9 +255,7 @@ TUpdateStateMachine = class
procedure HandleCheck;
function HandleKmShell : Integer;
- procedure HandleDownload;
procedure HandleInstall;
- procedure HandleMSIInstallComplete;
procedure HandleAbort;
procedure HandleInstallNow;
function CurrentStateName: string;
@@ -339,8 +320,6 @@ constructor TUpdateStateMachine.Create(AForce : Boolean);
// var TSerailsedState : TUpdateState; // TODO: Remove
begin
inherited Create;
-
-
FShowErrors := True;
FParams.Result := oucUnknown;
@@ -403,7 +382,6 @@ procedure TUpdateStateMachine.ShutDown;
Application.Terminate;
end;
-
{ TOnlineUpdateSharedData }
constructor TOnlineUpdateSharedData.Create(AParams: TUpdateStateMachineParams);
@@ -417,31 +395,41 @@ function TOnlineUpdateSharedData.Params: TUpdateStateMachineParams;
Result := FParams;
end;
-
function TUpdateStateMachine.SetRegistryState(Update : TUpdateState): Boolean;
var
UpdateStr : string;
+ Registry: TRegistryErrorControlled;
begin
-
Result := False;
- with TRegistryErrorControlled.Create do
+ Registry := TRegistryErrorControlled.Create;
+
try
- RootKey := HKEY_LOCAL_MACHINE;
+ Registry.RootKey := HKEY_CURRENT_USER;
KL.Log('SetRegistryState State Entry');
- if OpenKey(SRegKey_KeymanEngine_LM, True) then
+ if not Registry.OpenKey(SRegKey_KeymanEngine_LM, True) then
begin
- UpdateStr := GetEnumName(TypeInfo(TUpdateState), Ord(Update));
- WriteString(SRegValue_Update_State, UpdateStr);
- KL.Log('SetRegistryState State is:[' + UpdateStr + ']');
+ KL.Log('Failed to open registry key: ' + SRegKey_KeymanEngine_LM);
+ Exit;
end;
- Result := True;
+
+ try
+ UpdateStr := GetEnumName(TypeInfo(TUpdateState), Ord(Update));
+ Registry.WriteString(SRegValue_Update_State, UpdateStr);
+ KL.Log('SetRegistryState State is: [' + UpdateStr + ']');
+ Result := True;
+ except
+ on E: Exception do
+ begin
+ KL.Log('Failed to write to registry: ' + E.Message);
+ end;
+ end;
+
finally
- Free;
+ Registry.Free;
end;
end;
-
function TUpdateStateMachine.CheckRegistryState : TUpdateState; // I2329
var
UpdateState : TUpdateState;
@@ -452,7 +440,7 @@ function TUpdateStateMachine.CheckRegistryState : TUpdateState; // I2329
// check the registry value
with TRegistryErrorControlled.Create do // I2890
try
- RootKey := HKEY_LOCAL_MACHINE;
+ RootKey := HKEY_CURRENT_USER;
if OpenKeyReadOnly(SRegKey_KeymanEngine_LM) and ValueExists(SRegValue_Update_State) then
begin
UpdateState := TUpdateState(GetEnumValue(TypeInfo(TUpdateState), ReadString(SRegValue_Update_State)));
@@ -477,7 +465,7 @@ function TUpdateStateMachine.SetRegistryInstallMode (InstallMode : Boolean): Boo
Result := False;
with TRegistryErrorControlled.Create do
try
- RootKey := HKEY_LOCAL_MACHINE;
+ RootKey := HKEY_CURRENT_USER;
KL.Log('SetRegistryState State Entry');
if OpenKey(SRegKey_KeymanEngine_LM, True) then
begin
@@ -503,7 +491,7 @@ function TUpdateStateMachine.CheckRegistryInstallMode : Boolean;
// check the registry value
with TRegistryErrorControlled.Create do // I2890
try
- RootKey := HKEY_LOCAL_MACHINE;
+ RootKey := HKEY_CURRENT_USER;
if OpenKeyReadOnly(SRegKey_KeymanEngine_LM) and ValueExists(SRegValue_Install_Mode) then
begin
InstallMode := StrToBool(ReadString(SRegValue_Install_Mode));
@@ -520,7 +508,6 @@ function TUpdateStateMachine.CheckRegistryInstallMode : Boolean;
Result := InstallMode;
end;
-
function TUpdateStateMachine.CheckUpdateSchedule: Boolean;
begin
try
@@ -639,21 +626,11 @@ function TUpdateStateMachine.HandleKmShell;
Result := CurrentState.HandleKmShell;
end;
-procedure TUpdateStateMachine.HandleDownload;
-begin
- CurrentState.HandleDownload;
-end;
-
procedure TUpdateStateMachine.HandleInstall;
begin
CurrentState.HandleInstall;
end;
-procedure TUpdateStateMachine.HandleMSIInstallComplete;
-begin
- CurrentState.HandleMSIInstallComplete;
-end;
-
procedure TUpdateStateMachine.HandleAbort;
begin
CurrentState.HandleAbort;
@@ -669,8 +646,6 @@ function TUpdateStateMachine.CurrentStateName: string;
Result := CurrentState.StateName;
end;
-
-
{ State Class Memebers }
constructor TState.Create(Context: TUpdateStateMachine);
begin
@@ -679,10 +654,11 @@ constructor TState.Create(Context: TUpdateStateMachine);
procedure TState.ChangeState(NewState: TStateClass);
begin
+ KL.Log('TUpdateStateMachine.ChangeState old' + bucStateContext.CurrentStateName );
bucStateContext.State := NewState;
+ KL.Log('TUpdateStateMachine.ChangeState new' + bucStateContext.CurrentStateName );
end;
-
{ IdleState }
procedure IdleState.Enter;
@@ -702,6 +678,11 @@ procedure IdleState.HandleCheck;
Result : TRemoteUpdateCheckResult;
begin
+ {##### For Testing only just advancing to downloading ####}
+ ChangeState(UpdateAvailableState);
+ {#### End of Testing ### };
+
+
{ Make a HTTP request out and see if updates are available for now do
this all in the Idle HandleCheck message. But could be broken into an
@@ -710,33 +691,30 @@ procedure IdleState.HandleCheck;
// If handle check event force check
- CheckForUpdates := TRemoteUpdateCheck.Create(True);
- try
- Result:= CheckForUpdates.Run;
- finally
- CheckForUpdates.Free;
- end;
+ //CheckForUpdates := TRemoteUpdateCheck.Create(True);
+ //try
+ // Result:= CheckForUpdates.Run;
+ // finally
+ // CheckForUpdates.Free;
+ // end;
{ Response OK and Update is available }
- if Result = wucSuccess then
- begin
- ChangeState(UpdateAvailableState);
- end;
- // else staty in idle state
-end;
-
-procedure IdleState.HandleDownload;
-begin
+ // if Result = wucSuccess then
+ // begin
+ // ChangeState(UpdateAvailableState);
+ // end;
+ // else staty in idle state
end;
function IdleState.HandleKmShell;
var
CheckForUpdates: TRemoteUpdateCheck;
UpdateCheckResult : TRemoteUpdateCheckResult;
-const CheckPeriod: Integer = 7; // Days between checking for updates
+//const CheckPeriod: Integer = 7; // Days between checking for updates
begin
// Check if auto updates enable and if scheduled time has expired
+ KL.Log('IdleState.HandleKmShell');
if ConfigCheckContinue then
begin
CheckForUpdates := TRemoteUpdateCheck.Create(True);
@@ -759,11 +737,6 @@ procedure IdleState.HandleInstall;
end;
-procedure IdleState.HandleMSIInstallComplete;
-begin
-
-end;
-
procedure IdleState.HandleAbort;
begin
@@ -804,16 +777,11 @@ procedure UpdateAvailableState.HandleCheck;
end;
-procedure UpdateAvailableState.HandleDownload;
-begin
-
-end;
-
function UpdateAvailableState.HandleKmShell;
begin
if bucStateContext.FAuto then
begin
- ChangeState(DownloadingState);;
+ ChangeState(DownloadingState);
end;
Result := kmShellContinue;
end;
@@ -823,11 +791,6 @@ procedure UpdateAvailableState.HandleInstall;
end;
-procedure UpdateAvailableState.HandleMSIInstallComplete;
-begin
-
-end;
-
procedure UpdateAvailableState.HandleAbort;
begin
@@ -869,16 +832,14 @@ procedure DownloadingState.HandleCheck;
end;
-procedure DownloadingState.HandleDownload;
-var DownloadResult : Boolean;
-begin
- // We are already downloading do nothing
-end;
-
function DownloadingState.HandleKmShell;
var DownloadResult : Boolean;
begin
- DownloadResult := DownloadUpdatesBackground;
+ {## for testing log that we would download }
+ KL.Log('DownloadingState.HandleKmshell test code continue');
+ DownloadResult := True;
+ { End testing}
+ //DownloadResult := DownloadUpdatesBackground;
// TODO check if keyman is running then send to Waiting Restart
if DownloadResult then
begin
@@ -906,11 +867,6 @@ procedure DownloadingState.HandleInstall;
ChangeState(InstallingState);
end;
-procedure DownloadingState.HandleMSIInstallComplete;
-begin
-
-end;
-
procedure DownloadingState.HandleAbort;
begin
end;
@@ -926,7 +882,6 @@ function DownloadingState.StateName;
Result := 'DownloadingState';
end;
-
function DownloadingState.DownloadUpdatesBackground: Boolean;
var
DownloadBackGroundSavePath : String;
@@ -946,7 +901,6 @@ function DownloadingState.DownloadUpdatesBackground: Boolean;
// kmcom.Keyboards.Apply;
// kmcom.Packages.Refresh;
// end;
-
finally
DownloadUpdate.Free;
end;
@@ -956,7 +910,8 @@ function DownloadingState.DownloadUpdatesBackground: Boolean;
procedure WaitingRestartState.Enter;
begin
- // Enter DownloadingState
+ // Enter WaitingRestartState
+ KL.Log('WaitingRestartState.HandleKmShell Enter');
bucStateContext.SetRegistryState(usWaitingRestart);
end;
@@ -970,11 +925,6 @@ procedure WaitingRestartState.HandleCheck;
end;
-procedure WaitingRestartState.HandleDownload;
-begin
-
-end;
-
function WaitingRestartState.HandleKmShell;
var
SavedPath : String;
@@ -985,7 +935,7 @@ function WaitingRestartState.HandleKmShell;
if HasKeymanRun then
begin
KL.Log('WaitingRestartState.HandleKmShell Keyman Has Run');
- Result := kmShellExit;
+ Result := kmShellContinue;
// Exit; // Exit is not wokring for some reason.
// this else is only here because the exit is not working.
end
@@ -1017,11 +967,6 @@ procedure WaitingRestartState.HandleInstall;
end;
-procedure WaitingRestartState.HandleMSIInstallComplete;
-begin
-
-end;
-
procedure WaitingRestartState.HandleAbort;
begin
@@ -1032,7 +977,6 @@ procedure WaitingRestartState.HandleInstallNow;
bucStateContext.SetRegistryInstallMode(True);
// Notify User to install
ChangeState(InstallingState);
-
end;
function WaitingRestartState.StateName;
@@ -1042,7 +986,6 @@ function WaitingRestartState.StateName;
end;
{ InstallingState }
-
function InstallingState.DoInstallPackage(Package: TUpdateStateMachineParamsPackage): Boolean;
var
FPackage: IKeymanPackageFile2;
@@ -1136,16 +1079,11 @@ procedure InstallingState.Enter;
end;
procedure InstallingState.Exit;
-begin
- // Exit DownloadingState
-end;
-
-procedure InstallingState.HandleCheck;
begin
end;
-procedure InstallingState.HandleDownload;
+procedure InstallingState.HandleCheck;
begin
end;
@@ -1163,11 +1101,6 @@ procedure InstallingState.HandleInstall;
end;
-procedure InstallingState.HandleMSIInstallComplete;
-begin
-
-end;
-
procedure InstallingState.HandleAbort;
begin
ChangeState(IdleState);
@@ -1180,7 +1113,6 @@ procedure InstallingState.HandleInstallNow;
function InstallingState.StateName;
begin
-
Result := 'InstallingState';
end;
@@ -1188,21 +1120,15 @@ function InstallingState.StateName;
procedure RetryState.Enter;
begin
- // Enter DownloadingState
bucStateContext.SetRegistryState(usRetry);
end;
procedure RetryState.Exit;
-begin
- // Exit DownloadingState
-end;
-
-procedure RetryState.HandleCheck;
begin
end;
-procedure RetryState.HandleDownload;
+procedure RetryState.HandleCheck;
begin
end;
@@ -1218,11 +1144,6 @@ procedure RetryState.HandleInstall;
end;
-procedure RetryState.HandleMSIInstallComplete;
-begin
-
-end;
-
procedure RetryState.HandleAbort;
begin
@@ -1251,7 +1172,7 @@ procedure PostInstallState.Enter;
procedure PostInstallState.Exit;
begin
- // Exit downloading state
+
end;
procedure PostInstallState.HandleCheck;
@@ -1259,11 +1180,6 @@ procedure PostInstallState.HandleCheck;
// Handle Check
end;
-procedure PostInstallState.HandleDownload;
-begin
- // Handle Download
-end;
-
function PostInstallState.HandleKmShell;
begin
// TODO: #10210 have a counter if we get called in this state
@@ -1351,5 +1267,4 @@ function ConfigCheckContinue: Boolean;
end;
end;
-
end.
diff --git a/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas b/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas
index 77fe071786d..4eea8adb592 100644
--- a/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas
+++ b/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas
@@ -37,7 +37,7 @@ function UpdateState: Boolean;
Result := False;
UpdateStr := 'usPostInstall';
//KL.Log('SetBackgroundState State Entry');
- if RegOpenKeyEx(HKEY_LOCAL_MACHINE, PChar(SRegKey_KeymanEngine_LM), 0, KEY_ALL_ACCESS, hk) = ERROR_SUCCESS then
+ if RegOpenKeyEx(HKEY_LOCAL_MACHINE, PChar(SRegKey_KeymanEngine_CU), 0, KEY_ALL_ACCESS, hk) = ERROR_SUCCESS then
begin
try
if RegSetValueEx(hk, PChar(SRegValue_Update_State), 0, REG_SZ, PWideChar(UpdateStr), Length(UpdateStr) * SizeOf(Char)) = ERROR_SUCCESS then
From a7736a07d9d2476a225c5df532d464dbf744b407 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Wed, 14 Aug 2024 13:54:45 +1000
Subject: [PATCH 18/43] feat(windows): remove duplicate localisation string id
---
windows/src/desktop/kmshell/xml/strings.xml | 8 --------
1 file changed, 8 deletions(-)
diff --git a/windows/src/desktop/kmshell/xml/strings.xml b/windows/src/desktop/kmshell/xml/strings.xml
index 487a818ddfc..b963ae63be4 100644
--- a/windows/src/desktop/kmshell/xml/strings.xml
+++ b/windows/src/desktop/kmshell/xml/strings.xml
@@ -571,14 +571,6 @@
Diagnostics
-
-
-
- Check for Updates
-
-
-
-
From 98cd1f5b69455ff55c98ecb46a43f1e9b4d6f6de Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Fri, 16 Aug 2024 11:10:44 +1000
Subject: [PATCH 19/43] feat(windows): add handledownload back in
Fix all the old style with do code blocks to create the object
Add handledownload back in as I realised you need to otherwise
you can neatly stop multiple downloads occuring.
---
.../main/Keyman.System.UpdateStateMachine.pas | 224 +++++++-----------
windows/src/desktop/kmshell/main/initprog.pas | 9 +-
2 files changed, 100 insertions(+), 133 deletions(-)
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
index 589bca7582e..f7cfd9ef288 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
@@ -97,7 +97,7 @@ TState = class abstract
procedure Exit; virtual; abstract;
procedure HandleCheck; virtual; abstract;
function HandleKmShell : Integer; virtual; abstract;
- procedure HandleInstall; virtual; abstract;
+ procedure HandleDownload; virtual; abstract;
procedure HandleAbort; virtual; abstract;
procedure HandleInstallNow; virtual; abstract;
@@ -113,19 +113,21 @@ IdleState = class(TState)
procedure Exit; override;
procedure HandleCheck; override;
function HandleKmShell : Integer; override;
- procedure HandleInstall; override;
+ procedure HandleDownload; override;
procedure HandleAbort; override;
procedure HandleInstallNow; override;
function StateName: string; override;
end;
UpdateAvailableState = class(TState)
+ private
+ procedure StartDownloadProcess;
public
procedure Enter; override;
procedure Exit; override;
procedure HandleCheck; override;
function HandleKmShell : Integer; override;
- procedure HandleInstall; override;
+ procedure HandleDownload; override;
procedure HandleAbort; override;
procedure HandleInstallNow; override;
function StateName: string; override;
@@ -139,7 +141,7 @@ DownloadingState = class(TState)
procedure Exit; override;
procedure HandleCheck; override;
function HandleKmShell : Integer; override;
- procedure HandleInstall; override;
+ procedure HandleDownload; override;
procedure HandleAbort; override;
procedure HandleInstallNow; override;
function StateName: string; override;
@@ -151,7 +153,7 @@ WaitingRestartState = class(TState)
procedure Exit; override;
procedure HandleCheck; override;
function HandleKmShell : Integer; override;
- procedure HandleInstall; override;
+ procedure HandleDownload; override;
procedure HandleAbort; override;
procedure HandleInstallNow; override;
function StateName: string; override;
@@ -175,7 +177,7 @@ InstallingState = class(TState)
procedure Exit; override;
procedure HandleCheck; override;
function HandleKmShell : Integer; override;
- procedure HandleInstall; override;
+ procedure HandleDownload; override;
procedure HandleAbort; override;
procedure HandleInstallNow; override;
function StateName: string; override;
@@ -187,7 +189,7 @@ RetryState = class(TState)
procedure Exit; override;
procedure HandleCheck; override;
function HandleKmShell : Integer; override;
- procedure HandleInstall; override;
+ procedure HandleDownload; override;
procedure HandleAbort; override;
procedure HandleInstallNow; override;
function StateName: string; override;
@@ -201,7 +203,7 @@ PostInstallState = class(TState)
procedure Exit; override;
procedure HandleCheck; override;
function HandleKmShell : Integer; override;
- procedure HandleInstall; override;
+ procedure HandleDownload; override;
procedure HandleAbort; override;
procedure HandleInstallNow; override;
function StateName: string; override;
@@ -244,7 +246,6 @@ TUpdateStateMachine = class
function checkUpdateSchedule : Boolean;
function SetRegistryState (Update : TUpdateState): Boolean;
- function SetRegistryInstallMode (InstallMode : Boolean): Boolean;
protected
property State: TStateClass read GetState write SetState;
@@ -255,14 +256,13 @@ TUpdateStateMachine = class
procedure HandleCheck;
function HandleKmShell : Integer;
- procedure HandleInstall;
+ procedure HandleDownload;
procedure HandleAbort;
procedure HandleInstallNow;
function CurrentStateName: string;
property ShowErrors: Boolean read FShowErrors write FShowErrors;
function CheckRegistryState : TUpdateState;
- function CheckRegistryInstallMode : Boolean;
end;
@@ -363,16 +363,17 @@ destructor TUpdateStateMachine.Destroy;
procedure TUpdateStateMachine.SavePackageUpgradesToDownloadTempPath;
var
i: Integer;
+ StringList : TStringList;
begin
- with TStringList.Create do
+ StringList := TStringList.Create;
try
for i := 0 to High(FParams.Packages) do
if FParams.Packages[i].NewID <> '' then
- Add(FParams.Packages[i].NewID+'='+FParams.Packages[i].ID);
- if Count > 0 then
- SaveToFile(DownloadTempPath + SPackageUpgradeFileName);
+ StringList.Add(FParams.Packages[i].NewID+'='+FParams.Packages[i].ID);
+ if StringList.Count > 0 then
+ StringList.SaveToFile(DownloadTempPath + SPackageUpgradeFileName);
finally
- Free;
+ StringList.Free;
end;
end;
@@ -430,98 +431,52 @@ function TUpdateStateMachine.SetRegistryState(Update : TUpdateState): Boolean;
end;
-function TUpdateStateMachine.CheckRegistryState : TUpdateState; // I2329
+function TUpdateStateMachine.CheckRegistryState: TUpdateState; // I2329
var
- UpdateState : TUpdateState;
+ UpdateState: TUpdateState;
+ Registry: TRegistryErrorControlled;
begin
// We will use a registry flag to maintain the state of the background update
// check the registry value
- with TRegistryErrorControlled.Create do // I2890
+ Registry := TRegistryErrorControlled.Create; // I2890
try
- RootKey := HKEY_CURRENT_USER;
- if OpenKeyReadOnly(SRegKey_KeymanEngine_LM) and ValueExists(SRegValue_Update_State) then
- begin
- UpdateState := TUpdateState(GetEnumValue(TypeInfo(TUpdateState), ReadString(SRegValue_Update_State)));
- KL.Log('CheckRegistryState State is:[' + ReadString(SRegValue_Update_State) + ']');
- end
+ Registry.RootKey := HKEY_CURRENT_USER;
+ if Registry.OpenKeyReadOnly(SRegKey_KeymanEngine_LM) and Registry.ValueExists(SRegValue_Update_State) then
+ begin
+ UpdateState := TUpdateState(GetEnumValue(TypeInfo(TUpdateState), Registry.ReadString(SRegValue_Update_State)));
+ KL.Log('CheckRegistryState State is:[' + Registry.ReadString(SRegValue_Update_State) + ']');
+ end
else
- begin
- UpdateState := usIdle; // do we need a unknown state ?
- KL.Log('CheckRegistryState State reg value not found default:[' + ReadString(SRegValue_Update_State) + ']');
- end
- finally
- Free;
- end;
- Result := UpdateState;
-end;
-
-function TUpdateStateMachine.SetRegistryInstallMode (InstallMode : Boolean): Boolean;
-var
- InstallModeStr : string;
-begin
-
- Result := False;
- with TRegistryErrorControlled.Create do
- try
- RootKey := HKEY_CURRENT_USER;
- KL.Log('SetRegistryState State Entry');
- if OpenKey(SRegKey_KeymanEngine_LM, True) then
begin
- InstallModeStr := BoolToStr(InstallMode, True);
- WriteString(SRegValue_Install_Mode, InstallModeStr);
- KL.Log('SetRegistryInstallMode is:[' + InstallModeStr + ']');
+ UpdateState := usIdle; // do we need a unknown state ?
+ KL.Log('CheckRegistryState State reg value not found default:[' + Registry.ReadString(SRegValue_Update_State) + ']');
end;
- Result := True;
finally
- Free;
+ Registry.Free;
end;
-end;
-
-function TUpdateStateMachine.CheckRegistryInstallMode : Boolean;
-var
- InstallMode : Boolean;
-
-begin
- // We will use a registry flag to maintain the install mode background/foreground
-
- InstallMode := False;
- // check the registry value
- with TRegistryErrorControlled.Create do // I2890
- try
- RootKey := HKEY_CURRENT_USER;
- if OpenKeyReadOnly(SRegKey_KeymanEngine_LM) and ValueExists(SRegValue_Install_Mode) then
- begin
- InstallMode := StrToBool(ReadString(SRegValue_Install_Mode));
- KL.Log('CheckRegistryState State is:[' + ReadString(SRegValue_Update_State) + ']');
- end
- else
- begin
- InstallMode := False; // default to background
- KL.Log('CheckRegistryInstallMode reg value not found default:[ False ]');
- end
- finally
- Free;
- end;
- Result := InstallMode;
+ Result := UpdateState;
end;
function TUpdateStateMachine.CheckUpdateSchedule: Boolean;
+var
+ RegistryErrorControlled :TRegistryErrorControlled;
begin
try
Result := False;
- with TRegistryErrorControlled.Create do
+ RegistryErrorControlled := TRegistryErrorControlled.Create;
+
try
- if OpenKeyReadOnly(SRegKey_KeymanDesktop_CU) then
+ if RegistryErrorControlled.OpenKeyReadOnly(SRegKey_KeymanDesktop_CU) then
begin
- if ValueExists(SRegValue_CheckForUpdates) and not ReadBool(SRegValue_CheckForUpdates) and not FForce then
+ if RegistryErrorControlled.ValueExists(SRegValue_CheckForUpdates) and not RegistryErrorControlled.ReadBool(SRegValue_CheckForUpdates) and not FForce then
begin
Result := False;
Exit;
end;
- if ValueExists(SRegValue_LastUpdateCheckTime) and (Now - ReadDateTime(SRegValue_LastUpdateCheckTime) < 1) and not FForce then
+ if RegistryErrorControlled.ValueExists(SRegValue_LastUpdateCheckTime) and (Now - RegistryErrorControlled.ReadDateTime(SRegValue_LastUpdateCheckTime) < 1) and not FForce then
begin
Result := False;
Exit;
@@ -530,7 +485,7 @@ function TUpdateStateMachine.CheckUpdateSchedule: Boolean;
Result := True;
end;
finally
- Free;
+ RegistryErrorControlled.Free;
end;
except
{ we will not run the check if an error occurs reading the settings }
@@ -626,9 +581,9 @@ function TUpdateStateMachine.HandleKmShell;
Result := CurrentState.HandleKmShell;
end;
-procedure TUpdateStateMachine.HandleInstall;
+procedure TUpdateStateMachine.HandleDownload;
begin
- CurrentState.HandleInstall;
+ CurrentState.HandleDownload;
end;
procedure TUpdateStateMachine.HandleAbort;
@@ -732,9 +687,9 @@ function IdleState.HandleKmShell;
Result := kmShellContinue;
end;
-procedure IdleState.HandleInstall;
+procedure IdleState.HandleDownload;
begin
-
+ // Do Nothing
end;
procedure IdleState.HandleAbort;
@@ -744,7 +699,6 @@ procedure IdleState.HandleAbort;
procedure IdleState.HandleInstallNow;
begin
- bucStateContext.SetRegistryInstallMode(True);
bucStateContext.CurrentState.HandleCheck;
// TODO: How do we notify the command line no update available
end;
@@ -757,13 +711,25 @@ function IdleState.StateName;
{ UpdateAvailableState }
+
+procedure UpdateAvailableState.StartDownloadProcess;
+var DownloadResult, FResult : Boolean;
+RootPath: string;
+begin
+ // call seperate process
+ RootPath := ExtractFilePath(ParamStr(0));
+ FResult := TUtilExecute.ShellCurrentUser(0, ParamStr(0), IncludeTrailingPathDelimiter(RootPath), '-bd');
+ if not FResult then
+ KL.Log('TrmfMain: Executing KMshell for download updated Failed');
+end;
+
procedure UpdateAvailableState.Enter;
begin
// Enter UpdateAvailableState
bucStateContext.SetRegistryState(usUpdateAvailable);
if bucStateContext.FAuto then
begin
- ChangeState(DownloadingState);
+ StartDownloadProcess;
end;
end;
@@ -781,14 +747,16 @@ function UpdateAvailableState.HandleKmShell;
begin
if bucStateContext.FAuto then
begin
- ChangeState(DownloadingState);
+ // we will use a new kmshell process to enable
+ // the download as background process.
+ StartDownloadProcess;
end;
Result := kmShellContinue;
end;
-procedure UpdateAvailableState.HandleInstall;
+procedure UpdateAvailableState.HandleDownload;
begin
-
+ ChangeState(DownloadingState);
end;
procedure UpdateAvailableState.HandleAbort;
@@ -798,7 +766,6 @@ procedure UpdateAvailableState.HandleAbort;
procedure UpdateAvailableState.HandleInstallNow;
begin
- bucStateContext.SetRegistryInstallMode(True);
ChangeState(DownloadingState);
end;
@@ -815,27 +782,7 @@ procedure DownloadingState.Enter;
begin
// Enter DownloadingState
bucStateContext.SetRegistryState(usDownloading);
- // call seperate process
- RootPath := ExtractFilePath(ParamStr(0));
- FResult := TUtilExecute.ShellCurrentUser(0, ParamStr(0), IncludeTrailingPathDelimiter(RootPath), '');
- if not FResult then
- KL.Log('TrmfMain: Executing KMshell for download updated Failed');
-end;
-
-procedure DownloadingState.Exit;
-begin
- // Exit DownloadingState
-end;
-
-procedure DownloadingState.HandleCheck;
-begin
-
-end;
-
-function DownloadingState.HandleKmShell;
-var DownloadResult : Boolean;
-begin
- {## for testing log that we would download }
+ {## for testing log that we would download }
KL.Log('DownloadingState.HandleKmshell test code continue');
DownloadResult := True;
{ End testing}
@@ -846,25 +793,41 @@ function DownloadingState.HandleKmShell;
if HasKeymanRun then
begin
ChangeState(WaitingRestartState);
- Result := kmShellContinue;
end
else
begin
ChangeState(InstallingState);
- Result := kmShellExit;
end;
end
else
begin
ChangeState(RetryState);
- Result := kmShellContinue;
end;
end;
-procedure DownloadingState.HandleInstall;
+procedure DownloadingState.Exit;
begin
- ChangeState(InstallingState);
+ // Exit DownloadingState
+end;
+
+procedure DownloadingState.HandleCheck;
+begin
+
+end;
+
+function DownloadingState.HandleKmShell;
+begin
+ // Downloading state, in other process, so continue
+ Result := kmShellContinue;
+end;
+
+procedure DownloadingState.HandleDownload;
+var DownloadResult, FResult : Boolean;
+RootPath: string;
+begin
+ // Enter Already Downloading
+ KL.Log('DownloadingState.HandleDownload already downloading');
end;
procedure DownloadingState.HandleAbort;
@@ -873,8 +836,7 @@ procedure DownloadingState.HandleAbort;
procedure DownloadingState.HandleInstallNow;
begin
- bucStateContext.SetRegistryInstallMode(True);
- // Continue downloading
+
end;
function DownloadingState.StateName;
@@ -962,7 +924,7 @@ function WaitingRestartState.HandleKmShell;
end;
end;
-procedure WaitingRestartState.HandleInstall;
+procedure WaitingRestartState.HandleDownload;
begin
end;
@@ -974,8 +936,9 @@ procedure WaitingRestartState.HandleAbort;
procedure WaitingRestartState.HandleInstallNow;
begin
- bucStateContext.SetRegistryInstallMode(True);
- // Notify User to install
+ // TODO: Check if keyman has run, and error trying to install
+ // now when windows needs a restart ask the user if they
+ // want to restart now, if users says no stay in this (waitingrestart) state
ChangeState(InstallingState);
end;
@@ -1096,7 +1059,7 @@ function InstallingState.HandleKmShell;
Result := kmShellContinue;
end;
-procedure InstallingState.HandleInstall;
+procedure InstallingState.HandleDownload;
begin
end;
@@ -1139,7 +1102,7 @@ function RetryState.HandleKmShell;
Result := kmShellContinue
end;
-procedure RetryState.HandleInstall;
+procedure RetryState.HandleDownload;
begin
end;
@@ -1151,7 +1114,6 @@ procedure RetryState.HandleAbort;
procedure RetryState.HandleInstallNow;
begin
- bucStateContext.SetRegistryInstallMode(True);
// TODO: #10038 handle retry counts
ChangeState(InstallingState);
end;
@@ -1182,15 +1144,13 @@ procedure PostInstallState.HandleCheck;
function PostInstallState.HandleKmShell;
begin
- // TODO: #10210 have a counter if we get called in this state
- // too many time abort.
HandleMSIInstallComplete;
Result := kmShellContinue;
end;
-procedure PostInstallState.HandleInstall;
+procedure PostInstallState.HandleDownload;
begin
- // Handle Install
+ // Do Nothing
end;
procedure PostInstallState.HandleMSIInstallComplete;
diff --git a/windows/src/desktop/kmshell/main/initprog.pas b/windows/src/desktop/kmshell/main/initprog.pas
index 284695bfd48..88d08ef4d17 100644
--- a/windows/src/desktop/kmshell/main/initprog.pas
+++ b/windows/src/desktop/kmshell/main/initprog.pas
@@ -83,6 +83,7 @@ procedure Main(Owner: TComponent = nil);
fmUpgradeKeyboards, fmOnlineUpdateCheck,// I2548
fmOnlineUpdateAdmin, fmTextEditor,
fmBackgroundUpdateCheck,
+ fmBackgroundDownload,
fmApplyInstallNow,
fmFirstRun, // I2562
fmKeyboardWelcome, // I2569
@@ -254,6 +255,7 @@ function Init(var FMode: TKMShellMode; KeyboardFileNames: TStrings; var FSilent,
else if s = '-t' then FMode := fmTextEditor
else if s = '-ouc' then FMode := fmOnlineUpdateCheck
else if s = '-buc' then FMode := fmBackgroundUpdateCheck
+ else if s = '-bd' then FMode := fmBackgroundDownload
else if s = '-an' then FMode := fmApplyInstallNow
else if s = '-basekeyboard' then FMode := fmBaseKeyboard // I4169
else if s = '-nowelcome' then FNoWelcome := True
@@ -443,9 +445,14 @@ procedure RunKMCOM(FMode: TKMShellMode; KeyboardFileNames: TStrings; FSilent, FF
BUpdateSM.HandleCheck;
Exit;
end
+ else if (FMode = fmBackgroundDownload) then
+ begin
+ BUpdateSM.HandleDownload;
+ Exit;
+ end
else if (FMode = fmApplyInstallNow) then
begin
- BUpdateSM.HandleInstall;
+ BUpdateSM.HandleInstallNow;
Exit;
end
else
From 6a305c168bf54c24bac1e335f84b81c721715811 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Fri, 16 Aug 2024 13:26:02 +1000
Subject: [PATCH 20/43] feat(windows): remove uneeded registry keys
---
common/windows/delphi/general/RegistryKeys.pas | 2 --
1 file changed, 2 deletions(-)
diff --git a/common/windows/delphi/general/RegistryKeys.pas b/common/windows/delphi/general/RegistryKeys.pas
index c20c4baa0f2..efd60b4148d 100644
--- a/common/windows/delphi/general/RegistryKeys.pas
+++ b/common/windows/delphi/general/RegistryKeys.pas
@@ -178,9 +178,7 @@ interface
SRegValue_AvailableLanguages = 'available languages'; //CU
SRegValue_CurrentLanguage = 'current language'; //CU
- SRegValue_Install_Update = 'install update';
SRegValue_Update_State = 'update state';
- SRegValue_Install_Mode = 'install mode';
{ Privacy }
From e2ec08ca9b24925ef1c01acb167c68968ac0846f Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Tue, 27 Aug 2024 18:03:57 +1000
Subject: [PATCH 21/43] feat(windows): remove klogging enable
---
common/windows/delphi/general/klog.pas | 2 +-
core/tests/unit/kmx/fixtures/meson.build | 4 ++--
core/tests/unit/meson.build | 6 +++---
windows/src/desktop/insthelp/insthelp.dpr | 7 +------
windows/src/desktop/insthelp/insthelp.dproj | 5 -----
5 files changed, 7 insertions(+), 17 deletions(-)
diff --git a/common/windows/delphi/general/klog.pas b/common/windows/delphi/general/klog.pas
index 2756c63893d..2787f8b3472 100644
--- a/common/windows/delphi/general/klog.pas
+++ b/common/windows/delphi/general/klog.pas
@@ -23,7 +23,7 @@
interface
-{$DEFINE KLOGGING}
+{DEFINE KLOGGING}
{$IFDEF KLOGGING}
uses
diff --git a/core/tests/unit/kmx/fixtures/meson.build b/core/tests/unit/kmx/fixtures/meson.build
index 711bae6f806..e40c30ad462 100644
--- a/core/tests/unit/kmx/fixtures/meson.build
+++ b/core/tests/unit/kmx/fixtures/meson.build
@@ -2,5 +2,5 @@ if node.found()
# Note: if node is not available, we cannot build the keyboards; build.sh
# emits a warning that the 'ldml' keyboard tests will be skipped; that
# includes these tests for now
- #subdir('binary')
-endif
+ subdir('binary')
+endif
\ No newline at end of file
diff --git a/core/tests/unit/meson.build b/core/tests/unit/meson.build
index 4b0bb489e52..ee141cc12d3 100644
--- a/core/tests/unit/meson.build
+++ b/core/tests/unit/meson.build
@@ -10,6 +10,6 @@ hextobin_cmd = [node, hextobin_root]
subdir('json')
subdir('utftest')
-#subdir('kmnkbd')
-#subdir('kmx')
-#subdir('ldml')
+subdir('kmnkbd')
+subdir('kmx')
+subdir('ldml')
diff --git a/windows/src/desktop/insthelp/insthelp.dpr b/windows/src/desktop/insthelp/insthelp.dpr
index 905e4ac8eb6..ab0e47ff7db 100644
--- a/windows/src/desktop/insthelp/insthelp.dpr
+++ b/windows/src/desktop/insthelp/insthelp.dpr
@@ -8,12 +8,7 @@ uses
KeymanVersion in '..\..\..\..\common\windows\delphi\general\KeymanVersion.pas',
Keyman.System.InstHelp.KeymanStartTaskUninstall in 'Keyman.System.InstHelp.KeymanStartTaskUninstall.pas',
TaskScheduler_TLB in '..\..\global\delphi\winapi\TaskScheduler_TLB.pas',
- ErrorControlledRegistry in '..\..\..\..\common\windows\delphi\vcl\ErrorControlledRegistry.pas',
- UserMessages in '..\..\..\..\common\windows\delphi\general\UserMessages.pas',
- DebugPaths in '..\..\..\..\common\windows\delphi\general\DebugPaths.pas',
- VersionInfo in '..\..\..\..\common\windows\delphi\general\VersionInfo.pas',
- Unicode in '..\..\..\..\common\windows\delphi\general\Unicode.pas',
- KeymanPaths in '..\..\..\..\common\windows\delphi\general\KeymanPaths.pas';
+ UserMessages in '..\..\..\..\common\windows\delphi\general\UserMessages.pas';
{$R version.res}
{-R manifest.res}
diff --git a/windows/src/desktop/insthelp/insthelp.dproj b/windows/src/desktop/insthelp/insthelp.dproj
index cfb0ad37fcf..45913bd470b 100644
--- a/windows/src/desktop/insthelp/insthelp.dproj
+++ b/windows/src/desktop/insthelp/insthelp.dproj
@@ -98,11 +98,6 @@
-
-
-
-
-
Cfg_2
Base
From 4c1aa0f960bd60107dabd9b195965b7e8c3c1700 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Thu, 29 Aug 2024 15:48:43 +1000
Subject: [PATCH 22/43] feat(windows): add basic modal install form
---
.../main/Keyman.System.UpdateStateMachine.pas | 18 ++++++++++++++++--
.../desktop/kmshell/main/UfrmStartInstall.dfm | 18 ++++++++++++++++++
.../desktop/kmshell/main/UfrmStartInstall.pas | 13 ++++++-------
3 files changed, 40 insertions(+), 9 deletions(-)
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
index f7cfd9ef288..edcc649da44 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
@@ -33,6 +33,7 @@ interface
httpuploader,
Keyman.System.UpdateCheckResponse,
Keyman.System.ExecuteHistory,
+ UfrmStartInstall,
UfrmDownloadProgress;
const
@@ -891,6 +892,7 @@ function WaitingRestartState.HandleKmShell;
var
SavedPath : String;
Filenames : TStringDynArray;
+ frmStartInstall : TfrmStartInstall;
begin
KL.Log('WaitingRestartState.HandleKmShell Enter');
// Still can't go if keyman has run
@@ -918,8 +920,19 @@ function WaitingRestartState.HandleKmShell;
else
begin
KL.Log('WaitingRestartState.HandleKmShell is good to install');
- ChangeState(InstallingState);
- Result := kmShellExit;
+ // TODO Pop up toast here to ask user if we want to continue
+ frmStartInstall := TfrmStartInstall.Create(nil);
+ try
+ if frmStartInstall.ShowModal = mrOk then
+ begin
+ ChangeState(InstallingState);
+ Result := kmShellExit;
+ end
+ else
+ Result := kmShellContinue;
+ finally
+ frmStartInstall.Free;
+ end;
end;
end;
end;
@@ -994,6 +1007,7 @@ function InstallingState.DoInstallKeyman(SavePath: string) : Boolean;
KL.Log('TUpdateStateMachine.InstallingState.DoInstallKeyman SavePath:"'+ SavePath+'"');
// switch -au for auto update in silent mode.
// We will need to add the pop up that says install update now yes/no
+ // This will run the setup executable which will ask for elevated permissions
FResult := TUtilExecute.Shell(0, SavePath, '', '-au') // I3349
end
else
diff --git a/windows/src/desktop/kmshell/main/UfrmStartInstall.dfm b/windows/src/desktop/kmshell/main/UfrmStartInstall.dfm
index 4414a07c101..66d008bf2c5 100644
--- a/windows/src/desktop/kmshell/main/UfrmStartInstall.dfm
+++ b/windows/src/desktop/kmshell/main/UfrmStartInstall.dfm
@@ -13,4 +13,22 @@ object frmStartInstall: TfrmStartInstall
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
+ object Install: TButton
+ Left = 168
+ Top = 240
+ Width = 75
+ Height = 25
+ Caption = 'Install'
+ TabOrder = 0
+ OnClick = InstallClick
+ end
+ object Later: TButton
+ Left = 288
+ Top = 240
+ Width = 75
+ Height = 25
+ Caption = 'Later'
+ TabOrder = 1
+ OnClick = LaterClick
+ end
end
diff --git a/windows/src/desktop/kmshell/main/UfrmStartInstall.pas b/windows/src/desktop/kmshell/main/UfrmStartInstall.pas
index dce20b6ccef..7b88ddbeab4 100644
--- a/windows/src/desktop/kmshell/main/UfrmStartInstall.pas
+++ b/windows/src/desktop/kmshell/main/UfrmStartInstall.pas
@@ -9,11 +9,10 @@ interface
type
TfrmStartInstall = class(TfrmKeymanBase)
- LabelMessage: TLabel;
- InstallButton: TButton;
- CancelButton: TButton;
- procedure InstallButtonClick(Sender: TObject);
- procedure CancelButtonClick(Sender: TObject);
+ Install: TButton;
+ Later: TButton;
+ procedure InstallClick(Sender: TObject);
+ procedure LaterClick(Sender: TObject);
private
public
end;
@@ -25,12 +24,12 @@ implementation
{$R *.dfm}
-procedure TfrmStartInstall.InstallButtonClick(Sender: TObject);
+procedure TfrmStartInstall.InstallClick(Sender: TObject);
begin
ModalResult := mrOk;
end;
-procedure TfrmStartInstall.CancelButtonClick(Sender: TObject);
+procedure TfrmStartInstall.LaterClick(Sender: TObject);
begin
ModalResult := mrCancel;
end;
From 8df507d97d13a4b31ec82c57ac20fbae8d49e6be Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Thu, 29 Aug 2024 21:08:51 +1000
Subject: [PATCH 23/43] feat(windows): add menuframe_update image 4 config
---
.../src/desktop/kmshell/xml/menuframe_update.jpg | Bin 0 -> 1404 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 windows/src/desktop/kmshell/xml/menuframe_update.jpg
diff --git a/windows/src/desktop/kmshell/xml/menuframe_update.jpg b/windows/src/desktop/kmshell/xml/menuframe_update.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..6eeb76bfd6f64f987667906903678cbd227dc21b
GIT binary patch
literal 1404
zcmbVKX;4#F6g~;rNJ1nK5GY98NOfS^h%7}xFbV{rVAuqlfsjQhB!MIdIA9Gb&=$LZ
zwjctfRG~~Og#v|EWDx-!s-8V=2B;X
z26jM!9k4?z(If|j`i8-(u{Z!o383Ez*IJeK{{@OvrjejhADT)@vRVs3*om-fnl>57
zXaXlOOe8=!4`HrUp_C$AjqoO^LW*PjTah9u71zN7-wjiy3=c(Fgr+U{fe?Zq1h^0n
zsgM9kpamzCm1t!TxPlAHexL?1WPk=FC?|mot)(H377in%K}vqG8=)|S&~8@ovsv*A
zK`!w~BLQjQ4k}O}P23Og^F|K#y6)
zi-y?$^|lJfb%pbWJ!1D!r$$5~K2VU*Ak)ZX5{XQwQYkbhoyla-84MGanW+hj&0;W2
zElt_x919Bz=9;xumK-ZHjs*v62L=tDf<1x22CNJ0(GMb{ONrmhe}h0o!W1gan9e|i
zMpGaXjEsmRBistHvrrvKY_i#Uz7NHGznHoqg|kifMFnl+?z#>O(QU&f=XiCtG2L>l
zm9@=g2j1ITwr+QEb#s5uL$GJBub;m#AT%sIA`+dvL@JXXI-Kxy9-#(4LPLAObQ0N$bEAgx-rlPXJ?x5avgGJN4=X=ayx;aySMQ|
z=kfckK&^7o>2I+{(s|>;rn&Ix&e`XYiLXj7y4;qX@;O*=d{XydorfwUqZ$LLiXEhy
zRP6H~4f@_XnvrzoW5T%KX$Knn)U
z881h}$Y^;@LR!jjA1UqgZaLRWU;TBhppY9n^VWHybGcq+l3o?vT!z8HHaE9~wM7^t
z7LV@rt{JqZ>Mk=`xn5@Zizk9)fe9|~f(q!*6Ic6siIXny@${hh{`42#Ma!KJp!nfC*J
z-1dxDR^`y}(%8W0_9&~a)w?$;w)UD|7Hn5J>OMVisIT)Ok5_&nD|Vuk)pOOu=~D5<
Yp{(p1BS(7H*d8rVtA$gA18vyQKR^b-kN^Mx
literal 0
HcmV?d00001
From 7860ddce5b1302fbb9297f543842e4ae6201cadd Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Mon, 2 Sep 2024 20:11:23 +1000
Subject: [PATCH 24/43] feat(windows): add update to strings.xml
---
windows/src/desktop/kmshell/xml/strings.xml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/windows/src/desktop/kmshell/xml/strings.xml b/windows/src/desktop/kmshell/xml/strings.xml
index 824ce9dfc7c..62125399ff7 100644
--- a/windows/src/desktop/kmshell/xml/strings.xml
+++ b/windows/src/desktop/kmshell/xml/strings.xml
@@ -695,6 +695,10 @@ keyboard that you use in Windows. Keyman keyboards will adapt automatically to
+
+
+
+ Update
From 1667cfed097a8ea618138f5896d6eeb05299d811 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Tue, 3 Sep 2024 14:38:09 +1000
Subject: [PATCH 25/43] feat(windows): clean up ready for review
---
common/windows/delphi/general/RegistryKeys.pas | 2 --
.../src/desktop/kmshell/main/BackgroundUpdateStateDiagram.md | 4 ++--
windows/src/desktop/kmshell/main/UfrmMain.pas | 2 --
windows/src/desktop/kmshell/main/initprog.pas | 2 ++
.../insthelper/Keyman.System.Install.EnginePostInstall.pas | 3 +--
windows/src/engine/keyman/main.pas | 3 ---
6 files changed, 5 insertions(+), 11 deletions(-)
diff --git a/common/windows/delphi/general/RegistryKeys.pas b/common/windows/delphi/general/RegistryKeys.pas
index efd60b4148d..0d1ba5484ee 100644
--- a/common/windows/delphi/general/RegistryKeys.pas
+++ b/common/windows/delphi/general/RegistryKeys.pas
@@ -162,7 +162,6 @@ interface
SRegKey_KeymanDesktop_CU = SRegKey_KeymanDesktopRoot_CU;
SRegKey_KeymanDesktop_LM = SRegKey_KeymanDesktopRoot_LM;
-
{ Other Keyman Settings }
SRegValue_DeadkeyConversionMode = 'deadkey conversion mode'; // CU // I4552
@@ -300,7 +299,6 @@ interface
SRegKey_KeymanDeveloperRoot_LM = SRegKey_KeymanRoot_LM + '\Keyman Developer'; // LM CU
SRegKey_KeymanDeveloper_LM = SRegKey_KeymanDeveloperRoot_LM; // LM CU
-
SRegKey_IDE_CU = SRegKey_KeymanDeveloper_CU + '\IDE'; // CU
SRegKey_IDEDock_CU = SRegKey_IDE_CU + '\Dock'; // CU
SRegKey_IDEFiles_CU = SRegKey_IDE_CU + '\Files'; // CU
diff --git a/windows/src/desktop/kmshell/main/BackgroundUpdateStateDiagram.md b/windows/src/desktop/kmshell/main/BackgroundUpdateStateDiagram.md
index 59c735eeb2a..160d54b2c10 100644
--- a/windows/src/desktop/kmshell/main/BackgroundUpdateStateDiagram.md
+++ b/windows/src/desktop/kmshell/main/BackgroundUpdateStateDiagram.md
@@ -6,6 +6,6 @@ stateDiagram
Downloading --> Installing
Downloading --> WaitingRestart
WaitingRestart --> Installing
- Installing --> WaitingPostInstall
- WaitingPostInstall --> Idle
+ Installing --> PostInstall
+ PostInstall --> Idle
```
diff --git a/windows/src/desktop/kmshell/main/UfrmMain.pas b/windows/src/desktop/kmshell/main/UfrmMain.pas
index 5df7c2aa6a7..5f16aeb047e 100644
--- a/windows/src/desktop/kmshell/main/UfrmMain.pas
+++ b/windows/src/desktop/kmshell/main/UfrmMain.pas
@@ -847,8 +847,6 @@ procedure TfrmMain.Update_ApplyNow;
KL.Log('TrmfMain: Executing Update_ApplyNow Failed');
end;
-
-
procedure TfrmMain.TntFormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
inherited;
diff --git a/windows/src/desktop/kmshell/main/initprog.pas b/windows/src/desktop/kmshell/main/initprog.pas
index 88d08ef4d17..79f1f60a60c 100644
--- a/windows/src/desktop/kmshell/main/initprog.pas
+++ b/windows/src/desktop/kmshell/main/initprog.pas
@@ -253,6 +253,8 @@ function Init(var FMode: TKMShellMode; KeyboardFileNames: TStrings; var FSilent,
else if s = '-?' then FMode := fmHelpKMShell
else if s = '-h' then FMode := fmHelp
else if s = '-t' then FMode := fmTextEditor
+ //TODO: will remove -ouc not used
+ // -buc uses the Statemachine can be used for external scripts to force a check
else if s = '-ouc' then FMode := fmOnlineUpdateCheck
else if s = '-buc' then FMode := fmBackgroundUpdateCheck
else if s = '-bd' then FMode := fmBackgroundDownload
diff --git a/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas b/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas
index 4eea8adb592..6b0fe419b20 100644
--- a/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas
+++ b/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas
@@ -36,7 +36,6 @@ function UpdateState: Boolean;
Result := False;
UpdateStr := 'usPostInstall';
- //KL.Log('SetBackgroundState State Entry');
if RegOpenKeyEx(HKEY_LOCAL_MACHINE, PChar(SRegKey_KeymanEngine_CU), 0, KEY_ALL_ACCESS, hk) = ERROR_SUCCESS then
begin
try
@@ -54,7 +53,7 @@ function UpdateState: Boolean;
end
else
begin
- // couldn't open registry key
+ // TODO: couldn't open registry key
end;
end;
diff --git a/windows/src/engine/keyman/main.pas b/windows/src/engine/keyman/main.pas
index 9f1dbc9b3f0..8fcce5c0145 100644
--- a/windows/src/engine/keyman/main.pas
+++ b/windows/src/engine/keyman/main.pas
@@ -78,11 +78,8 @@ procedure RunProgram;
hMutex: Cardinal;
begin
- KL.Log('Keyman RunProgram');
if not ValidateParameters(FCommand) then Exit;
- // TODO set atom application running
- KL.Log('Calling RecordKeymanStarted');
RecordKeymanStarted;
hProgramMutex := CreateMutex(nil, False, 'KeymanEXE70');
From 85a998bb732a8e072b3635a30c86adda91f943eb Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Tue, 3 Sep 2024 15:31:49 +1000
Subject: [PATCH 26/43] feat(windows): rename executehistory module for clarity
---
...tory.pas => Keyman.System.ExecutionHistory.pas} | 14 +++++++++-----
windows/src/desktop/kmshell/kmshell.dpr | 2 +-
windows/src/desktop/kmshell/kmshell.dproj | 8 ++++----
.../main/Keyman.System.UpdateStateMachine.pas | 2 +-
4 files changed, 15 insertions(+), 11 deletions(-)
rename common/windows/delphi/general/{Keyman.System.ExecuteHistory.pas => Keyman.System.ExecutionHistory.pas} (77%)
diff --git a/common/windows/delphi/general/Keyman.System.ExecuteHistory.pas b/common/windows/delphi/general/Keyman.System.ExecutionHistory.pas
similarity index 77%
rename from common/windows/delphi/general/Keyman.System.ExecuteHistory.pas
rename to common/windows/delphi/general/Keyman.System.ExecutionHistory.pas
index e146f8fa691..08fd015c88d 100644
--- a/common/windows/delphi/general/Keyman.System.ExecuteHistory.pas
+++ b/common/windows/delphi/general/Keyman.System.ExecutionHistory.pas
@@ -1,4 +1,12 @@
-unit Keyman.System.ExecuteHistory;
+unit Keyman.System.ExecutionHistory;
+
+{
+ Copyright: © SIL International.
+
+ This module provides functionality to track the execution state of the Keyman
+ engine. It uses a global atom to record whether Keyman has started during the
+ current session and checks if it has previously run.
+}
interface
@@ -25,7 +33,6 @@ function RecordKeymanStarted : Boolean;
if GetLastError <> ERROR_FILE_NOT_FOUND then
RaiseLastOSError;
atom := GlobalAddAtom(AtomName);
- KL.Log('RecordKeymanStarted: True');
Result := True;
if atom = 0 then
RaiseLastOSError;
@@ -47,13 +54,10 @@ function HasKeymanRun : Boolean;
begin
if GetLastError <> ERROR_SUCCESS then
RaiseLastOSError;
-
- KL.Log('HasKeymanRun: Keyman Has Run');
Result := True;
end
else
begin
- KL.Log('HasKeymanRun: Keyman Has Run');
Result := False;
end;
diff --git a/windows/src/desktop/kmshell/kmshell.dpr b/windows/src/desktop/kmshell/kmshell.dpr
index 805a79aa58a..13cf5a79dec 100644
--- a/windows/src/desktop/kmshell/kmshell.dpr
+++ b/windows/src/desktop/kmshell/kmshell.dpr
@@ -183,7 +183,7 @@ uses
Keyman.System.RemoteUpdateCheck in 'main\Keyman.System.RemoteUpdateCheck.pas',
Keyman.System.UpdateStateMachine in 'main\Keyman.System.UpdateStateMachine.pas',
Keyman.System.DownloadUpdate in 'main\Keyman.System.DownloadUpdate.pas',
- Keyman.System.ExecuteHistory in '..\..\..\..\common\windows\delphi\general\Keyman.System.ExecuteHistory.pas',
+ Keyman.System.ExecutionHistory in '..\..\..\..\common\windows\delphi\general\Keyman.System.ExecutionHistory.pas',
UfrmStartInstall in 'main\UfrmStartInstall.pas' {Form1};
{$R VERSION.RES}
diff --git a/windows/src/desktop/kmshell/kmshell.dproj b/windows/src/desktop/kmshell/kmshell.dproj
index 79061ffe4af..3715f946592 100644
--- a/windows/src/desktop/kmshell/kmshell.dproj
+++ b/windows/src/desktop/kmshell/kmshell.dproj
@@ -359,7 +359,7 @@
-
+
dfm
@@ -425,19 +425,19 @@
False
-
+
kmshell.exe
true
-
+
kmshell.exe
true
-
+
kmshell.exe
true
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
index f7cfd9ef288..c91fdc858d8 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
@@ -32,7 +32,7 @@ interface
httpuploader,
Keyman.System.UpdateCheckResponse,
- Keyman.System.ExecuteHistory,
+ Keyman.System.ExecutionHistory,
UfrmDownloadProgress;
const
From eff8a8828c4bbbfe4de5f252cffd70fa95856c4a Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Tue, 3 Sep 2024 17:07:17 +1000
Subject: [PATCH 27/43] feat(windows): resize and add title for install now
Update the title bar to say Keyman update for the
install now pop up. Also resized to make it smaller and
inline with other windows applications.
---
.../desktop/kmshell/main/UfrmStartInstall.dfm | 29 ++++++++++++++-----
.../desktop/kmshell/main/UfrmStartInstall.pas | 1 +
2 files changed, 22 insertions(+), 8 deletions(-)
diff --git a/windows/src/desktop/kmshell/main/UfrmStartInstall.dfm b/windows/src/desktop/kmshell/main/UfrmStartInstall.dfm
index 66d008bf2c5..71fcfe27379 100644
--- a/windows/src/desktop/kmshell/main/UfrmStartInstall.dfm
+++ b/windows/src/desktop/kmshell/main/UfrmStartInstall.dfm
@@ -1,9 +1,9 @@
object frmStartInstall: TfrmStartInstall
Left = 0
Top = 0
- Caption = 'frmStartInstall'
- ClientHeight = 299
- ClientWidth = 635
+ Caption = 'Keyman Update'
+ ClientHeight = 225
+ ClientWidth = 425
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
@@ -13,9 +13,22 @@ object frmStartInstall: TfrmStartInstall
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
+ object InstallUpdate: TLabel
+ Left = 128
+ Top = 96
+ Width = 175
+ Height = 19
+ Caption = 'Keyman update available'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -16
+ Font.Name = 'Tahoma'
+ Font.Style = []
+ ParentFont = False
+ end
object Install: TButton
- Left = 168
- Top = 240
+ Left = 228
+ Top = 184
Width = 75
Height = 25
Caption = 'Install'
@@ -23,11 +36,11 @@ object frmStartInstall: TfrmStartInstall
OnClick = InstallClick
end
object Later: TButton
- Left = 288
- Top = 240
+ Left = 336
+ Top = 184
Width = 75
Height = 25
- Caption = 'Later'
+ Caption = 'Close'
TabOrder = 1
OnClick = LaterClick
end
diff --git a/windows/src/desktop/kmshell/main/UfrmStartInstall.pas b/windows/src/desktop/kmshell/main/UfrmStartInstall.pas
index 7b88ddbeab4..419491ca1e5 100644
--- a/windows/src/desktop/kmshell/main/UfrmStartInstall.pas
+++ b/windows/src/desktop/kmshell/main/UfrmStartInstall.pas
@@ -11,6 +11,7 @@ interface
TfrmStartInstall = class(TfrmKeymanBase)
Install: TButton;
Later: TButton;
+ InstallUpdate: TLabel;
procedure InstallClick(Sender: TObject);
procedure LaterClick(Sender: TObject);
private
From bfe1b45983608be526797c4670710dcfa3418f09 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Mon, 9 Sep 2024 15:51:32 +1000
Subject: [PATCH 28/43] feat(windows): automatic updates option to config reg
---
.../windows/delphi/general/RegistryKeys.pas | 2 +
oem/firstvoices/windows/src/xml/strings.xml | 5 +
windows/src/desktop/kmshell/kmshell.dpr | 3 +-
windows/src/desktop/kmshell/kmshell.dproj | 6 +-
.../main/Keyman.System.UpdateStateMachine.pas | 131 ++++++++++++++++--
.../main/UImportOlderVersionSettings.pas | 19 +--
.../kmshell/main/UfrmStartInstallNow.dfm | 50 +++++++
.../kmshell/main/UfrmStartInstallNow.pas | 38 +++++
windows/src/desktop/kmshell/main/initprog.pas | 1 +
windows/src/desktop/kmshell/xml/strings.xml | 5 +
windows/src/desktop/setup/RunTools.pas | 11 +-
windows/src/desktop/setup/UfrmRunDesktop.pas | 6 +-
windows/src/engine/keyman/keyman.dpr | 2 +-
windows/src/engine/keyman/keyman.dproj | 2 +-
windows/src/engine/keyman/main.pas | 2 +-
.../engine/kmcomapi/util/utilkeymanoption.pas | 5 +-
.../delphi/general/KeymanOptionNames.pas | 1 +
17 files changed, 256 insertions(+), 33 deletions(-)
create mode 100644 windows/src/desktop/kmshell/main/UfrmStartInstallNow.dfm
create mode 100644 windows/src/desktop/kmshell/main/UfrmStartInstallNow.pas
diff --git a/common/windows/delphi/general/RegistryKeys.pas b/common/windows/delphi/general/RegistryKeys.pas
index 0d1ba5484ee..7e7fe54c38e 100644
--- a/common/windows/delphi/general/RegistryKeys.pas
+++ b/common/windows/delphi/general/RegistryKeys.pas
@@ -313,8 +313,10 @@ interface
SRegValue_ActiveProject_Filename = 'project filename';
SRegValue_ActiveProject_SourcePath = 'source path';
+ SRegValue_AutomaticUpdates = 'automatic updates'; //CU
SRegValue_CheckForUpdates = 'check for updates'; // CU
SRegValue_LastUpdateCheckTime = 'last update check time'; // CU
+ SRegValue_ApplyNow = 'apply now'; // CU Start the install now even thought it will require an update
SRegValue_UpdateCheck_UseProxy = 'update check use proxy'; // CU
SRegValue_UpdateCheck_ProxyHost = 'update check proxy host'; // CU
diff --git a/oem/firstvoices/windows/src/xml/strings.xml b/oem/firstvoices/windows/src/xml/strings.xml
index d0708d2b4c9..193e2c9f893 100644
--- a/oem/firstvoices/windows/src/xml/strings.xml
+++ b/oem/firstvoices/windows/src/xml/strings.xml
@@ -388,6 +388,11 @@
Show welcome screen
+
+
+
+ Automatically download updates ready to install
+
diff --git a/windows/src/desktop/kmshell/kmshell.dpr b/windows/src/desktop/kmshell/kmshell.dpr
index 13cf5a79dec..d0915bd8656 100644
--- a/windows/src/desktop/kmshell/kmshell.dpr
+++ b/windows/src/desktop/kmshell/kmshell.dpr
@@ -184,7 +184,8 @@ uses
Keyman.System.UpdateStateMachine in 'main\Keyman.System.UpdateStateMachine.pas',
Keyman.System.DownloadUpdate in 'main\Keyman.System.DownloadUpdate.pas',
Keyman.System.ExecutionHistory in '..\..\..\..\common\windows\delphi\general\Keyman.System.ExecutionHistory.pas',
- UfrmStartInstall in 'main\UfrmStartInstall.pas' {Form1};
+ UfrmStartInstallNow in 'main\UfrmStartInstallNow.pas',
+ UfrmStartInstall in 'main\UfrmStartInstall.pas';
{$R VERSION.RES}
{$R manifest.res}
diff --git a/windows/src/desktop/kmshell/kmshell.dproj b/windows/src/desktop/kmshell/kmshell.dproj
index 3715f946592..6784e9c6cb7 100644
--- a/windows/src/desktop/kmshell/kmshell.dproj
+++ b/windows/src/desktop/kmshell/kmshell.dproj
@@ -360,8 +360,12 @@
+
+
+ dfm
+
-
+
dfm
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
index d47927dab5c..fa8820553c1 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
@@ -33,6 +33,7 @@ interface
httpuploader,
Keyman.System.UpdateCheckResponse,
UfrmStartInstall,
+ UfrmStartInstallNow,
Keyman.System.ExecutionHistory,
UfrmDownloadProgress;
@@ -215,7 +216,7 @@ PostInstallState = class(TState)
TUpdateStateMachine = class
private
FForce: Boolean;
- FAuto: Boolean;
+ FAutomaticUpdate: Boolean;
FParams: TUpdateStateMachineParams;
FErrorMessage: string;
DownloadTempPath: string;
@@ -247,6 +248,9 @@ TUpdateStateMachine = class
function checkUpdateSchedule : Boolean;
function SetRegistryState (Update : TUpdateState): Boolean;
+ function GetAutomaticUpdate: Boolean;
+ function SetApplyNow(Value : Boolean): Boolean;
+ function GetApplyNow: Boolean;
protected
property State: TStateClass read GetState write SetState;
@@ -325,7 +329,7 @@ constructor TUpdateStateMachine.Create(AForce : Boolean);
FParams.Result := oucUnknown;
FForce := AForce;
- FAuto := True; // Default to automatically check, download, and install
+ FAutomaticUpdate := GetAutomaticUpdate;
FIdle := IdleState.Create(Self);
FUpdateAvailable := UpdateAvailableState.Create(Self);
FDownloading := DownloadingState.Create(Self);
@@ -408,9 +412,9 @@ function TUpdateStateMachine.SetRegistryState(Update : TUpdateState): Boolean;
try
Registry.RootKey := HKEY_CURRENT_USER;
KL.Log('SetRegistryState State Entry');
- if not Registry.OpenKey(SRegKey_KeymanEngine_LM, True) then
+ if not Registry.OpenKey(SRegKey_KeymanEngine_CU, True) then
begin
- KL.Log('Failed to open registry key: ' + SRegKey_KeymanEngine_LM);
+ KL.Log('Failed to open registry key: ' + SRegKey_KeymanEngine_CU);
Exit;
end;
@@ -444,7 +448,7 @@ function TUpdateStateMachine.CheckRegistryState: TUpdateState; // I2329
Registry := TRegistryErrorControlled.Create; // I2890
try
Registry.RootKey := HKEY_CURRENT_USER;
- if Registry.OpenKeyReadOnly(SRegKey_KeymanEngine_LM) and Registry.ValueExists(SRegValue_Update_State) then
+ if Registry.OpenKeyReadOnly(SRegKey_KeymanEngine_CU) and Registry.ValueExists(SRegValue_Update_State) then
begin
UpdateState := TUpdateState(GetEnumValue(TypeInfo(TUpdateState), Registry.ReadString(SRegValue_Update_State)));
KL.Log('CheckRegistryState State is:[' + Registry.ReadString(SRegValue_Update_State) + ']');
@@ -461,6 +465,77 @@ function TUpdateStateMachine.CheckRegistryState: TUpdateState; // I2329
Result := UpdateState;
end;
+function TUpdateStateMachine.GetAutomaticUpdate: Boolean; // I2329
+var
+ Registry: TRegistryErrorControlled;
+
+begin
+ // check the registry value
+ Registry := TRegistryErrorControlled.Create; // I2890
+ try
+ Registry.RootKey := HKEY_CURRENT_USER;
+ if Registry.OpenKeyReadOnly(SRegKey_KeymanEngine_CU) and Registry.ValueExists(SRegValue_AutomaticUpdates) then
+ begin
+ Result := Registry.ReadBool(SRegValue_AutomaticUpdates);
+ end
+ else
+ begin
+ Result := True; // Default
+ end;
+ finally
+ Registry.Free;
+ end;
+end;
+
+function TUpdateStateMachine.SetApplyNow(Value : Boolean): Boolean;
+var
+ Registry: TRegistryErrorControlled;
+begin
+ Result := False;
+ Registry := TRegistryErrorControlled.Create;
+
+ try
+ Registry.RootKey := HKEY_CURRENT_USER;
+ if not Registry.OpenKey(SRegKey_KeymanEngine_CU, True) then
+ begin
+ Exit;
+ end;
+ try
+ Registry.WriteBool(SRegValue_ApplyNow, Value);
+ Result := True;
+ except
+ on E: Exception do
+ begin
+ KL.Log('Failed to write to registry: ' + E.Message);
+ end;
+ end;
+ finally
+ Registry.Free;
+ end;
+end;
+
+function TUpdateStateMachine.GetApplyNow: Boolean;
+var
+ Registry: TRegistryErrorControlled;
+begin
+ // check the registry value
+ Registry := TRegistryErrorControlled.Create;
+ try
+ Registry.RootKey := HKEY_CURRENT_USER;
+ if Registry.OpenKeyReadOnly(SRegKey_KeymanEngine_CU) and Registry.ValueExists(SRegValue_ApplyNow) then
+ begin
+ Result := Registry.ReadBool(SRegValue_ApplyNow);
+ end
+ else
+ begin
+ Result := False; // Default
+ end;
+ finally
+ Registry.Free;
+ end;
+end;
+
+
function TUpdateStateMachine.CheckUpdateSchedule: Boolean;
var
RegistryErrorControlled :TRegistryErrorControlled;
@@ -728,7 +803,7 @@ procedure UpdateAvailableState.Enter;
begin
// Enter UpdateAvailableState
bucStateContext.SetRegistryState(usUpdateAvailable);
- if bucStateContext.FAuto then
+ if bucStateContext.FAutomaticUpdate then
begin
StartDownloadProcess;
end;
@@ -746,7 +821,7 @@ procedure UpdateAvailableState.HandleCheck;
function UpdateAvailableState.HandleKmShell;
begin
- if bucStateContext.FAuto then
+ if bucStateContext.FAutomaticUpdate then
begin
// we will use a new kmshell process to enable
// the download as background process.
@@ -766,8 +841,20 @@ procedure UpdateAvailableState.HandleAbort;
end;
procedure UpdateAvailableState.HandleInstallNow;
+var
+ frmStartInstallNow : TfrmStartInstallNow;
begin
- ChangeState(DownloadingState);
+ // If user decides NOT to install now stay in UpdateAvailable State
+ frmStartInstallNow := TfrmStartInstallNow.Create(nil);
+ try
+ if frmStartInstallNow.ShowModal = mrOk then
+ begin
+ bucStateContext.SetApplyNow(True);
+ ChangeState(InstallingState)
+ end
+ finally
+ frmStartInstallNow.Free;
+ end;
end;
function UpdateAvailableState.StateName;
@@ -793,10 +880,17 @@ procedure DownloadingState.Enter;
begin
if HasKeymanRun then
begin
- ChangeState(WaitingRestartState);
+ if bucStateContext.GetApplyNow then
+ begin
+ bucStateContext.SetApplyNow(False);
+ ChangeState(InstallingState);
+ end
+ else
+ ChangeState(WaitingRestartState);
end
else
begin
+ bucStateContext.SetApplyNow(False);
ChangeState(InstallingState);
end;
end
@@ -837,7 +931,8 @@ procedure DownloadingState.HandleAbort;
procedure DownloadingState.HandleInstallNow;
begin
-
+ // Already downloading set the registry apply now
+ bucStateContext.SetApplyNow(True);
end;
function DownloadingState.StateName;
@@ -948,11 +1043,25 @@ procedure WaitingRestartState.HandleAbort;
end;
procedure WaitingRestartState.HandleInstallNow;
+var
+ frmStartInstallNow : TfrmStartInstallNow;
begin
// TODO: Check if keyman has run, and error trying to install
// now when windows needs a restart ask the user if they
// want to restart now, if users says no stay in this (waitingrestart) state
- ChangeState(InstallingState);
+
+
+ // If user decides not to install now stay in WaitingRestart State
+ frmStartInstallNow := TfrmStartInstallNow.Create(nil);
+ try
+ if frmStartInstallNow.ShowModal = mrOk then
+ begin
+ bucStateContext.SetApplyNow(True);
+ ChangeState(InstallingState)
+ end
+ finally
+ frmStartInstallNow.Free;
+ end;
end;
function WaitingRestartState.StateName;
diff --git a/windows/src/desktop/kmshell/main/UImportOlderVersionSettings.pas b/windows/src/desktop/kmshell/main/UImportOlderVersionSettings.pas
index 007d854797b..e4cdff54bb3 100644
--- a/windows/src/desktop/kmshell/main/UImportOlderVersionSettings.pas
+++ b/windows/src/desktop/kmshell/main/UImportOlderVersionSettings.pas
@@ -1,18 +1,18 @@
(*
Name: UImportOlderVersionSettings
Copyright: Copyright (C) 2003-2017 SIL International.
- Documentation:
- Description:
+ Documentation:
+ Description:
Create Date: 22 Feb 2011
Modified Date: 3 Jun 2014
Authors: mcdurdin
- Related Files:
- Dependencies:
+ Related Files:
+ Dependencies:
- Bugs:
- Todo:
- Notes:
+ Bugs:
+ Todo:
+ Notes:
History: 22 Feb 2011 - mcdurdin - I2651 - Install does not set desired default options
22 Feb 2011 - mcdurdin - I2753 - Firstrun crashes because start with windows and auto update check options are set in Engine instead of Desktop
03 May 2011 - mcdurdin - I2890 - Record diagnostic data when encountering registry errors
@@ -24,7 +24,7 @@
interface
-function FirstRunInstallDefaults(DoDefaults,DoStartWithWindows,DoCheckForUpdates: Boolean; FDisablePackages, FDefaultUILanguage: string; DoAutomaticallyReportUsage: Boolean): Boolean; // I2753
+function FirstRunInstallDefaults(DoDefaults,DoStartWithWindows,DoCheckForUpdates,DoAutomaticUpdates: Boolean; FDisablePackages, FDefaultUILanguage: string; DoAutomaticallyReportUsage: Boolean): Boolean; // I2753
implementation
@@ -43,7 +43,7 @@ implementation
RegistryKeys,
UImportOlderKeyboardUtils;
-function FirstRunInstallDefaults(DoDefaults,DoStartWithWindows,DoCheckForUpdates: Boolean; FDisablePackages, FDefaultUILanguage: string; DoAutomaticallyReportUsage: Boolean): Boolean; // I2753
+function FirstRunInstallDefaults(DoDefaults,DoStartWithWindows,DoCheckForUpdates,DoAutomaticUpdates: Boolean; FDisablePackages, FDefaultUILanguage: string; DoAutomaticallyReportUsage: Boolean): Boolean; // I2753
var
n, I: Integer;
v: Integer;
@@ -136,6 +136,7 @@ function FirstRunInstallDefaults(DoDefaults,DoStartWithWindows,DoCheckForUpdates
if DoStartWithWindows then kmcom.Options['koStartWithWindows'].Value := True; // I2753
if DoCheckForUpdates then kmcom.Options['koCheckForUpdates'].Value := True; // I2753
+ if DoAutomaticUpdates then kmcom.Options['koAutomaticUpdate'].Value := True;
if DoAutomaticallyReportUsage then kmcom.Options['koAutomaticallyReportUsage'].Value := True;
diff --git a/windows/src/desktop/kmshell/main/UfrmStartInstallNow.dfm b/windows/src/desktop/kmshell/main/UfrmStartInstallNow.dfm
new file mode 100644
index 00000000000..415796d5c82
--- /dev/null
+++ b/windows/src/desktop/kmshell/main/UfrmStartInstallNow.dfm
@@ -0,0 +1,50 @@
+object frmStartInstallNow: TfrmStartInstallNow
+ Left = 0
+ Top = 0
+ Caption = 'Keyman Update'
+ ClientHeight = 225
+ ClientWidth = 425
+ Color = clBtnFace
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -11
+ Font.Name = 'Tahoma'
+ Font.Style = []
+ OldCreateOrder = False
+ PixelsPerInch = 96
+ TextHeight = 13
+ object InstallUpdate: TLabel
+ Left = 69
+ Top = 72
+ Width = 296
+ Height = 38
+ Caption =
+ 'Installing Now will require a Keyman and Windows Restart Continu' +
+ 'e?'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -16
+ Font.Name = 'Tahoma'
+ Font.Style = []
+ ParentFont = False
+ WordWrap = True
+ end
+ object Install: TButton
+ Left = 228
+ Top = 184
+ Width = 75
+ Height = 25
+ Caption = 'Install'
+ TabOrder = 0
+ OnClick = InstallClick
+ end
+ object Later: TButton
+ Left = 336
+ Top = 184
+ Width = 75
+ Height = 25
+ Caption = 'Close'
+ TabOrder = 1
+ OnClick = LaterClick
+ end
+end
diff --git a/windows/src/desktop/kmshell/main/UfrmStartInstallNow.pas b/windows/src/desktop/kmshell/main/UfrmStartInstallNow.pas
new file mode 100644
index 00000000000..c1d153199b3
--- /dev/null
+++ b/windows/src/desktop/kmshell/main/UfrmStartInstallNow.pas
@@ -0,0 +1,38 @@
+unit UfrmStartInstallNow;
+
+interface
+
+uses
+
+ Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
+ Dialogs, UserMessages, StdCtrls, ExtCtrls, UfrmKeymanBase;
+
+type
+ TfrmStartInstallNow = class(TfrmKeymanBase)
+ Install: TButton;
+ Later: TButton;
+ InstallUpdate: TLabel;
+ procedure InstallClick(Sender: TObject);
+ procedure LaterClick(Sender: TObject);
+ private
+ public
+ end;
+
+var
+ frmStartInstall: TfrmStartInstallNow;
+
+implementation
+
+{$R *.dfm}
+
+procedure TfrmStartInstallNow.InstallClick(Sender: TObject);
+begin
+ ModalResult := mrOk;
+end;
+
+procedure TfrmStartInstallNow.LaterClick(Sender: TObject);
+begin
+ ModalResult := mrCancel;
+end;
+
+end.
diff --git a/windows/src/desktop/kmshell/main/initprog.pas b/windows/src/desktop/kmshell/main/initprog.pas
index 79f1f60a60c..d8c19b63099 100644
--- a/windows/src/desktop/kmshell/main/initprog.pas
+++ b/windows/src/desktop/kmshell/main/initprog.pas
@@ -658,6 +658,7 @@ function FirstRun(FQuery, FDisablePackages, FDefaultUILanguage: string): Boolean
Pos('installdefaults', FQuery) > 0,
Pos('startwithwindows', FQuery) > 0,
Pos('checkforupdates', FQuery) > 0,
+ Pos('automaticupdates', FQuery) > 0,
FDisablePackages,
FDefaultUILanguage,
Pos('automaticallyreportusage', FQuery) > 0); // I2651, I2753
diff --git a/windows/src/desktop/kmshell/xml/strings.xml b/windows/src/desktop/kmshell/xml/strings.xml
index 62125399ff7..678a05d6900 100644
--- a/windows/src/desktop/kmshell/xml/strings.xml
+++ b/windows/src/desktop/kmshell/xml/strings.xml
@@ -412,6 +412,11 @@
Show welcome screen
+
+
+
+ Automatically download updates ready to install
+
diff --git a/windows/src/desktop/setup/RunTools.pas b/windows/src/desktop/setup/RunTools.pas
index 341f036c6cd..0b654cfeec5 100644
--- a/windows/src/desktop/setup/RunTools.pas
+++ b/windows/src/desktop/setup/RunTools.pas
@@ -82,7 +82,7 @@ TRunTools = class
InstallSuccess: Boolean);
function InstallMSI(msiLocation: TInstallInfoFileLocation; var InstallDefaults: Boolean; ContinueSetup: Boolean): Boolean;
procedure ConfigFirstRun(StartKeyman,StartWithWindows,
- CheckForUpdates,StartDisabled,StartWithConfiguration,InstallDefaults,
+ CheckForUpdates,AutomaticUpdates,StartDisabled,StartWithConfiguration,InstallDefaults,
AutomaticallyReportUsage: Boolean);
procedure PrepareForReboot(res: Cardinal; InstallDefaults: Boolean);
function RestartWindows: Boolean;
@@ -101,7 +101,7 @@ TRunTools = class
destructor Destroy; override;
procedure CheckInternetConnectedState;
function DoInstall(Handle: THandle;
- StartAfterInstall, StartWithWindows, CheckForUpdates, StartDisabled,
+ StartAfterInstall, StartWithWindows, CheckForUpdates, AutomaticUpdates, StartDisabled,
StartWithConfiguration, InstallDefaults, AutomaticallyReportUsage, ContinueSetup: Boolean): Boolean;
procedure LogError(const msg: WideString; ShowDialogIfNotSilent: Boolean = True);
procedure LogInfo(const msg: string; ShowDialogIfNotSilent: Boolean = False);
@@ -194,7 +194,7 @@ destructor TRunTools.Destroy;
end;
function TRunTools.DoInstall(Handle: THandle;
- StartAfterInstall, StartWithWindows, CheckForUpdates, StartDisabled,
+ StartAfterInstall, StartWithWindows, CheckForUpdates, AutomaticUpdates, StartDisabled,
StartWithConfiguration, InstallDefaults, AutomaticallyReportUsage, ContinueSetup: Boolean): Boolean;
var
msiLocation: TInstallInfoFileLocation;
@@ -224,7 +224,7 @@ function TRunTools.DoInstall(Handle: THandle;
Exit(False);
end;
- ConfigFirstRun(StartAfterInstall,StartWithWindows,CheckForUpdates,
+ ConfigFirstRun(StartAfterInstall,StartWithWindows,CheckForUpdates,AutomaticUpdates,
StartDisabled,StartWithConfiguration,InstallDefaults,AutomaticallyReportUsage);
Result := True;
@@ -588,7 +588,7 @@ procedure TRunTools.WaitFor(hProcess: THandle; var Waiting, Cancelled: Boolean);
end;
end;
-procedure TRunTools.ConfigFirstRun(StartKeyman,StartWithWindows,CheckForUpdates,
+procedure TRunTools.ConfigFirstRun(StartKeyman,StartWithWindows,CheckForUpdates,AutomaticUpdates,
StartDisabled,StartWithConfiguration,InstallDefaults,AutomaticallyReportUsage: Boolean);
var
i: Integer;
@@ -686,6 +686,7 @@ procedure TRunTools.ConfigFirstRun(StartKeyman,StartWithWindows,CheckForUpdates,
if StartWithWindows then s := s + 'StartWithWindows,';
if CheckForUpdates then s := s + 'CheckForUpdates,';
+ if AutomaticUpdates then s := s + 'AutomaticUpdates,';
if AutomaticallyReportUsage then s := s + 'AutomaticallyReportUsage,';
if InstallDefaults then
diff --git a/windows/src/desktop/setup/UfrmRunDesktop.pas b/windows/src/desktop/setup/UfrmRunDesktop.pas
index 293df6a6138..3374324d64d 100644
--- a/windows/src/desktop/setup/UfrmRunDesktop.pas
+++ b/windows/src/desktop/setup/UfrmRunDesktop.pas
@@ -113,6 +113,7 @@ TfrmRunDesktop = class(TForm)
FCanUpgrade9: Boolean;
FCanUpgrade10: Boolean;
FCheckForUpdates: Boolean;
+ FAutomaticUpdates: Boolean;
FStartAfterInstall: Boolean;
FStartWithWindows: Boolean;
FAutomaticallyReportUsage: Boolean;
@@ -523,7 +524,7 @@ procedure TfrmRunDesktop.DoInstall(Silent, PromptForReboot: Boolean); // I3355
SetupMSI; // I2644
if GetRunTools.DoInstall(Handle, FStartAfterInstall, FStartWithWindows, FCheckForUpdates,
- FInstallInfo.StartDisabled, FInstallInfo.StartWithConfiguration, FInstallDefaults,
+ FAutomaticUpdates, FInstallInfo.StartDisabled, FInstallInfo.StartWithConfiguration, FInstallDefaults,
FAutomaticallyReportUsage, FContinueSetup) then
begin
if not Silent and not FStartAfterInstall then // I2610
@@ -1032,6 +1033,7 @@ procedure TfrmRunDesktop.GetDefaultSettings; // I2651
begin
FStartWithWindows := True; // I2607
FCheckForUpdates := True; // I2609
+ FAutomaticUpdates := True;
try
with CreateHKCURegistry do // I2749
@@ -1041,6 +1043,8 @@ procedure TfrmRunDesktop.GetDefaultSettings; // I2651
FCheckForUpdates := ValueExists(SRegValue_CheckForUpdates) and ReadBool(SRegValue_CheckForUpdates);
FStartWithWindows := ValueExists(SRegValue_UpgradeRunKeyman) or
(OpenKeyReadOnly('\' + SRegKey_WindowsRun_CU) and ValueExists(SRegValue_WindowsRun_Keyman));
+ FAutomaticUpdates := ValueExists(SRegValue_AutomaticUpdates) and ReadBool(SRegValue_AutomaticUpdates);
+
end
else if FCanUpgrade10 and OpenKeyReadOnly(SRegKey_KeymanEngine100_ProductOptions_Desktop_CU) then // I4293
begin
diff --git a/windows/src/engine/keyman/keyman.dpr b/windows/src/engine/keyman/keyman.dpr
index 2cff0c4b734..dadcca01fc5 100644
--- a/windows/src/engine/keyman/keyman.dpr
+++ b/windows/src/engine/keyman/keyman.dpr
@@ -113,7 +113,7 @@ uses
sentry in '..\..\..\..\common\windows\delphi\ext\sentry\sentry.pas',
Keyman.System.KeymanSentryClient in '..\..\..\..\common\windows\delphi\general\Keyman.System.KeymanSentryClient.pas',
Keyman.System.LocaleStrings in '..\..\global\delphi\cust\Keyman.System.LocaleStrings.pas',
- Keyman.System.ExecuteHistory in '..\..\..\..\common\windows\delphi\general\Keyman.System.ExecuteHistory.pas';
+ Keyman.System.ExecutionHistory in '..\..\..\..\common\windows\delphi\general\Keyman.System.ExecutionHistory.pas';
{$R ICONS.RES}
{$R VERSION.RES}
diff --git a/windows/src/engine/keyman/keyman.dproj b/windows/src/engine/keyman/keyman.dproj
index 71cdb756d38..aa70b2ab7bd 100644
--- a/windows/src/engine/keyman/keyman.dproj
+++ b/windows/src/engine/keyman/keyman.dproj
@@ -238,7 +238,7 @@
-
+
Cfg_2
diff --git a/windows/src/engine/keyman/main.pas b/windows/src/engine/keyman/main.pas
index 8fcce5c0145..1b6ed25b302 100644
--- a/windows/src/engine/keyman/main.pas
+++ b/windows/src/engine/keyman/main.pas
@@ -47,7 +47,7 @@ implementation
UfrmKeyman7Main,
UserMessages,
Klog,
- Keyman.System.ExecuteHistory;
+ Keyman.System.ExecutionHistory;
function ValidateParameters(var FCommand: Integer): Boolean; forward;
function PassParametersToRunningInstance(FCommand: Integer): Boolean; forward;
diff --git a/windows/src/engine/kmcomapi/util/utilkeymanoption.pas b/windows/src/engine/kmcomapi/util/utilkeymanoption.pas
index ce1b8af33a8..ec979eb2151 100644
--- a/windows/src/engine/kmcomapi/util/utilkeymanoption.pas
+++ b/windows/src/engine/kmcomapi/util/utilkeymanoption.pas
@@ -121,14 +121,15 @@ TKeymanOptionInfo = record
GroupName: string;
end;
-const KeymanOptionInfo: array[0..15] of TKeymanOptionInfo = ( // I3331 // I3620 // I4552
+const KeymanOptionInfo: array[0..16] of TKeymanOptionInfo = ( // I3331 // I3620 // I4552
// Global options
(opt: koKeyboardHotkeysAreToggle; RegistryName: SRegValue_KeyboardHotkeysAreToggle; OptionType: kotBool; BoolValue: False; GroupName: 'kogGeneral'),
(opt: koSwitchLanguageForAllApplications; RegistryName: SRegValue_SwitchLanguageForAllApplications; OptionType: kotBool; BoolValue: True; GroupName: 'kogGeneral'), // I2277 // I4393
(opt: koAltGrCtrlAlt; RegistryName: SRegValue_AltGrCtrlAlt; OptionType: kotBool; BoolValue: False; GroupName: 'kogGeneral'),
(opt: koShowHints; RegistryName: SRegValue_EnableHints; OptionType: kotBool; BoolValue: True; GroupName: 'kogGeneral'),
- (opt: koBaseLayout; RegistryName: SRegValue_UnderlyingLayout; OptionType: kotLong; IntValue: 0; GroupName: 'kogGeneral'),
+ (opt: koBaseLayout; RegistryName: SRegValue_UnderlyingLayout; OptionType: kotLong; IntValue: 0; GroupName: 'kogGeneral'),
+ (opt: koAutomaticUpdate; RegistryName: SRegValue_AutomaticUpdates; OptionType: kotBool; BoolValue: True; GroupName: 'kogGeneral'),
(opt: koAutomaticallyReportErrors; RegistryName: SRegValue_AutomaticallyReportErrors; OptionType: kotBool; BoolValue: True; GroupName: 'kogGeneral'), // I4393
(opt: koAutomaticallyReportUsage; RegistryName: SRegValue_AutomaticallyReportUsage; OptionType: kotBool; BoolValue: True; GroupName: 'kogGeneral'), // I4393
diff --git a/windows/src/global/delphi/general/KeymanOptionNames.pas b/windows/src/global/delphi/general/KeymanOptionNames.pas
index 27aef82ab55..07e0a85f7b4 100644
--- a/windows/src/global/delphi/general/KeymanOptionNames.pas
+++ b/windows/src/global/delphi/general/KeymanOptionNames.pas
@@ -7,6 +7,7 @@ interface
// General options
koKeyboardHotkeysAreToggle, koAltGrCtrlAlt, koReleaseShiftKeysAfterKeyPress,
koShowHints, // I1256
+ koAutomaticUpdate,
// Startup options
koTestKeymanFunctioning,
koStartWithWindows,
From 033cb227fe2f221620fcded57c99619204a59f6e Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Tue, 10 Sep 2024 14:37:41 +1000
Subject: [PATCH 29/43] feat(windows): changes update form now
---
.../main/Keyman.System.UpdateStateMachine.pas | 63 ++++++++++++-------
.../kmshell/main/UfrmStartInstallNow.dfm | 27 +++++---
.../kmshell/main/UfrmStartInstallNow.pas | 3 +-
3 files changed, 60 insertions(+), 33 deletions(-)
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
index fa8820553c1..63afb7e4d2a 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
@@ -843,18 +843,29 @@ procedure UpdateAvailableState.HandleAbort;
procedure UpdateAvailableState.HandleInstallNow;
var
frmStartInstallNow : TfrmStartInstallNow;
+ InstallNow : Boolean;
begin
+
+ InstallNow := True;
+ if HasKeymanRun then
+ begin
+ frmStartInstallNow := TfrmStartInstallNow.Create(nil);
+ try
+ if frmStartInstallNow.ShowModal = mrOk then
+ InstallNow := True
+ else
+ InstallNow := False;
+ finally
+ frmStartInstallNow.Free;
+ end;
+ end;
// If user decides NOT to install now stay in UpdateAvailable State
- frmStartInstallNow := TfrmStartInstallNow.Create(nil);
- try
- if frmStartInstallNow.ShowModal = mrOk then
- begin
- bucStateContext.SetApplyNow(True);
- ChangeState(InstallingState)
- end
- finally
- frmStartInstallNow.Free;
+ if InstallNow = True then
+ begin
+ bucStateContext.SetApplyNow(True);
+ ChangeState(InstallingState)
end;
+
end;
function UpdateAvailableState.StateName;
@@ -1043,24 +1054,28 @@ procedure WaitingRestartState.HandleAbort;
end;
procedure WaitingRestartState.HandleInstallNow;
+// If user decides not to install now stay in WaitingRestart State
var
frmStartInstallNow : TfrmStartInstallNow;
+ InstallNow : Boolean;
begin
- // TODO: Check if keyman has run, and error trying to install
- // now when windows needs a restart ask the user if they
- // want to restart now, if users says no stay in this (waitingrestart) state
-
-
- // If user decides not to install now stay in WaitingRestart State
- frmStartInstallNow := TfrmStartInstallNow.Create(nil);
- try
- if frmStartInstallNow.ShowModal = mrOk then
- begin
- bucStateContext.SetApplyNow(True);
- ChangeState(InstallingState)
- end
- finally
- frmStartInstallNow.Free;
+ InstallNow := True;
+ if HasKeymanRun then
+ begin
+ frmStartInstallNow := TfrmStartInstallNow.Create(nil);
+ try
+ if frmStartInstallNow.ShowModal = mrOk then
+ InstallNow := True
+ else
+ InstallNow := False;
+ finally
+ frmStartInstallNow.Free;
+ end;
+ end;
+ if InstallNow = True then
+ begin
+ bucStateContext.SetApplyNow(True);
+ ChangeState(InstallingState)
end;
end;
diff --git a/windows/src/desktop/kmshell/main/UfrmStartInstallNow.dfm b/windows/src/desktop/kmshell/main/UfrmStartInstallNow.dfm
index 415796d5c82..ae93b5cd1e7 100644
--- a/windows/src/desktop/kmshell/main/UfrmStartInstallNow.dfm
+++ b/windows/src/desktop/kmshell/main/UfrmStartInstallNow.dfm
@@ -13,14 +13,12 @@ object frmStartInstallNow: TfrmStartInstallNow
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
- object InstallUpdate: TLabel
- Left = 69
- Top = 72
- Width = 296
+ object UpdateMessage: TLabel
+ Left = 56
+ Top = 88
+ Width = 289
Height = 38
- Caption =
- 'Installing Now will require a Keyman and Windows Restart Continu' +
- 'e?'
+ Caption = 'Keyman and Windows will be restarted'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -16
@@ -29,12 +27,25 @@ object frmStartInstallNow: TfrmStartInstallNow
ParentFont = False
WordWrap = True
end
+ object UpdateNow: TLabel
+ Left = 56
+ Top = 40
+ Width = 115
+ Height = 25
+ Caption = 'Update Now'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -21
+ Font.Name = 'Tahoma'
+ Font.Style = []
+ ParentFont = False
+ end
object Install: TButton
Left = 228
Top = 184
Width = 75
Height = 25
- Caption = 'Install'
+ Caption = 'Update'
TabOrder = 0
OnClick = InstallClick
end
diff --git a/windows/src/desktop/kmshell/main/UfrmStartInstallNow.pas b/windows/src/desktop/kmshell/main/UfrmStartInstallNow.pas
index c1d153199b3..aee57e90964 100644
--- a/windows/src/desktop/kmshell/main/UfrmStartInstallNow.pas
+++ b/windows/src/desktop/kmshell/main/UfrmStartInstallNow.pas
@@ -11,7 +11,8 @@ interface
TfrmStartInstallNow = class(TfrmKeymanBase)
Install: TButton;
Later: TButton;
- InstallUpdate: TLabel;
+ UpdateMessage: TLabel;
+ UpdateNow: TLabel;
procedure InstallClick(Sender: TObject);
procedure LaterClick(Sender: TObject);
private
From 3f1236ab59ec18b27ada9935ceecb497f4a7f165 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Tue, 10 Sep 2024 17:22:21 +1000
Subject: [PATCH 30/43] feat(windows): comments copyright formating
---
.../windows/delphi/general/Keyman.System.ExecutionHistory.pas | 2 +-
.../src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas | 2 +-
.../desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas | 1 -
windows/src/desktop/kmshell/main/UfrmMain.pas | 2 +-
windows/src/desktop/kmshell/main/UfrmStartInstall.pas | 4 +++-
windows/src/desktop/kmshell/main/UfrmStartInstallNow.pas | 4 +++-
6 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/common/windows/delphi/general/Keyman.System.ExecutionHistory.pas b/common/windows/delphi/general/Keyman.System.ExecutionHistory.pas
index 08fd015c88d..4d3900659b6 100644
--- a/common/windows/delphi/general/Keyman.System.ExecutionHistory.pas
+++ b/common/windows/delphi/general/Keyman.System.ExecutionHistory.pas
@@ -1,7 +1,7 @@
unit Keyman.System.ExecutionHistory;
{
- Copyright: © SIL International.
+ Copyright: © SIL Global.
This module provides functionality to track the execution state of the Keyman
engine. It uses a global atom to record whether Keyman has started during the
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
index f095341c822..dcc6e28faad 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
@@ -1,6 +1,6 @@
(*
Name: WebUpdateCheck
- Copyright: Copyright (C) SIL International.
+ Copyright: Copyright (C) SIL Global.
Documentation:
Description:
Create Date: 5 Dec 2023
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
index 63afb7e4d2a..74c2433fa19 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
@@ -322,7 +322,6 @@ implementation
{ TUpdateStateMachine }
constructor TUpdateStateMachine.Create(AForce : Boolean);
-// var TSerailsedState : TUpdateState; // TODO: Remove
begin
inherited Create;
FShowErrors := True;
diff --git a/windows/src/desktop/kmshell/main/UfrmMain.pas b/windows/src/desktop/kmshell/main/UfrmMain.pas
index 5f16aeb047e..0403df92751 100644
--- a/windows/src/desktop/kmshell/main/UfrmMain.pas
+++ b/windows/src/desktop/kmshell/main/UfrmMain.pas
@@ -844,7 +844,7 @@ procedure TfrmMain.Update_ApplyNow;
ShellPath := TKeymanPaths.KeymanDesktopInstallPath(TKeymanPaths.S_KMShell);
FResult := TUtilExecute.Shell(0, ShellPath, '', '-an');
if not FResult then
- KL.Log('TrmfMain: Executing Update_ApplyNow Failed');
+ KL.Log('TrmfMain: Executing Update_ApplyNow Failed'); // TODO: Make error log
end;
procedure TfrmMain.TntFormCloseQuery(Sender: TObject; var CanClose: Boolean);
diff --git a/windows/src/desktop/kmshell/main/UfrmStartInstall.pas b/windows/src/desktop/kmshell/main/UfrmStartInstall.pas
index 419491ca1e5..f242b863276 100644
--- a/windows/src/desktop/kmshell/main/UfrmStartInstall.pas
+++ b/windows/src/desktop/kmshell/main/UfrmStartInstall.pas
@@ -1,5 +1,7 @@
unit UfrmStartInstall;
-
+{
+ Copyright: © SIL Global.
+}
interface
uses
diff --git a/windows/src/desktop/kmshell/main/UfrmStartInstallNow.pas b/windows/src/desktop/kmshell/main/UfrmStartInstallNow.pas
index aee57e90964..6782a166a75 100644
--- a/windows/src/desktop/kmshell/main/UfrmStartInstallNow.pas
+++ b/windows/src/desktop/kmshell/main/UfrmStartInstallNow.pas
@@ -1,5 +1,7 @@
unit UfrmStartInstallNow;
-
+{
+ Copyright: © SIL Global.
+}
interface
uses
From 5c187db7a6d306b52e36d077a91d857d72d600e8 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Thu, 12 Sep 2024 13:40:42 +1000
Subject: [PATCH 31/43] feat(windows): address review comments
---
VERSION.md | 2 +-
.../Keyman.System.ExecutionHistory.pas | 2 +
.../main/Keyman.System.UpdateStateMachine.pas | 65 +++++++------------
.../desktop/kmshell/main/UfrmStartInstall.dfm | 10 +--
.../desktop/kmshell/main/UfrmStartInstall.pas | 34 ++++++----
.../kmshell/main/UfrmStartInstallNow.dfm | 16 ++---
.../kmshell/main/UfrmStartInstallNow.pas | 25 +++----
windows/src/desktop/setup/UfrmRunDesktop.pas | 2 +-
8 files changed, 75 insertions(+), 81 deletions(-)
diff --git a/VERSION.md b/VERSION.md
index a598c2b1bd9..a41e5425c0a 100644
--- a/VERSION.md
+++ b/VERSION.md
@@ -1 +1 @@
-18.0.95
+18.0.95
\ No newline at end of file
diff --git a/common/windows/delphi/general/Keyman.System.ExecutionHistory.pas b/common/windows/delphi/general/Keyman.System.ExecutionHistory.pas
index 4d3900659b6..eb3bb11df3c 100644
--- a/common/windows/delphi/general/Keyman.System.ExecutionHistory.pas
+++ b/common/windows/delphi/general/Keyman.System.ExecutionHistory.pas
@@ -39,6 +39,7 @@ function RecordKeymanStarted : Boolean;
end;
except
on E: Exception do
+ // TODO: #10210 convert to sentry error
KL.Log(E.ClassName + ': ' + E.Message);
end;
end;
@@ -63,6 +64,7 @@ function HasKeymanRun : Boolean;
except
on E: Exception do
+ // TODO: #10210 convert to sentry error
KL.log(E.ClassName + ': ' + E.Message);
end;
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
index 74c2433fa19..5cdc681692e 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
@@ -248,7 +248,7 @@ TUpdateStateMachine = class
function checkUpdateSchedule : Boolean;
function SetRegistryState (Update : TUpdateState): Boolean;
- function GetAutomaticUpdate: Boolean;
+ function GetAutomaticUpdates: Boolean;
function SetApplyNow(Value : Boolean): Boolean;
function GetApplyNow: Boolean;
@@ -328,7 +328,7 @@ constructor TUpdateStateMachine.Create(AForce : Boolean);
FParams.Result := oucUnknown;
FForce := AForce;
- FAutomaticUpdate := GetAutomaticUpdate;
+ FAutomaticUpdate := GetAutomaticUpdates;
FIdle := IdleState.Create(Self);
FUpdateAvailable := UpdateAvailableState.Create(Self);
FDownloading := DownloadingState.Create(Self);
@@ -338,13 +338,12 @@ constructor TUpdateStateMachine.Create(AForce : Boolean);
FPostInstall := PostInstallState.Create(Self);
// Check the Registry setting.
SetStateOnly(ConvertEnumState(CheckRegistryState));
- KL.Log('TUpdateStateMachine.Create');
end;
destructor TUpdateStateMachine.Destroy;
begin
if (FErrorMessage <> '') and FShowErrors then
- KL.Log(FErrorMessage);
+ KL.Log(FErrorMessage); // TODO: #10210 Log to Sentry
if FParams.Result = oucShutDown then
ShutDown;
@@ -357,8 +356,9 @@ destructor TUpdateStateMachine.Destroy;
FRetry.Free;
FPostInstall.Free;
- KL.Log('TUpdateStateMachine.Destroy: FErrorMessage = '+FErrorMessage);
- KL.Log('TUpdateStateMachine.Destroy: FParams.Result = '+IntToStr(Ord(FParams.Result)));
+ // TODO: #10210 remove debugging comments
+ //KL.Log('TUpdateStateMachine.Destroy: FErrorMessage = '+FErrorMessage);
+ //KL.Log('TUpdateStateMachine.Destroy: FParams.Result = '+IntToStr(Ord(FParams.Result)));
inherited Destroy;
end;
@@ -410,9 +410,10 @@ function TUpdateStateMachine.SetRegistryState(Update : TUpdateState): Boolean;
try
Registry.RootKey := HKEY_CURRENT_USER;
- KL.Log('SetRegistryState State Entry');
+
if not Registry.OpenKey(SRegKey_KeymanEngine_CU, True) then
begin
+ // TODO: #10210 Log to Sentry
KL.Log('Failed to open registry key: ' + SRegKey_KeymanEngine_CU);
Exit;
end;
@@ -420,11 +421,11 @@ function TUpdateStateMachine.SetRegistryState(Update : TUpdateState): Boolean;
try
UpdateStr := GetEnumName(TypeInfo(TUpdateState), Ord(Update));
Registry.WriteString(SRegValue_Update_State, UpdateStr);
- KL.Log('SetRegistryState State is: [' + UpdateStr + ']');
Result := True;
except
on E: Exception do
begin
+ // TODO: #10210 Log to Sentry
KL.Log('Failed to write to registry: ' + E.Message);
end;
end;
@@ -450,12 +451,10 @@ function TUpdateStateMachine.CheckRegistryState: TUpdateState; // I2329
if Registry.OpenKeyReadOnly(SRegKey_KeymanEngine_CU) and Registry.ValueExists(SRegValue_Update_State) then
begin
UpdateState := TUpdateState(GetEnumValue(TypeInfo(TUpdateState), Registry.ReadString(SRegValue_Update_State)));
- KL.Log('CheckRegistryState State is:[' + Registry.ReadString(SRegValue_Update_State) + ']');
end
else
begin
UpdateState := usIdle; // do we need a unknown state ?
- KL.Log('CheckRegistryState State reg value not found default:[' + Registry.ReadString(SRegValue_Update_State) + ']');
end;
finally
Registry.Free;
@@ -464,7 +463,7 @@ function TUpdateStateMachine.CheckRegistryState: TUpdateState; // I2329
Result := UpdateState;
end;
-function TUpdateStateMachine.GetAutomaticUpdate: Boolean; // I2329
+function TUpdateStateMachine.GetAutomaticUpdates: Boolean; // I2329
var
Registry: TRegistryErrorControlled;
@@ -473,14 +472,9 @@ function TUpdateStateMachine.GetAutomaticUpdate: Boolean; // I2329
Registry := TRegistryErrorControlled.Create; // I2890
try
Registry.RootKey := HKEY_CURRENT_USER;
- if Registry.OpenKeyReadOnly(SRegKey_KeymanEngine_CU) and Registry.ValueExists(SRegValue_AutomaticUpdates) then
- begin
- Result := Registry.ReadBool(SRegValue_AutomaticUpdates);
- end
- else
- begin
- Result := True; // Default
- end;
+ Result := not Registry.OpenKeyReadOnly(SRegKey_KeymanEngine_CU) or
+ not Registry.ValueExists(SRegValue_AutomaticUpdates) or
+ Registry.ReadBool(SRegValue_AutomaticUpdates);
finally
Registry.Free;
end;
@@ -505,6 +499,7 @@ function TUpdateStateMachine.SetApplyNow(Value : Boolean): Boolean;
except
on E: Exception do
begin
+ // TODO: #10210 Log to Sentry 'Failed to write '+SRegValue_ApplyNow+' to registry: ' + E.Message
KL.Log('Failed to write to registry: ' + E.Message);
end;
end;
@@ -521,14 +516,9 @@ function TUpdateStateMachine.GetApplyNow: Boolean;
Registry := TRegistryErrorControlled.Create;
try
Registry.RootKey := HKEY_CURRENT_USER;
- if Registry.OpenKeyReadOnly(SRegKey_KeymanEngine_CU) and Registry.ValueExists(SRegValue_ApplyNow) then
- begin
- Result := Registry.ReadBool(SRegValue_ApplyNow);
- end
- else
- begin
- Result := False; // Default
- end;
+ Result := Registry.OpenKeyReadOnly(SRegKey_KeymanEngine_CU) and
+ Registry.ValueExists(SRegValue_ApplyNow) and
+ Registry.ReadBool(SRegValue_ApplyNow);
finally
Registry.Free;
end;
@@ -684,9 +674,7 @@ constructor TState.Create(Context: TUpdateStateMachine);
procedure TState.ChangeState(NewState: TStateClass);
begin
- KL.Log('TUpdateStateMachine.ChangeState old' + bucStateContext.CurrentStateName );
bucStateContext.State := NewState;
- KL.Log('TUpdateStateMachine.ChangeState new' + bucStateContext.CurrentStateName );
end;
{ IdleState }
@@ -744,7 +732,6 @@ function IdleState.HandleKmShell;
//const CheckPeriod: Integer = 7; // Days between checking for updates
begin
// Check if auto updates enable and if scheduled time has expired
- KL.Log('IdleState.HandleKmShell');
if ConfigCheckContinue then
begin
CheckForUpdates := TRemoteUpdateCheck.Create(True);
@@ -795,6 +782,7 @@ procedure UpdateAvailableState.StartDownloadProcess;
RootPath := ExtractFilePath(ParamStr(0));
FResult := TUtilExecute.ShellCurrentUser(0, ParamStr(0), IncludeTrailingPathDelimiter(RootPath), '-bd');
if not FResult then
+ // TODO: #10210 Log to Sentry
KL.Log('TrmfMain: Executing KMshell for download updated Failed');
end;
@@ -848,6 +836,9 @@ procedure UpdateAvailableState.HandleInstallNow;
InstallNow := True;
if HasKeymanRun then
begin
+ // TODO: UI and non-UI units should be split
+ // if the unit launches UI then it should be a .UI. unit
+ //https://github.com/keymanapp/keyman/pull/12375/files#r1751041747
frmStartInstallNow := TfrmStartInstallNow.Create(nil);
try
if frmStartInstallNow.ShowModal = mrOk then
@@ -932,7 +923,6 @@ procedure DownloadingState.HandleDownload;
RootPath: string;
begin
// Enter Already Downloading
- KL.Log('DownloadingState.HandleDownload already downloading');
end;
procedure DownloadingState.HandleAbort;
@@ -959,10 +949,8 @@ function DownloadingState.DownloadUpdatesBackground: Boolean;
DownloadUpdate := TDownloadUpdate.Create;
try
DownloadResult := DownloadUpdate.DownloadUpdates;
- KL.Log('TUpdateStateMachine.DownloadUpdatesBackground: DownloadResult = '+IntToStr(Ord(DownloadResult)));
Result := DownloadResult;
// #TODO: #10210 workout when we need to refresh kmcom keyboards
-
// if Result in [ wucSuccess] then
// begin
// kmcom.Keyboards.Refresh;
@@ -979,7 +967,6 @@ function DownloadingState.DownloadUpdatesBackground: Boolean;
procedure WaitingRestartState.Enter;
begin
// Enter WaitingRestartState
- KL.Log('WaitingRestartState.HandleKmShell Enter');
bucStateContext.SetRegistryState(usWaitingRestart);
end;
@@ -999,11 +986,9 @@ function WaitingRestartState.HandleKmShell;
Filenames : TStringDynArray;
frmStartInstall : TfrmStartInstall;
begin
- KL.Log('WaitingRestartState.HandleKmShell Enter');
// Still can't go if keyman has run
if HasKeymanRun then
begin
- KL.Log('WaitingRestartState.HandleKmShell Keyman Has Run');
Result := kmShellContinue;
// Exit; // Exit is not wokring for some reason.
// this else is only here because the exit is not working.
@@ -1015,7 +1000,6 @@ function WaitingRestartState.HandleKmShell;
GetFileNamesInDirectory(SavedPath, FileNames);
if Length(FileNames) = 0 then
begin
- KL.Log('WaitingRestartState.HandleKmShell No Files in Download Cache');
// Return to Idle state and check for Updates state
ChangeState(IdleState);
bucStateContext.CurrentState.HandleCheck; // TODO no event here
@@ -1024,7 +1008,6 @@ function WaitingRestartState.HandleKmShell;
end
else
begin
- KL.Log('WaitingRestartState.HandleKmShell is good to install');
// TODO Pop up toast here to ask user if we want to continue
frmStartInstall := TfrmStartInstall.Create(nil);
try
@@ -1074,7 +1057,7 @@ procedure WaitingRestartState.HandleInstallNow;
if InstallNow = True then
begin
bucStateContext.SetApplyNow(True);
- ChangeState(InstallingState)
+ ChangeState(InstallingState);
end;
end;
@@ -1127,7 +1110,6 @@ function InstallingState.DoInstallKeyman(SavePath: string) : Boolean;
FResult := TUtilExecute.Shell(0, 'msiexec.exe', '', '/qb /i "'+SavePath+'" AUTOLAUNCHPRODUCT=1') // I3349
else if s = '.exe' then
begin
- KL.Log('TUpdateStateMachine.InstallingState.DoInstallKeyman SavePath:"'+ SavePath+'"');
// switch -au for auto update in silent mode.
// We will need to add the pop up that says install update now yes/no
// This will run the setup executable which will ask for elevated permissions
@@ -1138,6 +1120,7 @@ function InstallingState.DoInstallKeyman(SavePath: string) : Boolean;
if not FResult then
begin
+ // TODO: #10210 Log to Sentry
KL.Log('TUpdateStateMachine.InstallingState.DoInstall: Result = '+IntToStr(Ord(FResult)));
// Log messageShowMessage(SysErrorMessage(GetLastError));
end;
@@ -1295,9 +1278,7 @@ procedure PostInstallState.HandleMSIInstallComplete;
FileName: String;
FileNames: TStringDynArray;
begin
- KL.Log('PostInstallState.HandleMSIInstallComplete');
SavePath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
- KL.Log('PostInstallState.HandleMSIInstallComplete remove SavePath:'+ SavePath);
GetFileNamesInDirectory(SavePath, FileNames);
for FileName in FileNames do
diff --git a/windows/src/desktop/kmshell/main/UfrmStartInstall.dfm b/windows/src/desktop/kmshell/main/UfrmStartInstall.dfm
index 71fcfe27379..010059e4418 100644
--- a/windows/src/desktop/kmshell/main/UfrmStartInstall.dfm
+++ b/windows/src/desktop/kmshell/main/UfrmStartInstall.dfm
@@ -13,7 +13,7 @@ object frmStartInstall: TfrmStartInstall
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
- object InstallUpdate: TLabel
+ object lblInstallUpdate: TLabel
Left = 128
Top = 96
Width = 175
@@ -26,22 +26,22 @@ object frmStartInstall: TfrmStartInstall
Font.Style = []
ParentFont = False
end
- object Install: TButton
+ object cmdInstall: TButton
Left = 228
Top = 184
Width = 75
Height = 25
Caption = 'Install'
TabOrder = 0
- OnClick = InstallClick
+ OnClick = cmdInstallClick
end
- object Later: TButton
+ object cmdLater: TButton
Left = 336
Top = 184
Width = 75
Height = 25
Caption = 'Close'
TabOrder = 1
- OnClick = LaterClick
+ OnClick = cmdLaterClick
end
end
diff --git a/windows/src/desktop/kmshell/main/UfrmStartInstall.pas b/windows/src/desktop/kmshell/main/UfrmStartInstall.pas
index f242b863276..2e54d140273 100644
--- a/windows/src/desktop/kmshell/main/UfrmStartInstall.pas
+++ b/windows/src/desktop/kmshell/main/UfrmStartInstall.pas
@@ -1,38 +1,48 @@
-unit UfrmStartInstall;
+unit UfrmStartInstall;
{
Copyright: © SIL Global.
+ // TODO: Localise all the labels and captions.
}
interface
uses
- Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
- Dialogs, UserMessages, StdCtrls, ExtCtrls, UfrmKeymanBase;
+ Windows,
+ Messages,
+ SysUtils,
+ Variants,
+ Classes,
+ Graphics,
+ Controls,
+ Forms,
+ Dialogs,
+ UserMessages,
+ StdCtrls,
+ ExtCtrls,
+ UfrmKeymanBase;
type
TfrmStartInstall = class(TfrmKeymanBase)
- Install: TButton;
- Later: TButton;
- InstallUpdate: TLabel;
- procedure InstallClick(Sender: TObject);
- procedure LaterClick(Sender: TObject);
+ cmdInstall: TButton;
+ cmdLater: TButton;
+ lblInstallUpdate: TLabel;
+ procedure cmdInstallClick(Sender: TObject);
+ procedure cmdLaterClick(Sender: TObject);
private
public
end;
-var
- frmStartInstall: TfrmStartInstall;
implementation
{$R *.dfm}
-procedure TfrmStartInstall.InstallClick(Sender: TObject);
+procedure TfrmStartInstall.cmdInstallClick(Sender: TObject);
begin
ModalResult := mrOk;
end;
-procedure TfrmStartInstall.LaterClick(Sender: TObject);
+procedure TfrmStartInstall.cmdLaterClick(Sender: TObject);
begin
ModalResult := mrCancel;
end;
diff --git a/windows/src/desktop/kmshell/main/UfrmStartInstallNow.dfm b/windows/src/desktop/kmshell/main/UfrmStartInstallNow.dfm
index ae93b5cd1e7..289745efd5e 100644
--- a/windows/src/desktop/kmshell/main/UfrmStartInstallNow.dfm
+++ b/windows/src/desktop/kmshell/main/UfrmStartInstallNow.dfm
@@ -13,11 +13,11 @@ object frmStartInstallNow: TfrmStartInstallNow
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
- object UpdateMessage: TLabel
+ object lblUpdateMessage: TLabel
Left = 56
Top = 88
- Width = 289
- Height = 38
+ Width = 274
+ Height = 19
Caption = 'Keyman and Windows will be restarted'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
@@ -27,7 +27,7 @@ object frmStartInstallNow: TfrmStartInstallNow
ParentFont = False
WordWrap = True
end
- object UpdateNow: TLabel
+ object lblUpdateNow: TLabel
Left = 56
Top = 40
Width = 115
@@ -40,22 +40,22 @@ object frmStartInstallNow: TfrmStartInstallNow
Font.Style = []
ParentFont = False
end
- object Install: TButton
+ object cmdInstall: TButton
Left = 228
Top = 184
Width = 75
Height = 25
Caption = 'Update'
TabOrder = 0
- OnClick = InstallClick
+ OnClick = cmdInstallClick
end
- object Later: TButton
+ object cmdLater: TButton
Left = 336
Top = 184
Width = 75
Height = 25
Caption = 'Close'
TabOrder = 1
- OnClick = LaterClick
+ OnClick = cmdLaterClick
end
end
diff --git a/windows/src/desktop/kmshell/main/UfrmStartInstallNow.pas b/windows/src/desktop/kmshell/main/UfrmStartInstallNow.pas
index 6782a166a75..2895502e8e0 100644
--- a/windows/src/desktop/kmshell/main/UfrmStartInstallNow.pas
+++ b/windows/src/desktop/kmshell/main/UfrmStartInstallNow.pas
@@ -1,6 +1,7 @@
-unit UfrmStartInstallNow;
+unit UfrmStartInstallNow;
{
Copyright: © SIL Global.
+ // TODO: Localise all the labels and captions.
}
interface
@@ -11,29 +12,29 @@ interface
type
TfrmStartInstallNow = class(TfrmKeymanBase)
- Install: TButton;
- Later: TButton;
- UpdateMessage: TLabel;
- UpdateNow: TLabel;
- procedure InstallClick(Sender: TObject);
- procedure LaterClick(Sender: TObject);
+ cmdInstall: TButton;
+ cmdLater: TButton;
+ lblUpdateMessage: TLabel;
+ lblUpdateNow: TLabel;
+ procedure cmdInstallClick(Sender: TObject);
+ procedure cmdLaterClick(Sender: TObject);
private
public
end;
-var
- frmStartInstall: TfrmStartInstallNow;
-
implementation
{$R *.dfm}
-procedure TfrmStartInstallNow.InstallClick(Sender: TObject);
+
+// TODO remove events as they are properties on the buttons
+
+procedure TfrmStartInstallNow.cmdInstallClick(Sender: TObject);
begin
ModalResult := mrOk;
end;
-procedure TfrmStartInstallNow.LaterClick(Sender: TObject);
+procedure TfrmStartInstallNow.cmdLaterClick(Sender: TObject);
begin
ModalResult := mrCancel;
end;
diff --git a/windows/src/desktop/setup/UfrmRunDesktop.pas b/windows/src/desktop/setup/UfrmRunDesktop.pas
index 3374324d64d..ded19ae75cd 100644
--- a/windows/src/desktop/setup/UfrmRunDesktop.pas
+++ b/windows/src/desktop/setup/UfrmRunDesktop.pas
@@ -1043,7 +1043,7 @@ procedure TfrmRunDesktop.GetDefaultSettings; // I2651
FCheckForUpdates := ValueExists(SRegValue_CheckForUpdates) and ReadBool(SRegValue_CheckForUpdates);
FStartWithWindows := ValueExists(SRegValue_UpgradeRunKeyman) or
(OpenKeyReadOnly('\' + SRegKey_WindowsRun_CU) and ValueExists(SRegValue_WindowsRun_Keyman));
- FAutomaticUpdates := ValueExists(SRegValue_AutomaticUpdates) and ReadBool(SRegValue_AutomaticUpdates);
+ FAutomaticUpdates := not ValueExists(SRegValue_AutomaticUpdates) or ReadBool(SRegValue_AutomaticUpdates);
end
else if FCanUpgrade10 and OpenKeyReadOnly(SRegKey_KeymanEngine100_ProductOptions_Desktop_CU) then // I4293
From 06de2b0475d66408ac56e41c8ad6f96f742884a8 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Thu, 12 Sep 2024 14:05:05 +1000
Subject: [PATCH 32/43] feat(windows): change automatic update description
---
windows/src/desktop/kmshell/xml/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/windows/src/desktop/kmshell/xml/strings.xml b/windows/src/desktop/kmshell/xml/strings.xml
index 678a05d6900..850f27930c0 100644
--- a/windows/src/desktop/kmshell/xml/strings.xml
+++ b/windows/src/desktop/kmshell/xml/strings.xml
@@ -415,7 +415,7 @@
- Automatically download updates ready to install
+ Automatically check for updates and download
From b4f52be7fd29e959882fdb7df2e7f2c24d59fe0e Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Wed, 2 Oct 2024 10:58:31 +1000
Subject: [PATCH 33/43] feat(windows): rename UI forms to convention
---
windows/src/desktop/kmshell/kmshell.dpr | 4 ++--
windows/src/desktop/kmshell/kmshell.dproj | 10 +++++-----
windows/src/desktop/kmshell/kmshell.res | Bin 0 -> 7036 bytes
...Keyman.Configuration.UI.UfrmStartInstall.dfm} | 0
...Keyman.Configuration.UI.UfrmStartInstall.pas} | 2 +-
...man.Configuration.UI.UfrmStartInstallNow.dfm} | 0
...man.Configuration.UI.UfrmStartInstallNow.pas} | 2 +-
.../main/Keyman.System.UpdateStateMachine.pas | 4 ++--
.../engine/kmcomapi/util/utilkeymanoption.pas | 2 +-
9 files changed, 12 insertions(+), 12 deletions(-)
create mode 100644 windows/src/desktop/kmshell/kmshell.res
rename windows/src/desktop/kmshell/main/{UfrmStartInstall.dfm => Keyman.Configuration.UI.UfrmStartInstall.dfm} (100%)
rename windows/src/desktop/kmshell/main/{UfrmStartInstall.pas => Keyman.Configuration.UI.UfrmStartInstall.pas} (93%)
rename windows/src/desktop/kmshell/main/{UfrmStartInstallNow.dfm => Keyman.Configuration.UI.UfrmStartInstallNow.dfm} (100%)
rename windows/src/desktop/kmshell/main/{UfrmStartInstallNow.pas => Keyman.Configuration.UI.UfrmStartInstallNow.pas} (93%)
diff --git a/windows/src/desktop/kmshell/kmshell.dpr b/windows/src/desktop/kmshell/kmshell.dpr
index d0915bd8656..87f9fbf6bfc 100644
--- a/windows/src/desktop/kmshell/kmshell.dpr
+++ b/windows/src/desktop/kmshell/kmshell.dpr
@@ -184,8 +184,8 @@ uses
Keyman.System.UpdateStateMachine in 'main\Keyman.System.UpdateStateMachine.pas',
Keyman.System.DownloadUpdate in 'main\Keyman.System.DownloadUpdate.pas',
Keyman.System.ExecutionHistory in '..\..\..\..\common\windows\delphi\general\Keyman.System.ExecutionHistory.pas',
- UfrmStartInstallNow in 'main\UfrmStartInstallNow.pas',
- UfrmStartInstall in 'main\UfrmStartInstall.pas';
+ Keyman.Configuration.UI.UfrmStartInstallNow in 'main\Keyman.Configuration.UI.UfrmStartInstallNow.pas' {frmInstallNow},
+ Keyman.Configuration.UI.UfrmStartInstall in 'main\Keyman.Configuration.UI.UfrmStartInstall.pas' {frmStartInstall};
{$R VERSION.RES}
{$R manifest.res}
diff --git a/windows/src/desktop/kmshell/kmshell.dproj b/windows/src/desktop/kmshell/kmshell.dproj
index 6784e9c6cb7..5be47294005 100644
--- a/windows/src/desktop/kmshell/kmshell.dproj
+++ b/windows/src/desktop/kmshell/kmshell.dproj
@@ -360,11 +360,11 @@
-
+
dfm
-
+
dfm
@@ -429,19 +429,19 @@
False
-
+
kmshell.exe
true
-
+
kmshell.exe
true
-
+
kmshell.exe
true
diff --git a/windows/src/desktop/kmshell/kmshell.res b/windows/src/desktop/kmshell/kmshell.res
new file mode 100644
index 0000000000000000000000000000000000000000..80494425e9cab438c239cd54b9fc76a6f09be330
GIT binary patch
literal 7036
zcmeI1O=uj+6~~`s1$k^NbP8*qI-STYftKc5qh&^3VeiJH_(Np%aST1vJ<_0=?#}c`
zvr-7?duj_CG}I-$&KHnP18_(G8&z@qlu4zXR1QI{GkPl`2#uixx3jkxFD@
zWQz)9g9!^JOmRdtfh}IHE9Jgm1vl!>2+nylr@LiO?vR;3D`iKLg4pgH|YyS-ZWu
zeeTdFO`k=0>QqK#x5W5)j8n$=Chj>X6^y83WRAl||1$brT7q4|izw9*?=AGZ@Zjfm
z5J@ZUZa?{)opIk2k(eTmZl0s-!;^7DL3Uc%B>I(jCuX7Q$drrN^p5#M1OFWO6ggKg
zj^|xyu8HB{)|&Vf+AuC=`owrdhiC_dwN}|6!av&BkQMG|(&6DDY8H55a^=Yicez%%
zQ+A~Ns
zfZqYX4SpSb4*WFuD0mON2i^nkf%m|B;Q4ie*E75x;pO5rBlrgRGw^lrN8k^@^XNOE
zw?VIi&Vil=9mV+P;E%!Yg5Lt~f?ohX2R;iv13m(N1pHO-7-UIJ}_rN2d69n`OZ`UKdT`u0PnHi!6_%rZz@JHYe!0&+Xv3%$p{4w}l
z@LS+r@C)F3EFX+pr+x67;FrPQ20sG6SAy!^IrwAnyWqFLBh%gj`1dEM?lr)lfv;iyLa#C_3PL4>eVZH`SK;bc=3XsK7C3%J3I8`
z$rIY%-lhi+9?<&wI$4%Qxm=Fs=jUm9dYUFDCn*|@()H`t>DskxG&D4X713EnWg+^x
zDCC=shU}0wHBdx{u!Si**p?>lAYccoaKY}yz9^!`+@g)jZ#?IkEm3dMbM6o!qQaVf
zE;`tWO<@ZcX-Zp;0oB*xLJYU;=f1fKAxQRz)&Ig*uOdd_{pEoBL??aKmy4ViaNr^YeBhV0vjVJ{@+mO5hC3$b^g*JuwLFl_9@
z@J9oCudw033T(MS6ByWZ+K(0rdSN?5r~L|V*o?!bH>dron1Dxt%(lWah(g$}`&9w3
z4O!R$09n{Rz_PR@z_PUE6fFBT148y~0Kz-a5rdQiT_xcS-G7^ZGSy#k#8428Jl73~
zxO#sM~c|#sh0*HqQBzd0c
z(E0Sy33T`ehm*Du+I1;+j?ktDatF}TTXH+l)Z67YAkaJI7U1jMaublcl>IXnpR}5J
z5W4Rz^ta*;T_%e4VDK(o0MG+jp5a>lVg!Ce#NmwxY`hhgoS%DHZOW{3V4{~*Ul#y#
z>?d^8zKru6&U#GB7rhm2sq+~Sx&Y)%Vg}~%53%t1P#iGPFAB*Dcb-a|A6dr;O~G&Y
zvh;rs&^&(%|Nj93Iq>fSnsQ5TkD(>E=`Em5Zr1~#UGC5&&>?r~40Os}I)LDy&B;R%
zdH*TjCJ|g1m*)-9U7Et4d=J-6@vCGIcew3*FHiNu!^7fq&%bW<;r!3{i^RXt^pm1U
zjsK@`0hZMGyu`Mw-6yb-ARhniaQbTNyPn8deq4%G*ZKTFj_(
zvtMc{RdK7jW#}c_GBfI$>8dv-uUty&u4|Tar8NZuSZ+pLsaT)6`6aWgyIQ%Juh_0#
zsA_q;{F&~SM^_`NQr4|v!E~$h9kU5{W>#b0J4j5$%vUZcO4=|T(=rh0+9b@jj_Q;j
zm+XfU1eyh2EmqeA&py>P2M<&$TCu3w?6mYUI`0M#FuG8*47=uz-n7eQ+tO~@R<&Z6
z+?GQ#cgfK(zgmj3k}DPSzctrp?PU|mO(rH1xnw*NkLUA+h*4;fCEZ$F(HAlCuUnl?
z#m<|qYgeW!`K4mj%vV<`f*A83c`Qgm$1|s!-N=o|RkgC>REZ?7l4xAV6
zq;lm_D6KmV-c5{STcRVB{)MK^E}2U9mXb3|c1^)bVZd}1u!_6lICiC~6zq!PFCt~W
zh>wz@X^1KmO8bOq-2Y0+8X5JEW;7g$#1rFMBoR+)@mM~g>4}1=g~KVsG|iY1D}?`~
zj(za-k4AdW9VsNollk$amI|8@Egp?cX!wZc
zv{cLp7m`NU(8qtw+>vnasZ(aSFs?_UTCR|bA>n39L;4e1JXJ6fVLg)2Cq%+Sp%AaW
zv99Hp9_v`9yv)afFFF|57l_j0d;b!Cx4na%W)^=_xsShr-9>8(^>6Uks9F34OP2OM
Ifp>}j0>X0-NdN!<
literal 0
HcmV?d00001
diff --git a/windows/src/desktop/kmshell/main/UfrmStartInstall.dfm b/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstall.dfm
similarity index 100%
rename from windows/src/desktop/kmshell/main/UfrmStartInstall.dfm
rename to windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstall.dfm
diff --git a/windows/src/desktop/kmshell/main/UfrmStartInstall.pas b/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstall.pas
similarity index 93%
rename from windows/src/desktop/kmshell/main/UfrmStartInstall.pas
rename to windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstall.pas
index 2e54d140273..6628ea570e7 100644
--- a/windows/src/desktop/kmshell/main/UfrmStartInstall.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstall.pas
@@ -1,4 +1,4 @@
-unit UfrmStartInstall;
+unit Keyman.Configuration.UI.UfrmStartInstall;
{
Copyright: © SIL Global.
// TODO: Localise all the labels and captions.
diff --git a/windows/src/desktop/kmshell/main/UfrmStartInstallNow.dfm b/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstallNow.dfm
similarity index 100%
rename from windows/src/desktop/kmshell/main/UfrmStartInstallNow.dfm
rename to windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstallNow.dfm
diff --git a/windows/src/desktop/kmshell/main/UfrmStartInstallNow.pas b/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstallNow.pas
similarity index 93%
rename from windows/src/desktop/kmshell/main/UfrmStartInstallNow.pas
rename to windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstallNow.pas
index 2895502e8e0..9603dec5d63 100644
--- a/windows/src/desktop/kmshell/main/UfrmStartInstallNow.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstallNow.pas
@@ -1,4 +1,4 @@
-unit UfrmStartInstallNow;
+unit Keyman.Configuration.UI.UfrmStartInstallNow;
{
Copyright: © SIL Global.
// TODO: Localise all the labels and captions.
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
index 5cdc681692e..3e22b1f7fea 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
@@ -32,8 +32,8 @@ interface
httpuploader,
Keyman.System.UpdateCheckResponse,
- UfrmStartInstall,
- UfrmStartInstallNow,
+ Keyman.Configuration.UI.UfrmStartInstall,
+ Keyman.Configuration.UI.UfrmStartInstallNow,
Keyman.System.ExecutionHistory,
UfrmDownloadProgress;
diff --git a/windows/src/engine/kmcomapi/util/utilkeymanoption.pas b/windows/src/engine/kmcomapi/util/utilkeymanoption.pas
index 5ceafbc07e4..8a18f3be262 100644
--- a/windows/src/engine/kmcomapi/util/utilkeymanoption.pas
+++ b/windows/src/engine/kmcomapi/util/utilkeymanoption.pas
@@ -121,7 +121,7 @@ TKeymanOptionInfo = record
GroupName: string;
end;
-const KeymanOptionInfo: array[0..16] of TKeymanOptionInfo = ( // I3331 // I3620 // I4552
+const KeymanOptionInfo: array[0..17] of TKeymanOptionInfo = ( // I3331 // I3620 // I4552
// Global options
(opt: koKeyboardHotkeysAreToggle; RegistryName: SRegValue_KeyboardHotkeysAreToggle; OptionType: kotBool; BoolValue: False; GroupName: 'kogGeneral'),
From c98827162891e6d895c878639d57484167382e2b Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Tue, 15 Oct 2024 12:12:21 +1000
Subject: [PATCH 34/43] feat(windows): formatting review suggestions
Co-authored-by: Marc Durdin
---
.../Keyman.System.ExecutionHistory.pas | 12 ++--
.../windows/delphi/general/RegistryKeys.pas | 2 +-
oem/firstvoices/windows/src/xml/strings.xml | 2 +-
...yman.Configuration.UI.UfrmStartInstall.pas | 7 ++-
...n.Configuration.UI.UfrmStartInstallNow.pas | 9 +--
.../main/Keyman.System.UpdateStateMachine.pas | 56 +++++++++----------
6 files changed, 43 insertions(+), 45 deletions(-)
diff --git a/common/windows/delphi/general/Keyman.System.ExecutionHistory.pas b/common/windows/delphi/general/Keyman.System.ExecutionHistory.pas
index eb3bb11df3c..52fda002dcb 100644
--- a/common/windows/delphi/general/Keyman.System.ExecutionHistory.pas
+++ b/common/windows/delphi/general/Keyman.System.ExecutionHistory.pas
@@ -1,12 +1,12 @@
-unit Keyman.System.ExecutionHistory;
-
{
- Copyright: © SIL Global.
+ Keyman is copyright (C) SIL Global. MIT License.
This module provides functionality to track the execution state of the Keyman
engine. It uses a global atom to record whether Keyman has started during the
current session and checks if it has previously run.
}
+unit Keyman.System.ExecutionHistory;
+
interface
@@ -17,9 +17,11 @@ function RecordKeymanStarted : Boolean;
function HasKeymanRun : Boolean;
implementation
+
uses
- System.SysUtils,KLog,
- Winapi.Windows;
+ System.SysUtils,
+ Winapi.Windows,
+ KLog;
function RecordKeymanStarted : Boolean;
var
diff --git a/common/windows/delphi/general/RegistryKeys.pas b/common/windows/delphi/general/RegistryKeys.pas
index 511f36e6d95..5bcbb967927 100644
--- a/common/windows/delphi/general/RegistryKeys.pas
+++ b/common/windows/delphi/general/RegistryKeys.pas
@@ -317,7 +317,7 @@ interface
SRegValue_AutomaticUpdates = 'automatic updates'; //CU
SRegValue_CheckForUpdates = 'check for updates'; // CU
SRegValue_LastUpdateCheckTime = 'last update check time'; // CU
- SRegValue_ApplyNow = 'apply now'; // CU Start the install now even thought it will require an update
+ SRegValue_ApplyNow = 'apply now'; // CU Start the install now even though it will require an update
SRegValue_UpdateCheck_UseProxy = 'update check use proxy'; // CU
SRegValue_UpdateCheck_ProxyHost = 'update check proxy host'; // CU
diff --git a/oem/firstvoices/windows/src/xml/strings.xml b/oem/firstvoices/windows/src/xml/strings.xml
index bbb79c88692..692a582dd72 100644
--- a/oem/firstvoices/windows/src/xml/strings.xml
+++ b/oem/firstvoices/windows/src/xml/strings.xml
@@ -391,7 +391,7 @@
- Automatically download updates ready to install
+ Automatically download updates in the background, for installation later
diff --git a/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstall.pas b/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstall.pas
index 6628ea570e7..7e2de18a039 100644
--- a/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstall.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstall.pas
@@ -1,8 +1,9 @@
-unit Keyman.Configuration.UI.UfrmStartInstall;
{
- Copyright: © SIL Global.
- // TODO: Localise all the labels and captions.
+ Keyman is copyright (C) SIL Global. MIT License.
+
+ // TODO-WINDOWS-UPDATES: Localise all the labels and captions.
}
+unit Keyman.Configuration.UI.UfrmStartInstall;
interface
uses
diff --git a/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstallNow.pas b/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstallNow.pas
index 9603dec5d63..e5d0c2e5015 100644
--- a/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstallNow.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstallNow.pas
@@ -1,8 +1,9 @@
-unit Keyman.Configuration.UI.UfrmStartInstallNow;
{
- Copyright: © SIL Global.
- // TODO: Localise all the labels and captions.
+ Keyman is copyright (C) SIL Global. MIT License.
+
+ // TODO-WINDOWS-UPDATES: Localise all the labels and captions.
}
+unit Keyman.Configuration.UI.UfrmStartInstallNow;
interface
uses
@@ -27,7 +28,7 @@ implementation
{$R *.dfm}
-// TODO remove events as they are properties on the buttons
+// TODO-WINDOWS-UPDATES: remove events as they are properties on the buttons
procedure TfrmStartInstallNow.cmdInstallClick(Sender: TObject);
begin
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
index 3e22b1f7fea..263702c37a5 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
@@ -1,20 +1,8 @@
-(*
- Name: UpdateStateMachine
- Copyright: Copyright (C) SIL International.
- Documentation:
- Description:
- Create Date: 2 Nov 2023
-
- Modified Date: 2 Nov 2023
- Authors: rcruickshank
- Related Files:
- Dependencies:
-
- Bugs:
- Todo:
+{
+ Keyman is copyright (C) SIL Global. MIT License.
+
Notes: For the state diagram in mermaid ../BackgroundUpdateStateDiagram.md
- History:
-*)
+}
unit Keyman.System.UpdateStateMachine;
interface
@@ -25,17 +13,17 @@ interface
System.UITypes,
System.IOUtils,
System.Types,
+ System.TypInfo,
Vcl.Forms,
- TypInfo,
- KeymanPaths,
- utilkmshell,
httpuploader,
+ KeymanPaths,
Keyman.System.UpdateCheckResponse,
Keyman.Configuration.UI.UfrmStartInstall,
Keyman.Configuration.UI.UfrmStartInstallNow,
Keyman.System.ExecutionHistory,
- UfrmDownloadProgress;
+ UfrmDownloadProgress,
+ utilkmshell;
const
CheckPeriod: Integer = 7; // Days between checking for updates
@@ -82,11 +70,12 @@ TUpdateStateMachineDownloadParams = record
StartPosition: Integer;
end;
- // Forward declaration
- TUpdateStateMachine = class;
- { State Classes Update }
+ // Forward declaration
+ TUpdateStateMachine = class;
- TStateClass = class of TState;
+ { State Classes Update }
+
+ TStateClass = class of TState;
TState = class abstract
private
@@ -282,9 +271,11 @@ TOnlineUpdateSharedData = class(TInterfacedObject, IOnlineUpdateSharedData)
public
constructor Create(AParams: TUpdateStateMachineParams);
function Params: TUpdateStateMachineParams;
- end;
- // Private Utility functions
- function ConfigCheckContinue: Boolean;
+end;
+
+// Private Utility functions
+function ConfigCheckContinue: Boolean;
+
implementation
uses
@@ -305,7 +296,7 @@ implementation
Upload_Settings,
utildir,
utilexecute,
- OnlineUpdateCheckMessages, // todo create own messages
+ OnlineUpdateCheckMessages, // TODO-WINDOWS-UPDATES: create own messages
UfrmOnlineUpdateIcon,
UfrmOnlineUpdateNewVersion,
utilsystem,
@@ -372,10 +363,16 @@ procedure TUpdateStateMachine.SavePackageUpgradesToDownloadTempPath;
StringList := TStringList.Create;
try
for i := 0 to High(FParams.Packages) do
+ begin
if FParams.Packages[i].NewID <> '' then
+ begin
StringList.Add(FParams.Packages[i].NewID+'='+FParams.Packages[i].ID);
+ end;
+ end;
if StringList.Count > 0 then
+ begin
StringList.SaveToFile(DownloadTempPath + SPackageUpgradeFileName);
+ end;
finally
StringList.Free;
end;
@@ -538,12 +535,10 @@ function TUpdateStateMachine.CheckUpdateSchedule: Boolean;
begin
if RegistryErrorControlled.ValueExists(SRegValue_CheckForUpdates) and not RegistryErrorControlled.ReadBool(SRegValue_CheckForUpdates) and not FForce then
begin
- Result := False;
Exit;
end;
if RegistryErrorControlled.ValueExists(SRegValue_LastUpdateCheckTime) and (Now - RegistryErrorControlled.ReadDateTime(SRegValue_LastUpdateCheckTime) < 1) and not FForce then
begin
- Result := False;
Exit;
end;
// Else Time to check for updates
@@ -1318,7 +1313,6 @@ function ConfigCheckContinue: Boolean;
begin
if registry.ValueExists(SRegValue_CheckForUpdates) and not registry.ReadBool(SRegValue_CheckForUpdates) then
begin
- Result := False;
Exit;
end;
if registry.ValueExists(SRegValue_LastUpdateCheckTime) and (Now - registry.ReadDateTime(SRegValue_LastUpdateCheckTime) > CheckPeriod) then
From ac38649ffbd77e08244016b9ac732dfa0b6a394c Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Tue, 15 Oct 2024 13:31:47 +1000
Subject: [PATCH 35/43] feat(windows): More formatting fixes.
Co-authored-by: Marc Durdin
---
...yman.Configuration.UI.UfrmStartInstall.pas | 27 ++++----
.../main/Keyman.System.DownloadUpdate.pas | 66 +++++++------------
.../main/Keyman.System.RemoteUpdateCheck.pas | 7 --
...eyman.System.Install.EnginePostInstall.pas | 2 +-
4 files changed, 39 insertions(+), 63 deletions(-)
diff --git a/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstall.pas b/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstall.pas
index 7e2de18a039..dddcedb668e 100644
--- a/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstall.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstall.pas
@@ -7,20 +7,19 @@
interface
uses
-
- Windows,
- Messages,
- SysUtils,
- Variants,
- Classes,
- Graphics,
- Controls,
- Forms,
- Dialogs,
- UserMessages,
- StdCtrls,
- ExtCtrls,
- UfrmKeymanBase;
+ System.Classes,
+ System.SysUtils,
+ System.Variants,
+ Vcl.Controls,
+ Vcl.Dialogs,
+ Vcl.ExtCtrls,
+ Vcl.Forms,
+ Vcl.Graphics,
+ Vcl.StdCtrls,
+ Winapi.Messages,
+ Winapi.Windows,
+ UfrmKeymanBase,
+ UserMessages;
type
TfrmStartInstall = class(TfrmKeymanBase)
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
index dcc6e28faad..aa4304d7e8f 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
@@ -1,21 +1,6 @@
(*
- Name: WebUpdateCheck
- Copyright: Copyright (C) SIL Global.
- Documentation:
- Description:
- Create Date: 5 Dec 2023
-
- Modified Date:
- Authors: rcruickshank
- Related Files:
- Dependencies:
-
- Bugs:
- Todo:
- Notes:
- History:
-*)
-
+ * Keyman is copyright (C) SIL Global. MIT License.
+ *)
unit Keyman.System.DownloadUpdate;
interface
@@ -28,7 +13,7 @@ interface
OnlineUpdateCheck;
const
- CheckPeriod: Integer = 7; // Days between checking for updates
+ DaysBetweenCheckingForUpdates: Integer = 7; // Days between checking for updates
type
TDownloadUpdateParams = record
@@ -88,7 +73,7 @@ implementation
System.Types,
System.StrUtils;
- // temp wrapper for converting showmessage to logs don't know where
+ // TODO-WINDOWS-UPDATES: temp wrapper for converting showmessage to logs don't know where
// if not using klog
procedure LogMessage(LogMessage: string);
begin
@@ -142,9 +127,10 @@ procedure TDownloadUpdate.DoDownloadUpdates(SavePath: string; Params: TUpdateChe
Result := True;
end
else // I2742
+ begin
// If it fails we set to false but will try the other files
- Result := False;
- Exit;
+ Exit(False);
+ end;
finally
http.Free;
end;
@@ -181,20 +167,20 @@ procedure TDownloadUpdate.DoDownloadUpdates(SavePath: string; Params: TUpdateChe
// Keyboard Packages
FDownload.StartPosition := 0;
for i := 0 to High(Params.Packages) do
+ begin
+ if not DownloadFile(Params.Packages[i].DownloadURL, Params.Packages[i].SavePath) then // I2742
begin
- if not DownloadFile(Params.Packages[i].DownloadURL, Params.Packages[i].SavePath) then // I2742
- begin
- Params.Packages[i].Install := False; // Download failed but install other files
- end
- else
- Inc(downloadCount);
- FDownload.StartPosition := FDownload.StartPosition + Params.Packages[i].DownloadSize;
- end;
+ Params.Packages[i].Install := False; // Download failed but install other files
+ end
+ else
+ Inc(downloadCount);
+ FDownload.StartPosition := FDownload.StartPosition + Params.Packages[i].DownloadSize;
+ end;
// Keyman Installer
if not DownloadFile(Params.InstallURL, SavePath + Params.FileName) then // I2742
begin
- // TODO: #10210 convert to error log.
+ // TODO-WINDOWS-UPDATES: #10210 convert to error log.
LogMessage('DoDownloadUpdates Failed to download' + Params.InstallURL);
end
else
@@ -249,23 +235,21 @@ function TDownloadUpdate.CheckAllFilesDownloaded: Boolean;
begin
Inc(VerifyDownloads.TotalDownloads);
Inc(VerifyDownloads.TotalSize, Params.Packages[i].DownloadSize);
- if Not MatchStr(Params.Packages[i].FileName, FileNames) then
- begin
- Result := False;
- Exit;
- end;
+ if not MatchStr(Params.Packages[i].FileName, FileNames) then
+ begin
+ Exit(False);
+ end;
Params.Packages[i].SavePath := SavedPath + Params.Packages[i].FileName;
end;
// Add the Keyman installer
Inc(FDownload.TotalDownloads);
Inc(FDownload.TotalSize, Params.InstallSize);
// Check if the Keyman installer downloaded
- if Not MatchStr(Params.FileName, FileNames) then
- begin
- Result := False;
- Exit;
- end;
- // TODO verify filesizes match so we know we don't have partial downloades.
+ if not MatchStr(Params.FileName, FileNames) then
+ begin
+ Exit(False);
+ end;
+ // TODO-WINDOWS-UPDATES: verify filesizes match so we know we don't have partial downloades.
Result := True;
end
else
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas b/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas
index b1738bad936..ba3e8b62eb4 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas
@@ -231,18 +231,12 @@ function ConfigCheckContinue: Boolean;
begin
if registry.ValueExists(SRegValue_CheckForUpdates) and not registry.ReadBool(SRegValue_CheckForUpdates) then
begin
- Result := False;
Exit;
end;
if registry.ValueExists(SRegValue_LastUpdateCheckTime) and (Now - registry.ReadDateTime(SRegValue_LastUpdateCheckTime) > CheckPeriod) then
begin
Result := True;
- end
- else
- begin
- Result := False;
end;
- Exit;
end;
finally
registry.Free;
@@ -253,7 +247,6 @@ function ConfigCheckContinue: Boolean;
begin
Result := False;
LogMessage(E.Message);
- Exit;
end;
end;
end;
diff --git a/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas b/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas
index 6b0fe419b20..31846bf8e1e 100644
--- a/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas
+++ b/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas
@@ -94,7 +94,7 @@ function EnginePostInstall(hInstall: MSIHANDLE): UINT;
end;
Result := ERROR_SUCCESS;
- // TODO better error checking on the registry key update
+ // TODO-WINDOWS-UPDATES: better error checking on the registry key update
UpdateState;
finally
From 141bfda66accf0df66f7429831d15de01879235b Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Tue, 22 Oct 2024 16:42:11 +1000
Subject: [PATCH 36/43] feat(windows): remove redundant time since last update
Remove the extra time since last update check and let remote
update check control it. Also remove the events on the buttons
as and use the button modal property
---
...yman.Configuration.UI.UfrmStartInstall.dfm | 4 +-
...yman.Configuration.UI.UfrmStartInstall.pas | 11 --
...n.Configuration.UI.UfrmStartInstallNow.dfm | 4 +-
...n.Configuration.UI.UfrmStartInstallNow.pas | 31 +++--
.../main/Keyman.System.DownloadUpdate.pas | 14 +--
.../main/Keyman.System.UpdateStateMachine.pas | 112 ++----------------
...eyman.System.Install.EnginePostInstall.pas | 7 +-
7 files changed, 42 insertions(+), 141 deletions(-)
diff --git a/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstall.dfm b/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstall.dfm
index 010059e4418..9084afe1b69 100644
--- a/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstall.dfm
+++ b/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstall.dfm
@@ -32,8 +32,8 @@ object frmStartInstall: TfrmStartInstall
Width = 75
Height = 25
Caption = 'Install'
+ ModalResult = 1
TabOrder = 0
- OnClick = cmdInstallClick
end
object cmdLater: TButton
Left = 336
@@ -41,7 +41,7 @@ object frmStartInstall: TfrmStartInstall
Width = 75
Height = 25
Caption = 'Close'
+ ModalResult = 8
TabOrder = 1
- OnClick = cmdLaterClick
end
end
diff --git a/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstall.pas b/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstall.pas
index dddcedb668e..8571c0b5158 100644
--- a/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstall.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstall.pas
@@ -26,8 +26,6 @@ TfrmStartInstall = class(TfrmKeymanBase)
cmdInstall: TButton;
cmdLater: TButton;
lblInstallUpdate: TLabel;
- procedure cmdInstallClick(Sender: TObject);
- procedure cmdLaterClick(Sender: TObject);
private
public
end;
@@ -37,14 +35,5 @@ implementation
{$R *.dfm}
-procedure TfrmStartInstall.cmdInstallClick(Sender: TObject);
-begin
- ModalResult := mrOk;
-end;
-
-procedure TfrmStartInstall.cmdLaterClick(Sender: TObject);
-begin
- ModalResult := mrCancel;
-end;
end.
diff --git a/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstallNow.dfm b/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstallNow.dfm
index 289745efd5e..eae54930403 100644
--- a/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstallNow.dfm
+++ b/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstallNow.dfm
@@ -46,8 +46,8 @@ object frmStartInstallNow: TfrmStartInstallNow
Width = 75
Height = 25
Caption = 'Update'
+ ModalResult = 1
TabOrder = 0
- OnClick = cmdInstallClick
end
object cmdLater: TButton
Left = 336
@@ -55,7 +55,7 @@ object frmStartInstallNow: TfrmStartInstallNow
Width = 75
Height = 25
Caption = 'Close'
+ ModalResult = 8
TabOrder = 1
- OnClick = cmdLaterClick
end
end
diff --git a/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstallNow.pas b/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstallNow.pas
index e5d0c2e5015..5f9c9caefc7 100644
--- a/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstallNow.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.Configuration.UI.UfrmStartInstallNow.pas
@@ -1,15 +1,25 @@
{
Keyman is copyright (C) SIL Global. MIT License.
-
+
// TODO-WINDOWS-UPDATES: Localise all the labels and captions.
}
unit Keyman.Configuration.UI.UfrmStartInstallNow;
interface
uses
-
- Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
- Dialogs, UserMessages, StdCtrls, ExtCtrls, UfrmKeymanBase;
+ System.Classes,
+ System.SysUtils,
+ System.Variants,
+ Vcl.Controls,
+ Vcl.Dialogs,
+ Vcl.ExtCtrls,
+ Vcl.Forms,
+ Vcl.Graphics,
+ Vcl.StdCtrls,
+ Winapi.Messages,
+ Winapi.Windows,
+ UfrmKeymanBase,
+ UserMessages;
type
TfrmStartInstallNow = class(TfrmKeymanBase)
@@ -17,8 +27,6 @@ TfrmStartInstallNow = class(TfrmKeymanBase)
cmdLater: TButton;
lblUpdateMessage: TLabel;
lblUpdateNow: TLabel;
- procedure cmdInstallClick(Sender: TObject);
- procedure cmdLaterClick(Sender: TObject);
private
public
end;
@@ -28,16 +36,5 @@ implementation
{$R *.dfm}
-// TODO-WINDOWS-UPDATES: remove events as they are properties on the buttons
-
-procedure TfrmStartInstallNow.cmdInstallClick(Sender: TObject);
-begin
- ModalResult := mrOk;
-end;
-
-procedure TfrmStartInstallNow.cmdLaterClick(Sender: TObject);
-begin
- ModalResult := mrCancel;
-end;
end.
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
index aa4304d7e8f..57c8a2cad06 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
@@ -7,9 +7,9 @@ interface
uses
System.Classes,
System.SysUtils,
- KeymanPaths,
httpuploader,
Keyman.System.UpdateCheckResponse,
+ KeymanPaths,
OnlineUpdateCheck;
const
@@ -59,19 +59,19 @@ implementation
uses
+ System.StrUtils,
+ System.Types,
+ ErrorControlledRegistry,
GlobalProxySettings,
- KLog,
keymanapi_TLB,
KeymanVersion,
Keyman.System.UpdateCheckStorage,
+ KLog,
kmint,
- ErrorControlledRegistry,
+ OnlineUpdateCheckMessages,
RegistryKeys,
Upload_Settings,
- OnlineUpdateCheckMessages,
- utilkmshell,
- System.Types,
- System.StrUtils;
+ utilkmshell;
// TODO-WINDOWS-UPDATES: temp wrapper for converting showmessage to logs don't know where
// if not using klog
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
index 263702c37a5..ea19ab70662 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
@@ -1,6 +1,6 @@
{
Keyman is copyright (C) SIL Global. MIT License.
-
+
Notes: For the state diagram in mermaid ../BackgroundUpdateStateDiagram.md
}
unit Keyman.System.UpdateStateMachine;
@@ -25,9 +25,6 @@ interface
UfrmDownloadProgress,
utilkmshell;
-const
- CheckPeriod: Integer = 7; // Days between checking for updates
-
type
EUpdateStateMachine = class(Exception);
@@ -234,7 +231,6 @@ TUpdateStateMachine = class
tempPath.
}
procedure SavePackageUpgradesToDownloadTempPath;
- function checkUpdateSchedule : Boolean;
function SetRegistryState (Update : TUpdateState): Boolean;
function GetAutomaticUpdates: Boolean;
@@ -273,9 +269,6 @@ TOnlineUpdateSharedData = class(TInterfacedObject, IOnlineUpdateSharedData)
function Params: TUpdateStateMachineParams;
end;
-// Private Utility functions
-function ConfigCheckContinue: Boolean;
-
implementation
uses
@@ -521,43 +514,6 @@ function TUpdateStateMachine.GetApplyNow: Boolean;
end;
end;
-
-function TUpdateStateMachine.CheckUpdateSchedule: Boolean;
-var
- RegistryErrorControlled :TRegistryErrorControlled;
-begin
- try
- Result := False;
- RegistryErrorControlled := TRegistryErrorControlled.Create;
-
- try
- if RegistryErrorControlled.OpenKeyReadOnly(SRegKey_KeymanDesktop_CU) then
- begin
- if RegistryErrorControlled.ValueExists(SRegValue_CheckForUpdates) and not RegistryErrorControlled.ReadBool(SRegValue_CheckForUpdates) and not FForce then
- begin
- Exit;
- end;
- if RegistryErrorControlled.ValueExists(SRegValue_LastUpdateCheckTime) and (Now - RegistryErrorControlled.ReadDateTime(SRegValue_LastUpdateCheckTime) < 1) and not FForce then
- begin
- Exit;
- end;
- // Else Time to check for updates
- Result := True;
- end;
- finally
- RegistryErrorControlled.Free;
- end;
- except
- { we will not run the check if an error occurs reading the settings }
- on E:Exception do
- begin
- Result := False;
- FErrorMessage := E.Message;
- Exit;
- end;
- end;
-end;
-
function TUpdateStateMachine.GetState: TStateClass;
begin
Result := TStateClass(CurrentState.ClassType);
@@ -724,22 +680,20 @@ function IdleState.HandleKmShell;
var
CheckForUpdates: TRemoteUpdateCheck;
UpdateCheckResult : TRemoteUpdateCheckResult;
-//const CheckPeriod: Integer = 7; // Days between checking for updates
begin
- // Check if auto updates enable and if scheduled time has expired
- if ConfigCheckContinue then
+ // Remote manages the last check time therfore
+ // we will allow it to return early if it hasn't reached
+ // the configured time between checks.
+ CheckForUpdates := TRemoteUpdateCheck.Create(False);
+ try
+ UpdateCheckResult:= CheckForUpdates.Run;
+ finally
+ CheckForUpdates.Free;
+ end;
+ { Response OK and Update is available }
+ if UpdateCheckResult = wucSuccess then
begin
- CheckForUpdates := TRemoteUpdateCheck.Create(True);
- try
- UpdateCheckResult:= CheckForUpdates.Run;
- finally
- CheckForUpdates.Free;
- end;
- { Response OK and Update is available }
- if UpdateCheckResult = wucSuccess then
- begin
- ChangeState(UpdateAvailableState);
- end;
+ ChangeState(UpdateAvailableState);
end;
Result := kmShellContinue;
end;
@@ -1299,44 +1253,4 @@ function PostInstallState.StateName;
Result := 'PostInstallState';
end;
-// Private Functions:
-function ConfigCheckContinue: Boolean;
-var
- registry: TRegistryErrorControlled;
-begin
-{ Verify that it has been at least CheckPeriod days since last update check }
- Result := False;
- try
- registry := TRegistryErrorControlled.Create; // I2890
- try
- if registry.OpenKeyReadOnly(SRegKey_KeymanDesktop_CU) then
- begin
- if registry.ValueExists(SRegValue_CheckForUpdates) and not registry.ReadBool(SRegValue_CheckForUpdates) then
- begin
- Exit;
- end;
- if registry.ValueExists(SRegValue_LastUpdateCheckTime) and (Now - registry.ReadDateTime(SRegValue_LastUpdateCheckTime) > CheckPeriod) then
- begin
- Result := True;
- end
- else
- begin
- Result := False;
- end;
- Exit;
- end;
- finally
- registry.Free;
- end;
- except
- { we will not run the check if an error occurs reading the settings }
- on E:Exception do
- begin
- Result := False;
- LogMessage(E.Message);
- Exit;
- end;
- end;
-end;
-
end.
diff --git a/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas b/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas
index 31846bf8e1e..4871d939764 100644
--- a/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas
+++ b/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas
@@ -36,7 +36,8 @@ function UpdateState: Boolean;
Result := False;
UpdateStr := 'usPostInstall';
- if RegOpenKeyEx(HKEY_LOCAL_MACHINE, PChar(SRegKey_KeymanEngine_CU), 0, KEY_ALL_ACCESS, hk) = ERROR_SUCCESS then
+
+ if RegCreateKeyEx(HKEY_CURRENT_USER, PChar(SRegKey_KeymanEngine_CU), 0, NULL, KEY_ALL_ACCESS, NULL, &hk, NULL) = ERROR_SUCCESS then
begin
try
if RegSetValueEx(hk, PChar(SRegValue_Update_State), 0, REG_SZ, PWideChar(UpdateStr), Length(UpdateStr) * SizeOf(Char)) = ERROR_SUCCESS then
@@ -45,7 +46,7 @@ function UpdateState: Boolean;
end
else
begin
- // error log
+ // TODO-WINDOWS-UPDATES: error log
end;
finally
RegCloseKey(hk);
@@ -53,7 +54,7 @@ function UpdateState: Boolean;
end
else
begin
- // TODO: couldn't open registry key
+ // TODO-WINDOWS-UPDATES: error log creating key
end;
end;
From 3cf1fe5895276a053ffaaa96e7c73ce596e1bf36 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Wed, 23 Oct 2024 15:03:47 +1000
Subject: [PATCH 37/43] feat(windows): use FStateInstance array instead
Use FStateInstance array instead of invidual StateClass flags
---
.../main/Keyman.System.UpdateStateMachine.pas | 109 +++++++-----------
1 file changed, 42 insertions(+), 67 deletions(-)
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
index ea19ab70662..3328b76fac3 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
@@ -211,17 +211,13 @@ TUpdateStateMachine = class
CurrentState: TState;
// State object for performance (could lazy create?)
- FIdle: IdleState;
- FUpdateAvailable: UpdateAvailableState;
- FDownloading: DownloadingState;
- FWaitingRestart: WaitingRestartState;
- FInstalling: InstallingState;
- FRetry: RetryState;
- FPostInstall: PostInstallState;
+
+ FStateInstance: array[TUpdateState] of TState;
+
function GetState: TStateClass;
procedure SetState(const Value: TStateClass);
- procedure SetStateOnly(const Value: TStateClass);
- function ConvertEnumState(const TEnumState: TUpdateState): TStateClass;
+ procedure SetStateOnly(const enumState: TUpdateState);
+ function ConvertStateToEnum(const StateClass: TStateClass): TUpdateState;
procedure ShutDown;
{
@@ -313,18 +309,22 @@ constructor TUpdateStateMachine.Create(AForce : Boolean);
FForce := AForce;
FAutomaticUpdate := GetAutomaticUpdates;
- FIdle := IdleState.Create(Self);
- FUpdateAvailable := UpdateAvailableState.Create(Self);
- FDownloading := DownloadingState.Create(Self);
- FWaitingRestart := WaitingRestartState.Create(Self);
- FInstalling := InstallingState.Create(Self);
- FRetry := RetryState.Create(Self);
- FPostInstall := PostInstallState.Create(Self);
+
+ FStateInstance[usIdle] := IdleState.Create(Self);
+ FStateInstance[usUpdateAvailable] := UpdateAvailableState.Create(Self);
+ FStateInstance[usDownloading] := DownloadingState.Create(Self);
+ FStateInstance[usWaitingRestart] := WaitingRestartState.Create(Self);
+ FStateInstance[usInstalling] := InstallingState.Create(Self);
+ FStateInstance[usRetry] := RetryState.Create(Self);
+ FStateInstance[usPostInstall] := PostInstallState.Create(Self);
+
// Check the Registry setting.
- SetStateOnly(ConvertEnumState(CheckRegistryState));
+ SetStateOnly(CheckRegistryState);
end;
destructor TUpdateStateMachine.Destroy;
+var
+ lpState: TUpdateState;
begin
if (FErrorMessage <> '') and FShowErrors then
KL.Log(FErrorMessage); // TODO: #10210 Log to Sentry
@@ -332,13 +332,10 @@ destructor TUpdateStateMachine.Destroy;
if FParams.Result = oucShutDown then
ShutDown;
- FIdle.Free;
- FUpdateAvailable.Free;
- FDownloading.Free;
- FWaitingRestart.Free;
- FInstalling.Free;
- FRetry.Free;
- FPostInstall.Free;
+ for lpState := Low(TUpdateState) to High(TUpdateState) do
+ begin
+ FStateInstance[lpState].Free;
+ end;
// TODO: #10210 remove debugging comments
//KL.Log('TUpdateStateMachine.Destroy: FErrorMessage = '+FErrorMessage);
@@ -440,11 +437,12 @@ function TUpdateStateMachine.CheckRegistryState: TUpdateState; // I2329
Registry.RootKey := HKEY_CURRENT_USER;
if Registry.OpenKeyReadOnly(SRegKey_KeymanEngine_CU) and Registry.ValueExists(SRegValue_Update_State) then
begin
+ // TODO-WINDOWS-UPDATES Check if value in register is valid
UpdateState := TUpdateState(GetEnumValue(TypeInfo(TUpdateState), Registry.ReadString(SRegValue_Update_State)));
end
else
begin
- UpdateState := usIdle; // do we need a unknown state ?
+ UpdateState := usIdle;
end;
finally
Registry.Free;
@@ -526,7 +524,7 @@ procedure TUpdateStateMachine.SetState(const Value: TStateClass);
CurrentState.Exit;
end;
- SetStateOnly(Value);
+ SetStateOnly(ConvertStateToEnum(Value));
if Assigned(CurrentState) then
begin
@@ -539,52 +537,29 @@ procedure TUpdateStateMachine.SetState(const Value: TStateClass);
end;
-procedure TUpdateStateMachine.SetStateOnly(const Value: TStateClass);
+procedure TUpdateStateMachine.SetStateOnly(const enumState: TUpdateState);
begin
- if Value = IdleState then
- begin
- CurrentState := FIdle;
- end
- else if Value = UpdateAvailableState then
- begin
- CurrentState := FUpdateAvailable;
- end
- else if Value = DownloadingState then
- begin
- CurrentState := FDownloading;
- end
- else if Value = WaitingRestartState then
- begin
- CurrentState := FWaitingRestart;
- end
- else if Value = InstallingState then
- begin
- CurrentState := FInstalling;
- end
- else if Value = RetryState then
- begin
- CurrentState := FRetry;
- end
- else if Value = PostInstallState then
- begin
- CurrentState := FPostInstall;
- end;
+ CurrentState := FStateInstance[enumState];
end;
-function TUpdateStateMachine.ConvertEnumState(const TEnumState: TUpdateState) : TStateClass;
+function TUpdateStateMachine.ConvertStateToEnum(const StateClass: TStateClass): TUpdateState;
begin
- case TEnumState of
- usIdle: Result := IdleState;
- usUpdateAvailable: Result := UpdateAvailableState;
- usDownloading: Result := DownloadingState;
- usWaitingRestart: Result := WaitingRestartState;
- usInstalling: Result := InstallingState;
- usRetry: Result := RetryState;
- usPostInstall: Result := PostInstallState;
+ if StateClass = IdleState then
+ Result := usIdle
+ else if StateClass = UpdateAvailableState then
+ Result := usUpdateAvailable
+ else if StateClass = DownloadingState then
+ Result := usDownloading
+ else if StateClass = WaitingRestartState then
+ Result := usWaitingRestart
+ else if StateClass = InstallingState then
+ Result := usInstalling
+ else if StateClass = RetryState then
+ Result := usRetry
+ else if StateClass = PostInstallState then
+ Result := usPostInstall
else
- // TODO: #10210 Log error unknown state setting to idle
- Result := IdleState;
- end;
+ KL.Log('Unknown StateClass'); // TODO-WINDOWS-UPDATES
end;
procedure TUpdateStateMachine.HandleCheck;
From f2d0e268a7712293e60f9cf8338249ea42bc3429 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Wed, 30 Oct 2024 16:59:32 +1000
Subject: [PATCH 38/43] feat(windows): remove TUpdateStateMachineParams
There was some dead code from before the RemoteUpdateCheck refactor
was created. This is removed in this commit
---
.../Keyman.System.ExecutionHistory.pas | 49 +++++--------------
.../windows/delphi/general/RegistryKeys.pas | 8 +--
2 files changed, 17 insertions(+), 40 deletions(-)
diff --git a/common/windows/delphi/general/Keyman.System.ExecutionHistory.pas b/common/windows/delphi/general/Keyman.System.ExecutionHistory.pas
index 52fda002dcb..33dff407802 100644
--- a/common/windows/delphi/general/Keyman.System.ExecutionHistory.pas
+++ b/common/windows/delphi/general/Keyman.System.ExecutionHistory.pas
@@ -27,49 +27,26 @@ function RecordKeymanStarted : Boolean;
var
atom: WORD;
begin
- Result := False;
- try
- atom := GlobalFindAtom(AtomName);
- if atom = 0 then
- begin
- if GetLastError <> ERROR_FILE_NOT_FOUND then
- RaiseLastOSError;
- atom := GlobalAddAtom(AtomName);
- Result := True;
- if atom = 0 then
- RaiseLastOSError;
- end;
- except
- on E: Exception do
- // TODO: #10210 convert to sentry error
- KL.Log(E.ClassName + ': ' + E.Message);
- end;
+ atom := GlobalAddAtom(AtomName);
+ if atom = 0 then
+ begin
+ // TODO-WINDOWS-UPDATES: #10210 log to sentry
+ Result := False;
+ end
+ else
+ Result := True;
end;
function HasKeymanRun : Boolean;
-var
- atom: WORD;
begin
- Result := False;
- try
- atom := GlobalFindAtom(AtomName);
- if atom <> 0 then
- begin
- if GetLastError <> ERROR_SUCCESS then
- RaiseLastOSError;
- Result := True;
- end
- else
+ Result := GlobalFindAtom(AtomName) <> 0;
+ if not Result then
+ begin
+ if GetLastError <> ERROR_FILE_NOT_FOUND then
begin
- Result := False;
+ // TODO-WINDOWS-UPDATES: log to Sentry
end;
-
- except
- on E: Exception do
- // TODO: #10210 convert to sentry error
- KL.log(E.ClassName + ': ' + E.Message);
end;
-
end;
end.
diff --git a/common/windows/delphi/general/RegistryKeys.pas b/common/windows/delphi/general/RegistryKeys.pas
index 5bcbb967927..bacca938c66 100644
--- a/common/windows/delphi/general/RegistryKeys.pas
+++ b/common/windows/delphi/general/RegistryKeys.pas
@@ -175,10 +175,10 @@ interface
SRegValue_CharMapSourceData = 'charmap source data'; // LM
- SRegValue_AvailableLanguages = 'available languages'; //CU
- SRegValue_CurrentLanguage = 'current language'; //CU
+ SRegValue_AvailableLanguages = 'available languages'; // CU
+ SRegValue_CurrentLanguage = 'current language'; // CU
- SRegValue_Update_State = 'update state';
+ SRegValue_Update_State = 'update state'; // CU
{ Privacy }
@@ -317,7 +317,7 @@ interface
SRegValue_AutomaticUpdates = 'automatic updates'; //CU
SRegValue_CheckForUpdates = 'check for updates'; // CU
SRegValue_LastUpdateCheckTime = 'last update check time'; // CU
- SRegValue_ApplyNow = 'apply now'; // CU Start the install now even though it will require an update
+ SRegValue_ApplyNow = 'apply now'; // CU Start the install now even though it will require an restart
SRegValue_UpdateCheck_UseProxy = 'update check use proxy'; // CU
SRegValue_UpdateCheck_ProxyHost = 'update check proxy host'; // CU
From 544b955570969a17b63fdf902533ca85b1679404 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Fri, 1 Nov 2024 18:24:51 +1000
Subject: [PATCH 39/43] feat(windows): add error checking - private class
---
.../main/Keyman.System.DownloadUpdate.pas | 115 +--
.../main/Keyman.System.UpdateStateMachine.pas | 735 +++++++-----------
2 files changed, 326 insertions(+), 524 deletions(-)
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
index 57c8a2cad06..9a3a27995b6 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
@@ -26,31 +26,32 @@ TDownloadUpdate = class
private
FShowErrors: Boolean;
FDownload: TDownloadUpdateParams;
- FErrorMessage: string;
- {
- Performs updates download in the background, without displaying a GUI
- progress bar.
- @params SavePath The path where the downloaded files will be saved.
- Result A Boolean value indicating the overall result of the
- download process.
- }
+ (**
+ *
+ * Performs updates download in the background, without displaying a GUI
+ * progress bar.
+ * @params SavePath The path where the downloaded files will be saved.
+ * Result A Boolean value indicating the overall result of the
+ * download process.
+ *)
procedure DoDownloadUpdates(SavePath: string; Params: TUpdateCheckResponse; var Result: Boolean);
public
constructor Create;
destructor Destroy; override;
- {
- Performs updates download in the background, without displaying a GUI
- progress bar. This function is similar to DownloadUpdates, but it runs in
- the background.
+ (**
+ * Performs updates download in the background, without displaying a GUI
+ * progress bar. This function is similar to DownloadUpdates, but it runs in
+ * the background.
- @returns True if all updates were successfully downloaded, False if any
- download failed.
- }
+ * @returns True if all updates were successfully downloaded, False if any
+ * download failed.
+ *)
function DownloadUpdates : Boolean;
- function CheckAllFilesDownloaded : Boolean;
+ // TODO-WINDOWS-UPDATES: verify filesizes match the ucr metadata so we know we don't have partial downloades.
+ //function VerifyAllFilesDownloaded : Boolean;
property ShowErrors: Boolean read FShowErrors write FShowErrors;
end;
@@ -90,18 +91,15 @@ constructor TDownloadUpdate.Create;
destructor TDownloadUpdate.Destroy;
begin
- if (FErrorMessage <> '') and FShowErrors then
- LogMessage(FErrorMessage);
-
- KL.Log('TDownloadUpdate.Destroy: FErrorMessage = '+FErrorMessage);
inherited Destroy;
end;
procedure TDownloadUpdate.DoDownloadUpdates(SavePath: string; Params: TUpdateCheckResponse; var Result: Boolean);
var
- i, downloadCount: Integer;
+ i : Integer;
http: THttpUploader;
fs: TFileStream;
+ InstallerDownloaded: Boolean;
function DownloadFile(const url, savepath: string): Boolean;
begin
@@ -150,47 +148,38 @@ procedure TDownloadUpdate.DoDownloadUpdates(SavePath: string; Params: TUpdateChe
FDownload.TotalSize := 0;
FDownload.TotalDownloads := 0;
- downloadCount := 0;
// Keyboard Packages
+ FDownload.StartPosition := 0;
for i := 0 to High(Params.Packages) do
begin
Inc(FDownload.TotalDownloads);
Inc(FDownload.TotalSize, Params.Packages[i].DownloadSize);
Params.Packages[i].SavePath := SavePath + Params.Packages[i].FileName;
- end;
-
- // Add the Keyman installer
- Inc(FDownload.TotalDownloads);
- Inc(FDownload.TotalSize, Params.InstallSize);
-
- // Keyboard Packages
- FDownload.StartPosition := 0;
- for i := 0 to High(Params.Packages) do
- begin
if not DownloadFile(Params.Packages[i].DownloadURL, Params.Packages[i].SavePath) then // I2742
begin
Params.Packages[i].Install := False; // Download failed but install other files
- end
- else
- Inc(downloadCount);
+ end;
FDownload.StartPosition := FDownload.StartPosition + Params.Packages[i].DownloadSize;
end;
// Keyman Installer
+ Inc(FDownload.TotalDownloads);
+ Inc(FDownload.TotalSize, Params.InstallSize);
if not DownloadFile(Params.InstallURL, SavePath + Params.FileName) then // I2742
begin
// TODO-WINDOWS-UPDATES: #10210 convert to error log.
LogMessage('DoDownloadUpdates Failed to download' + Params.InstallURL);
+ InstallerDownloaded := False;
end
else
begin
- Inc(downloadCount)
+ InstallerDownloaded := True;
end;
- // There needs to be at least one file successfully downloaded to return
- // True that files were downloaded
- if downloadCount > 0 then
+ // If installer has downloaded that is success even
+ // if zero packages where downloaded.
+ if InstallerDownloaded then
Result := True;
end;
@@ -211,52 +200,4 @@ function TDownloadUpdate.DownloadUpdates: Boolean;
Result := False;
end;
-function TDownloadUpdate.CheckAllFilesDownloaded: Boolean;
-var
- i : Integer;
- SavedPath : String;
- DownloadResult : Boolean;
- Params: TUpdateCheckResponse;
- VerifyDownloads : TDownloadUpdateParams;
- FileNames : TStringDynArray;
-
-begin
- SavedPath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
- GetFileNamesInDirectory(SavedPath, FileNames);
- if Length(FileNames) = 0 then
- begin
- Result := False;
- Exit;
- end;
-
- if TUpdateCheckStorage.LoadUpdateCacheData(Params) then
- begin
- for i := 0 to High(Params.Packages) do
- begin
- Inc(VerifyDownloads.TotalDownloads);
- Inc(VerifyDownloads.TotalSize, Params.Packages[i].DownloadSize);
- if not MatchStr(Params.Packages[i].FileName, FileNames) then
- begin
- Exit(False);
- end;
- Params.Packages[i].SavePath := SavedPath + Params.Packages[i].FileName;
- end;
- // Add the Keyman installer
- Inc(FDownload.TotalDownloads);
- Inc(FDownload.TotalSize, Params.InstallSize);
- // Check if the Keyman installer downloaded
- if not MatchStr(Params.FileName, FileNames) then
- begin
- Exit(False);
- end;
- // TODO-WINDOWS-UPDATES: verify filesizes match so we know we don't have partial downloades.
- Result := True;
- end
- else
- begin
- Result := False;
- end;
-
-end;
-
end.
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
index 3328b76fac3..eaf6b50bd83 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
@@ -1,71 +1,32 @@
-{
- Keyman is copyright (C) SIL Global. MIT License.
-
- Notes: For the state diagram in mermaid ../BackgroundUpdateStateDiagram.md
-}
+(*
+ * Keyman is copyright (C) SIL Global. MIT License.
+ *
+ * Notes: For the state diagram in mermaid ../BackgroundUpdateStateDiagram.md
+ *)
unit Keyman.System.UpdateStateMachine;
interface
uses
- System.Classes,
System.SysUtils,
System.UITypes,
System.IOUtils,
System.Types,
System.TypInfo,
- Vcl.Forms,
httpuploader,
KeymanPaths,
- Keyman.System.UpdateCheckResponse,
Keyman.Configuration.UI.UfrmStartInstall,
Keyman.Configuration.UI.UfrmStartInstallNow,
Keyman.System.ExecutionHistory,
- UfrmDownloadProgress,
+ Keyman.System.UpdateCheckResponse,
utilkmshell;
type
EUpdateStateMachine = class(Exception);
- TUpdateStateMachineResult = (oucUnknown, oucShutDown, oucSuccess, oucNoUpdates, oucUpdatesAvailable, oucFailure, oucOffline);
-
- TUpdateState = (usIdle, usUpdateAvailable, usDownloading, usWaitingRestart, usInstalling, usRetry, usPostInstall);
-
- { Keyboard Package Params }
- TUpdateStateMachineParamsPackage = record
- ID: string;
- NewID: string;
- Description: string;
- OldVersion, NewVersion: string;
- DownloadURL: string;
- SavePath: string;
- FileName: string;
- DownloadSize: Integer;
- Install: Boolean;
- end;
- { Main Keyman Program }
- TUpdateStateMachineParamsKeyman = record
- OldVersion, NewVersion: string;
- DownloadURL: string;
- SavePath: string;
- FileName: string;
- DownloadSize: Integer;
- Install: Boolean;
- end;
-
- TUpdateStateMachineParams = record
- Keyman: TUpdateStateMachineParamsKeyman;
- Packages: array of TUpdateStateMachineParamsPackage;
- Result: TUpdateStateMachineResult;
- end;
-
- TUpdateStateMachineDownloadParams = record
- Owner: TfrmDownloadProgress;
- TotalSize: Integer;
- TotalDownloads: Integer;
- StartPosition: Integer;
- end;
+ TUpdateState = (usIdle, usUpdateAvailable, usDownloading, usWaitingRestart,
+ usInstalling, usRetry, usPostInstall);
// Forward declaration
TUpdateStateMachine = class;
@@ -84,27 +45,100 @@ TState = class abstract
procedure Enter; virtual; abstract;
procedure Exit; virtual; abstract;
procedure HandleCheck; virtual; abstract;
- function HandleKmShell : Integer; virtual; abstract;
+ function HandleKmShell: Integer; virtual; abstract;
procedure HandleDownload; virtual; abstract;
procedure HandleAbort; virtual; abstract;
procedure HandleInstallNow; virtual; abstract;
+ end;
+
+ { This class also controls the state flow see
+ ../BackgroundUpdateStateDiagram.md }
+ TUpdateStateMachine = class
+ private
+ FForce: Boolean;
+ FAutomaticUpdate: Boolean;
+ FErrorMessage: string;
+ FShowErrors: Boolean;
+
+ CurrentState: TState;
+ // State object for performance (could lazy create?)
+
+ FStateInstance: array [TUpdateState] of TState;
+
+ function GetState: TStateClass;
+ procedure SetState(const Value: TStateClass);
+ procedure SetStateOnly(const enumState: TUpdateState);
+ function ConvertStateToEnum(const StateClass: TStateClass): TUpdateState;
+ function IsCurrentStateAssigned: Boolean;
+
+ function SetRegistryState(Update: TUpdateState): Boolean;
+ function GetAutomaticUpdates: Boolean;
+ function SetApplyNow(Value: Boolean): Boolean;
+ function GetApplyNow: Boolean;
+
+ protected
+ property State: TStateClass read GetState write SetState;
+
+ public
+ constructor Create(AForce: Boolean);
+ destructor Destroy; override;
+
+ procedure HandleCheck;
+ function HandleKmShell: Integer;
+ procedure HandleDownload;
+ procedure HandleAbort;
+ procedure HandleInstallNow;
+ function CurrentStateName: string;
- // For convenience
- function StateName: string; virtual; abstract;
+ property ShowErrors: Boolean read FShowErrors write FShowErrors;
+ function CheckRegistryState: TUpdateState;
end;
- // Derived classes for each state
+implementation
+
+uses
+
+ Winapi.Windows,
+ Winapi.WinINet,
+ ErrorControlledRegistry,
+
+ GlobalProxySettings,
+ Keyman.System.DownloadUpdate,
+ Keyman.System.RemoteUpdateCheck,
+ KLog,
+ RegistryKeys,
+ utilexecute;
+
+const
+ SPackageUpgradeFilename = 'upgrade_packages.inf';
+ kmShellContinue = 0;
+ kmShellExit = 1;
+
+ { State Class Memebers }
+
+constructor TState.Create(Context: TUpdateStateMachine);
+begin
+ bucStateContext := Context;
+end;
+
+procedure TState.ChangeState(newState: TStateClass);
+begin
+ bucStateContext.State := newState;
+end;
+
+type
+
+// Derived classes for each state
IdleState = class(TState)
public
procedure Enter; override;
procedure Exit; override;
procedure HandleCheck; override;
- function HandleKmShell : Integer; override;
+ function HandleKmShell: Integer; override;
procedure HandleDownload; override;
procedure HandleAbort; override;
procedure HandleInstallNow; override;
- function StateName: string; override;
end;
UpdateAvailableState = class(TState)
@@ -114,11 +148,10 @@ UpdateAvailableState = class(TState)
procedure Enter; override;
procedure Exit; override;
procedure HandleCheck; override;
- function HandleKmShell : Integer; override;
+ function HandleKmShell: Integer; override;
procedure HandleDownload; override;
procedure HandleAbort; override;
procedure HandleInstallNow; override;
- function StateName: string; override;
end;
DownloadingState = class(TState)
@@ -128,11 +161,10 @@ DownloadingState = class(TState)
procedure Enter; override;
procedure Exit; override;
procedure HandleCheck; override;
- function HandleKmShell : Integer; override;
+ function HandleKmShell: Integer; override;
procedure HandleDownload; override;
procedure HandleAbort; override;
procedure HandleInstallNow; override;
- function StateName: string; override;
end;
WaitingRestartState = class(TState)
@@ -140,35 +172,33 @@ WaitingRestartState = class(TState)
procedure Enter; override;
procedure Exit; override;
procedure HandleCheck; override;
- function HandleKmShell : Integer; override;
+ function HandleKmShell: Integer; override;
procedure HandleDownload; override;
procedure HandleAbort; override;
procedure HandleInstallNow; override;
- function StateName: string; override;
end;
InstallingState = class(TState)
private
- procedure DoInstallKeyman; overload;
- function DoInstallKeyman(SavePath: string) : Boolean; overload;
- {
- Installs the Keyman file using either msiexec.exe or the setup launched in
- a separate shell.
- @params Package The package to be installed.
+ (**
+ * Installs the Keyman setup file using separate shell.
+ *
+ * @params SavePath The path to the downloaded files.
+ *
+ * @returns True if the installation is successful, False otherwise.
+ *)
+
+ function DoInstallKeyman(SavePath: string): Boolean; overload;
- @returns True if the installation is successful, False otherwise.
- }
- function DoInstallPackage(Package: TUpdateStateMachineParamsPackage): Boolean;
public
procedure Enter; override;
procedure Exit; override;
procedure HandleCheck; override;
- function HandleKmShell : Integer; override;
+ function HandleKmShell: Integer; override;
procedure HandleDownload; override;
procedure HandleAbort; override;
procedure HandleInstallNow; override;
- function StateName: string; override;
end;
RetryState = class(TState)
@@ -176,136 +206,31 @@ RetryState = class(TState)
procedure Enter; override;
procedure Exit; override;
procedure HandleCheck; override;
- function HandleKmShell : Integer; override;
+ function HandleKmShell: Integer; override;
procedure HandleDownload; override;
procedure HandleAbort; override;
procedure HandleInstallNow; override;
- function StateName: string; override;
end;
PostInstallState = class(TState)
private
- procedure HandleMSIInstallComplete;
+ procedure HandleMSIInstallComplete;
public
procedure Enter; override;
procedure Exit; override;
procedure HandleCheck; override;
- function HandleKmShell : Integer; override;
+ function HandleKmShell: Integer; override;
procedure HandleDownload; override;
procedure HandleAbort; override;
procedure HandleInstallNow; override;
- function StateName: string; override;
end;
+ { TUpdateStateMachine }
- { This class also controls the state flow see }
- TUpdateStateMachine = class
- private
- FForce: Boolean;
- FAutomaticUpdate: Boolean;
- FParams: TUpdateStateMachineParams;
- FErrorMessage: string;
- DownloadTempPath: string;
- FShowErrors: Boolean;
- FDownload: TUpdateStateMachineDownloadParams;
-
- CurrentState: TState;
- // State object for performance (could lazy create?)
-
- FStateInstance: array[TUpdateState] of TState;
-
- function GetState: TStateClass;
- procedure SetState(const Value: TStateClass);
- procedure SetStateOnly(const enumState: TUpdateState);
- function ConvertStateToEnum(const StateClass: TStateClass): TUpdateState;
-
- procedure ShutDown;
- {
- SavePackageUpgradesToDownloadTempPath saves any new package IDs to a
- single file in the download tempPath. This procedure saves the IDs of any
- new packages to a file named "upgrade_packages.inf" in the download
- tempPath.
- }
- procedure SavePackageUpgradesToDownloadTempPath;
-
- function SetRegistryState (Update : TUpdateState): Boolean;
- function GetAutomaticUpdates: Boolean;
- function SetApplyNow(Value : Boolean): Boolean;
- function GetApplyNow: Boolean;
-
- protected
- property State: TStateClass read GetState write SetState;
-
- public
- constructor Create(AForce: Boolean);
- destructor Destroy; override;
-
- procedure HandleCheck;
- function HandleKmShell : Integer;
- procedure HandleDownload;
- procedure HandleAbort;
- procedure HandleInstallNow;
- function CurrentStateName: string;
-
- property ShowErrors: Boolean read FShowErrors write FShowErrors;
- function CheckRegistryState : TUpdateState;
-
- end;
-
- IOnlineUpdateSharedData = interface
- ['{7442A323-C1E3-404B-BEEA-5B24A52BBB0E}']
- function Params: TUpdateStateMachineParams;
- end;
-
- TOnlineUpdateSharedData = class(TInterfacedObject, IOnlineUpdateSharedData)
- private
- FParams: TUpdateStateMachineParams;
- public
- constructor Create(AParams: TUpdateStateMachineParams);
- function Params: TUpdateStateMachineParams;
-end;
-
-implementation
-
-uses
- Winapi.Shlobj,
- System.WideStrUtils,
- Vcl.Dialogs,
- Winapi.ShellApi,
- Winapi.Windows,
- Winapi.WinINet,
-
- GlobalProxySettings,
- KLog,
- keymanapi_TLB,
- KeymanVersion,
- kmint,
- ErrorControlledRegistry,
- RegistryKeys,
- Upload_Settings,
- utildir,
- utilexecute,
- OnlineUpdateCheckMessages, // TODO-WINDOWS-UPDATES: create own messages
- UfrmOnlineUpdateIcon,
- UfrmOnlineUpdateNewVersion,
- utilsystem,
- utiluac,
- versioninfo,
- Keyman.System.RemoteUpdateCheck,
- Keyman.System.DownloadUpdate;
-
-const
- SPackageUpgradeFilename = 'upgrade_packages.inf';
- kmShellContinue = 0;
- kmShellExit = 1;
-
-{ TUpdateStateMachine }
-
-constructor TUpdateStateMachine.Create(AForce : Boolean);
+constructor TUpdateStateMachine.Create(AForce: Boolean);
begin
inherited Create;
FShowErrors := True;
- FParams.Result := oucUnknown;
FForce := AForce;
FAutomaticUpdate := GetAutomaticUpdates;
@@ -329,67 +254,21 @@ destructor TUpdateStateMachine.Destroy;
if (FErrorMessage <> '') and FShowErrors then
KL.Log(FErrorMessage); // TODO: #10210 Log to Sentry
- if FParams.Result = oucShutDown then
- ShutDown;
-
for lpState := Low(TUpdateState) to High(TUpdateState) do
begin
FStateInstance[lpState].Free;
end;
// TODO: #10210 remove debugging comments
- //KL.Log('TUpdateStateMachine.Destroy: FErrorMessage = '+FErrorMessage);
- //KL.Log('TUpdateStateMachine.Destroy: FParams.Result = '+IntToStr(Ord(FParams.Result)));
+ // KL.Log('TUpdateStateMachine.Destroy: FErrorMessage = '+FErrorMessage);
+ // KL.Log('TUpdateStateMachine.Destroy: FParams.Result = '+IntToStr(Ord(FParams.Result)));
inherited Destroy;
end;
-
-procedure TUpdateStateMachine.SavePackageUpgradesToDownloadTempPath;
-var
- i: Integer;
- StringList : TStringList;
-begin
- StringList := TStringList.Create;
- try
- for i := 0 to High(FParams.Packages) do
- begin
- if FParams.Packages[i].NewID <> '' then
- begin
- StringList.Add(FParams.Packages[i].NewID+'='+FParams.Packages[i].ID);
- end;
- end;
- if StringList.Count > 0 then
- begin
- StringList.SaveToFile(DownloadTempPath + SPackageUpgradeFileName);
- end;
- finally
- StringList.Free;
- end;
-end;
-
-procedure TUpdateStateMachine.ShutDown;
-begin
- if Assigned(Application) then
- Application.Terminate;
-end;
-
-{ TOnlineUpdateSharedData }
-
-constructor TOnlineUpdateSharedData.Create(AParams: TUpdateStateMachineParams);
-begin
- inherited Create;
- FParams := AParams;
-end;
-
-function TOnlineUpdateSharedData.Params: TUpdateStateMachineParams;
-begin
- Result := FParams;
-end;
-
-function TUpdateStateMachine.SetRegistryState(Update : TUpdateState): Boolean;
+function TUpdateStateMachine.SetRegistryState(Update: TUpdateState): Boolean;
var
- UpdateStr : string;
+ UpdateStr: string;
Registry: TRegistryErrorControlled;
begin
Result := False;
@@ -423,26 +302,39 @@ function TUpdateStateMachine.SetRegistryState(Update : TUpdateState): Boolean;
end;
-function TUpdateStateMachine.CheckRegistryState: TUpdateState; // I2329
+function TUpdateStateMachine.CheckRegistryState: TUpdateState;
var
UpdateState: TUpdateState;
Registry: TRegistryErrorControlled;
-
+ StateValue: string;
+ EnumValue: Integer;
begin
- // We will use a registry flag to maintain the state of the background update
+ // Default to Idle state if any issues occur
+ UpdateState := usIdle;
+ Registry := TRegistryErrorControlled.Create;
- // check the registry value
- Registry := TRegistryErrorControlled.Create; // I2890
try
Registry.RootKey := HKEY_CURRENT_USER;
- if Registry.OpenKeyReadOnly(SRegKey_KeymanEngine_CU) and Registry.ValueExists(SRegValue_Update_State) then
- begin
- // TODO-WINDOWS-UPDATES Check if value in register is valid
- UpdateState := TUpdateState(GetEnumValue(TypeInfo(TUpdateState), Registry.ReadString(SRegValue_Update_State)));
- end
- else
+ if Registry.OpenKeyReadOnly(SRegKey_KeymanEngine_CU) and
+ Registry.ValueExists(SRegValue_Update_State) then
begin
- UpdateState := usIdle;
+ try
+ StateValue := Registry.ReadString(SRegValue_Update_State);
+ EnumValue := GetEnumValue(TypeInfo(TUpdateState), StateValue);
+
+ // Bounds Check EnumValue against TUpdateState
+ if (EnumValue >= Ord(Low(TUpdateState))) and (EnumValue <= Ord(High(TUpdateState))) then
+ UpdateState := TUpdateState(EnumValue)
+ else
+ UpdateState := usIdle; // Default if out of bounds
+ except
+ on E: Exception do
+ begin
+ // TODO: #10210 Log to Sentry
+ KL.Log('Failed to write to registry: ' + E.Message);
+ UpdateState := usIdle;
+ end;
+ end;
end;
finally
Registry.Free;
@@ -451,24 +343,33 @@ function TUpdateStateMachine.CheckRegistryState: TUpdateState; // I2329
Result := UpdateState;
end;
-function TUpdateStateMachine.GetAutomaticUpdates: Boolean; // I2329
+function TUpdateStateMachine.GetAutomaticUpdates: Boolean; // I2329
var
Registry: TRegistryErrorControlled;
begin
// check the registry value
- Registry := TRegistryErrorControlled.Create; // I2890
+ Registry := TRegistryErrorControlled.Create; // I2890
try
Registry.RootKey := HKEY_CURRENT_USER;
- Result := not Registry.OpenKeyReadOnly(SRegKey_KeymanEngine_CU) or
- not Registry.ValueExists(SRegValue_AutomaticUpdates) or
- Registry.ReadBool(SRegValue_AutomaticUpdates);
+ try
+ Result := not Registry.OpenKeyReadOnly(SRegKey_KeymanEngine_CU) or
+ not Registry.ValueExists(SRegValue_AutomaticUpdates) or
+ Registry.ReadBool(SRegValue_AutomaticUpdates);
+ except
+ on E: Exception do
+ begin
+ // TODO: #10210 Log to Sentry
+ KL.Log('Failed to read registery: ' + E.Message);
+ Result := False;
+ end;
+ end;
finally
Registry.Free;
end;
end;
-function TUpdateStateMachine.SetApplyNow(Value : Boolean): Boolean;
+function TUpdateStateMachine.SetApplyNow(Value: Boolean): Boolean;
var
Registry: TRegistryErrorControlled;
begin
@@ -504,9 +405,17 @@ function TUpdateStateMachine.GetApplyNow: Boolean;
Registry := TRegistryErrorControlled.Create;
try
Registry.RootKey := HKEY_CURRENT_USER;
- Result := Registry.OpenKeyReadOnly(SRegKey_KeymanEngine_CU) and
- Registry.ValueExists(SRegValue_ApplyNow) and
- Registry.ReadBool(SRegValue_ApplyNow);
+ try
+ Result := Registry.OpenKeyReadOnly(SRegKey_KeymanEngine_CU) and
+ Registry.ValueExists(SRegValue_ApplyNow) and
+ Registry.ReadBool(SRegValue_ApplyNow);
+ except
+ on E: Exception do
+ begin
+ KL.Log('Failed to read registry: ' + E.Message);
+ Result := False;
+ end;
+ end;
finally
Registry.Free;
end;
@@ -514,7 +423,14 @@ function TUpdateStateMachine.GetApplyNow: Boolean;
function TUpdateStateMachine.GetState: TStateClass;
begin
- Result := TStateClass(CurrentState.ClassType);
+ if Assigned(CurrentState) then
+ Result := TStateClass(CurrentState.ClassType)
+ else
+ begin
+ // TODO: #10210 Log to Sentry
+ KL.Log('Error CurrentState was uninitiallised: ' );
+ Result := nil;
+ end;
end;
procedure TUpdateStateMachine.SetState(const Value: TStateClass);
@@ -532,7 +448,7 @@ procedure TUpdateStateMachine.SetState(const Value: TStateClass);
end
else
begin
- // TODO: #10210 Error log for Unable to set state for Value
+ // TODO: #10210 Error log for Unable to set state for Value
end;
end;
@@ -542,7 +458,7 @@ procedure TUpdateStateMachine.SetStateOnly(const enumState: TUpdateState);
CurrentState := FStateInstance[enumState];
end;
-function TUpdateStateMachine.ConvertStateToEnum(const StateClass: TStateClass): TUpdateState;
+function TUpdateStateMachine.ConvertStateToEnum(const StateClass: TStateClass) : TUpdateState;
begin
if StateClass = IdleState then
Result := usIdle
@@ -559,48 +475,65 @@ function TUpdateStateMachine.ConvertStateToEnum(const StateClass: TStateClass):
else if StateClass = PostInstallState then
Result := usPostInstall
else
- KL.Log('Unknown StateClass'); // TODO-WINDOWS-UPDATES
+ begin
+ // TODO: #10210 Log to Sentry
+ Result := usIdle;
+ KL.Log('Unknown StateClass'); // TODO-WINDOWS-UPDATES
+ end;
+end;
+
+function TUpdateStateMachine.IsCurrentStateAssigned: Boolean;
+begin
+ if Assigned(CurrentState) then
+ Result := True
+ else
+ begin
+ // TODO: #10210 Log to Sentry
+ KL.Log('Unexpected Error: Current state is not assigned.');
+ Result := False;
+ end;
end;
procedure TUpdateStateMachine.HandleCheck;
begin
+ if not IsCurrentStateAssigned then
+ Exit;
CurrentState.HandleCheck;
end;
-function TUpdateStateMachine.HandleKmShell;
+function TUpdateStateMachine.HandleKmShell: Integer;
begin
+ if not IsCurrentStateAssigned then
+ Exit(kmShellContinue);
Result := CurrentState.HandleKmShell;
end;
procedure TUpdateStateMachine.HandleDownload;
begin
+ if not IsCurrentStateAssigned then
+ Exit;
CurrentState.HandleDownload;
end;
procedure TUpdateStateMachine.HandleAbort;
begin
+ if not IsCurrentStateAssigned then
+ Exit;
CurrentState.HandleAbort;
end;
procedure TUpdateStateMachine.HandleInstallNow;
begin
+ if not IsCurrentStateAssigned then
+ Exit;
CurrentState.HandleInstallNow;
end;
function TUpdateStateMachine.CurrentStateName: string;
begin
- Result := CurrentState.StateName;
-end;
-
-{ State Class Memebers }
-constructor TState.Create(Context: TUpdateStateMachine);
-begin
- bucStateContext := Context;
-end;
-
-procedure TState.ChangeState(NewState: TStateClass);
-begin
- bucStateContext.State := NewState;
+ if not IsCurrentStateAssigned then
+ Exit('Undefined');
+ Result := CurrentState.ClassName;
end;
{ IdleState }
@@ -622,11 +555,9 @@ procedure IdleState.HandleCheck;
Result : TRemoteUpdateCheckResult;
begin
- {##### For Testing only just advancing to downloading ####}
+ { ##### For Testing only just advancing to downloading #### }
ChangeState(UpdateAvailableState);
- {#### End of Testing ### };
-
-
+ { #### End of Testing ### };
{ Make a HTTP request out and see if updates are available for now do
this all in the Idle HandleCheck message. But could be broken into an
@@ -635,33 +566,33 @@ procedure IdleState.HandleCheck;
// If handle check event force check
- //CheckForUpdates := TRemoteUpdateCheck.Create(True);
- //try
- // Result:= CheckForUpdates.Run;
- // finally
- // CheckForUpdates.Free;
- // end;
+ // CheckForUpdates := TRemoteUpdateCheck.Create(True);
+ // try
+ // Result:= CheckForUpdates.Run;
+ // finally
+ // CheckForUpdates.Free;
+ // end;
{ Response OK and Update is available }
- // if Result = wucSuccess then
- // begin
- // ChangeState(UpdateAvailableState);
- // end;
+ // if Result = wucSuccess then
+ // begin
+ // ChangeState(UpdateAvailableState);
+ // end;
// else staty in idle state
end;
function IdleState.HandleKmShell;
var
- CheckForUpdates: TRemoteUpdateCheck;
- UpdateCheckResult : TRemoteUpdateCheckResult;
+ CheckForUpdates: TRemoteUpdateCheck;
+ UpdateCheckResult: TRemoteUpdateCheckResult;
begin
// Remote manages the last check time therfore
// we will allow it to return early if it hasn't reached
// the configured time between checks.
CheckForUpdates := TRemoteUpdateCheck.Create(False);
try
- UpdateCheckResult:= CheckForUpdates.Run;
+ UpdateCheckResult := CheckForUpdates.Run;
finally
CheckForUpdates.Free;
end;
@@ -675,7 +606,7 @@ function IdleState.HandleKmShell;
procedure IdleState.HandleDownload;
begin
- // Do Nothing
+ // Do Nothing
end;
procedure IdleState.HandleAbort;
@@ -686,21 +617,15 @@ procedure IdleState.HandleAbort;
procedure IdleState.HandleInstallNow;
begin
bucStateContext.CurrentState.HandleCheck;
- // TODO: How do we notify the command line no update available
-end;
-
-function IdleState.StateName;
-begin
-
- Result := 'IdleState';
+ // TODO: How do we notify the command line no update available
end;
{ UpdateAvailableState }
-
procedure UpdateAvailableState.StartDownloadProcess;
-var DownloadResult, FResult : Boolean;
-RootPath: string;
+var
+ FResult: Boolean;
+ RootPath: string;
begin
// call seperate process
RootPath := ExtractFilePath(ParamStr(0));
@@ -743,7 +668,7 @@ function UpdateAvailableState.HandleKmShell;
procedure UpdateAvailableState.HandleDownload;
begin
- ChangeState(DownloadingState);
+ ChangeState(DownloadingState);
end;
procedure UpdateAvailableState.HandleAbort;
@@ -753,8 +678,8 @@ procedure UpdateAvailableState.HandleAbort;
procedure UpdateAvailableState.HandleInstallNow;
var
- frmStartInstallNow : TfrmStartInstallNow;
- InstallNow : Boolean;
+ frmStartInstallNow: TfrmStartInstallNow;
+ InstallNow: Boolean;
begin
InstallNow := True;
@@ -762,7 +687,7 @@ procedure UpdateAvailableState.HandleInstallNow;
begin
// TODO: UI and non-UI units should be split
// if the unit launches UI then it should be a .UI. unit
- //https://github.com/keymanapp/keyman/pull/12375/files#r1751041747
+ // https://github.com/keymanapp/keyman/pull/12375/files#r1751041747
frmStartInstallNow := TfrmStartInstallNow.Create(nil);
try
if frmStartInstallNow.ShowModal = mrOk then
@@ -782,30 +707,25 @@ procedure UpdateAvailableState.HandleInstallNow;
end;
-function UpdateAvailableState.StateName;
-begin
- Result := 'UpdateAvailableState';
-end;
-
{ DownloadingState }
procedure DownloadingState.Enter;
-var DownloadResult, FResult : Boolean;
-RootPath: string;
+var
+ DownloadResult: Boolean;
begin
// Enter DownloadingState
bucStateContext.SetRegistryState(usDownloading);
- {## for testing log that we would download }
+ { ## for testing log that we would download }
KL.Log('DownloadingState.HandleKmshell test code continue');
DownloadResult := True;
- { End testing}
- //DownloadResult := DownloadUpdatesBackground;
+ { End testing }
+ DownloadResult := DownloadUpdatesBackground;
// TODO check if keyman is running then send to Waiting Restart
if DownloadResult then
begin
if HasKeymanRun then
begin
- if bucStateContext.GetApplyNow then
+ if bucStateContext.GetApplyNow then
begin
bucStateContext.SetApplyNow(False);
ChangeState(InstallingState);
@@ -843,8 +763,6 @@ function DownloadingState.HandleKmShell;
end;
procedure DownloadingState.HandleDownload;
-var DownloadResult, FResult : Boolean;
-RootPath: string;
begin
// Enter Already Downloading
end;
@@ -859,28 +777,22 @@ procedure DownloadingState.HandleInstallNow;
bucStateContext.SetApplyNow(True);
end;
-function DownloadingState.StateName;
-begin
- Result := 'DownloadingState';
-end;
-
function DownloadingState.DownloadUpdatesBackground: Boolean;
var
- DownloadBackGroundSavePath : String;
- DownloadResult : Boolean;
+ DownloadResult: Boolean;
DownloadUpdate: TDownloadUpdate;
begin
DownloadUpdate := TDownloadUpdate.Create;
try
DownloadResult := DownloadUpdate.DownloadUpdates;
Result := DownloadResult;
-// #TODO: #10210 workout when we need to refresh kmcom keyboards
-// if Result in [ wucSuccess] then
-// begin
-// kmcom.Keyboards.Refresh;
-// kmcom.Keyboards.Apply;
-// kmcom.Packages.Refresh;
-// end;
+ // #TODO: #10210 workout when we need to refresh kmcom keyboards
+ // if Result in [ wucSuccess] then
+ // begin
+ // kmcom.Keyboards.Refresh;
+ // kmcom.Keyboards.Apply;
+ // kmcom.Packages.Refresh;
+ // end;
finally
DownloadUpdate.Free;
end;
@@ -906,9 +818,9 @@ procedure WaitingRestartState.HandleCheck;
function WaitingRestartState.HandleKmShell;
var
- SavedPath : String;
- Filenames : TStringDynArray;
- frmStartInstall : TfrmStartInstall;
+ SavedPath: String;
+ Filenames: TStringDynArray;
+ frmStartInstall: TfrmStartInstall;
begin
// Still can't go if keyman has run
if HasKeymanRun then
@@ -920,15 +832,16 @@ function WaitingRestartState.HandleKmShell;
else
begin
// Check downloaded cache if available then
- SavedPath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
- GetFileNamesInDirectory(SavedPath, FileNames);
- if Length(FileNames) = 0 then
+ SavedPath := IncludeTrailingPathDelimiter
+ (TKeymanPaths.KeymanUpdateCachePath);
+ GetFileNamesInDirectory(SavedPath, Filenames);
+ if Length(Filenames) = 0 then
begin
- // Return to Idle state and check for Updates state
- ChangeState(IdleState);
- bucStateContext.CurrentState.HandleCheck; // TODO no event here
- Result := kmShellExit;
- // Exit; // again exit was not working
+ // Return to Idle state and check for Updates state
+ ChangeState(IdleState);
+ bucStateContext.CurrentState.HandleCheck; // TODO no event here
+ Result := kmShellExit;
+ // Exit; // again exit was not working
end
else
begin
@@ -962,8 +875,8 @@ procedure WaitingRestartState.HandleAbort;
procedure WaitingRestartState.HandleInstallNow;
// If user decides not to install now stay in WaitingRestart State
var
- frmStartInstallNow : TfrmStartInstallNow;
- InstallNow : Boolean;
+ frmStartInstallNow: TfrmStartInstallNow;
+ InstallNow: Boolean;
begin
InstallNow := True;
if HasKeymanRun then
@@ -985,68 +898,31 @@ procedure WaitingRestartState.HandleInstallNow;
end;
end;
-function WaitingRestartState.StateName;
-begin
-
- Result := 'WaitingRestartState';
-end;
-
-{ InstallingState }
-function InstallingState.DoInstallPackage(Package: TUpdateStateMachineParamsPackage): Boolean;
-var
- FPackage: IKeymanPackageFile2;
-begin
- Result := True;
-
- FPackage := kmcom.Packages.GetPackageFromFile(Package.SavePath) as IKeymanPackageFile2;
- FPackage.Install2(True); // Force overwrites existing package and leaves most settings for it intact
- FPackage := nil;
-
- kmcom.Refresh;
- kmcom.Apply;
- System.SysUtils.DeleteFile(Package.SavePath);
-end;
-
-procedure InstallingState.DoInstallKeyman;
-var
- s: string;
- FResult: Boolean;
-begin
- FResult := False;
- s := LowerCase(ExtractFileExt(bucStateContext.FParams.Keyman.SavePath));
- if s = '.msi' then
- FResult := TUtilExecute.Shell(0, 'msiexec.exe', '', '/qb /i "'+bucStateContext.FParams.Keyman.SavePath+'" AUTOLAUNCHPRODUCT=1') // I3349
- else if s = '.exe' then
- FResult := TUtilExecute.Shell(0, bucStateContext.FParams.Keyman.SavePath, '', '-au') // I3349
- else
- Exit;
- if not FResult then
- ShowMessage(SysErrorMessage(GetLastError));
-end;
-
-function InstallingState.DoInstallKeyman(SavePath: string) : Boolean;
+function InstallingState.DoInstallKeyman(SavePath: string): Boolean;
var
s: string;
FResult: Boolean;
begin
s := LowerCase(ExtractFileExt(SavePath));
if s = '.msi' then
- FResult := TUtilExecute.Shell(0, 'msiexec.exe', '', '/qb /i "'+SavePath+'" AUTOLAUNCHPRODUCT=1') // I3349
+ FResult := TUtilExecute.Shell(0, 'msiexec.exe', '', '/qb /i "' + SavePath +
+ '" AUTOLAUNCHPRODUCT=1') // I3349
else if s = '.exe' then
begin
// switch -au for auto update in silent mode.
// We will need to add the pop up that says install update now yes/no
- // This will run the setup executable which will ask for elevated permissions
- FResult := TUtilExecute.Shell(0, SavePath, '', '-au') // I3349
+ // This will run the setup executable which will ask for elevated permissions
+ FResult := TUtilExecute.Shell(0, SavePath, '', '-au') // I3349
end
else
FResult := False;
if not FResult then
begin
- // TODO: #10210 Log to Sentry
- KL.Log('TUpdateStateMachine.InstallingState.DoInstall: Result = '+IntToStr(Ord(FResult)));
- // Log messageShowMessage(SysErrorMessage(GetLastError));
+ // TODO: #10210 Log to Sentry
+ KL.Log('TUpdateStateMachine.InstallingState.DoInstall: Result = ' +
+ IntToStr(Ord(FResult)));
+ // Log message ShowMessage(SysErrorMessage(GetLastError));
end;
Result := FResult;
@@ -1055,34 +931,35 @@ function InstallingState.DoInstallKeyman(SavePath: string) : Boolean;
procedure InstallingState.Enter;
var
SavePath: String;
- fileExt : String;
+ fileExt: String;
fileName: String;
- fileNames: TStringDynArray;
+ Filenames: TStringDynArray;
begin
- bucStateContext.SetRegistryState(usInstalling);
- SavePath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
- GetFileNamesInDirectory(SavePath, fileNames);
- // for now we only want the exe although excute install can
- // handle msi
- for fileName in fileNames do
- begin
- fileExt := LowerCase(ExtractFileExt(fileName));
- if fileExt = '.exe' then
- break;
- end;
+ bucStateContext.SetRegistryState(usInstalling);
+ SavePath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
- if DoInstallKeyman(SavePath + ExtractFileName(fileName)) then
- begin
- KL.Log('TUpdateStateMachine.InstallingState.Enter: DoInstall OK');
- end
- else
- begin
- // TODO: #10210 clean failed download
- // TODO: #10210 Do we do a retry on install? probably not
- KL.Log('TUpdateStateMachine.InstallingState.Enter: DoInstall fail');
- ChangeState(IdleState);
- end
+ GetFileNamesInDirectory(SavePath, Filenames);
+ // for now we only want the exe although excute install can
+ // handle msi
+ for fileName in Filenames do
+ begin
+ fileExt := LowerCase(ExtractFileExt(fileName));
+ if fileExt = '.exe' then
+ break;
+ end;
+
+ if DoInstallKeyman(SavePath + ExtractFileName(fileName)) then
+ begin
+ KL.Log('TUpdateStateMachine.InstallingState.Enter: DoInstall OK');
+ end
+ else
+ begin
+ // TODO: #10210 clean failed download
+ // TODO: #10210 Do we do a retry on install? probably not
+ KL.Log('TUpdateStateMachine.InstallingState.Enter: DoInstall fail');
+ ChangeState(IdleState);
+ end
end;
procedure InstallingState.Exit;
@@ -1118,11 +995,6 @@ procedure InstallingState.HandleInstallNow;
// Do Nothing. Need the UI to let user know installation in progress OR
end;
-function InstallingState.StateName;
-begin
- Result := 'InstallingState';
-end;
-
{ RetryState }
procedure RetryState.Enter;
@@ -1162,12 +1034,6 @@ procedure RetryState.HandleInstallNow;
ChangeState(InstallingState);
end;
-function RetryState.StateName;
-begin
-
- Result := 'RetryState';
-end;
-
{ PostInstallState }
procedure PostInstallState.Enter;
@@ -1198,18 +1064,19 @@ procedure PostInstallState.HandleDownload;
end;
procedure PostInstallState.HandleMSIInstallComplete;
-var SavePath: string;
- FileName: String;
- FileNames: TStringDynArray;
+var
+ SavePath: string;
+ fileName: String;
+ Filenames: TStringDynArray;
begin
- SavePath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
+ SavePath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
- GetFileNamesInDirectory(SavePath, FileNames);
- for FileName in FileNames do
- begin
- System.SysUtils.DeleteFile(FileName);
- end;
- ChangeState(IdleState);
+ GetFileNamesInDirectory(SavePath, Filenames);
+ for fileName in Filenames do
+ begin
+ System.SysUtils.DeleteFile(fileName);
+ end;
+ ChangeState(IdleState);
end;
procedure PostInstallState.HandleAbort;
@@ -1222,10 +1089,4 @@ procedure PostInstallState.HandleInstallNow;
// Do nothing as files will be cleaned via HandleKmShell
end;
-function PostInstallState.StateName;
-begin
-
- Result := 'PostInstallState';
-end;
-
end.
From 7be490a67b042afa459c703976894de46e6f0a47 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Tue, 5 Nov 2024 11:33:18 +1000
Subject: [PATCH 40/43] feat(windows): review comment suggestions
Co-authored-by: Marc Durdin
---
.../kmshell/main/Keyman.System.DownloadUpdate.pas | 13 +++----------
.../Keyman.System.Install.EnginePostInstall.pas | 2 +-
2 files changed, 4 insertions(+), 11 deletions(-)
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
index 9a3a27995b6..373ba513e5f 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
@@ -12,9 +12,6 @@ interface
KeymanPaths,
OnlineUpdateCheck;
-const
- DaysBetweenCheckingForUpdates: Integer = 7; // Days between checking for updates
-
type
TDownloadUpdateParams = record
TotalSize: Integer;
@@ -170,17 +167,13 @@ procedure TDownloadUpdate.DoDownloadUpdates(SavePath: string; Params: TUpdateChe
begin
// TODO-WINDOWS-UPDATES: #10210 convert to error log.
LogMessage('DoDownloadUpdates Failed to download' + Params.InstallURL);
- InstallerDownloaded := False;
end
else
begin
- InstallerDownloaded := True;
- end;
-
- // If installer has downloaded that is success even
- // if zero packages where downloaded.
- if InstallerDownloaded then
+ // If installer has downloaded that is success even
+ // if zero packages were downloaded.
Result := True;
+ end;
end;
function TDownloadUpdate.DownloadUpdates: Boolean;
diff --git a/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas b/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas
index 4871d939764..56d33c83392 100644
--- a/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas
+++ b/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas
@@ -40,7 +40,7 @@ function UpdateState: Boolean;
if RegCreateKeyEx(HKEY_CURRENT_USER, PChar(SRegKey_KeymanEngine_CU), 0, NULL, KEY_ALL_ACCESS, NULL, &hk, NULL) = ERROR_SUCCESS then
begin
try
- if RegSetValueEx(hk, PChar(SRegValue_Update_State), 0, REG_SZ, PWideChar(UpdateStr), Length(UpdateStr) * SizeOf(Char)) = ERROR_SUCCESS then
+ if RegSetValueEx(hk, PChar(SRegValue_Update_State), 0, REG_SZ, PChar(UpdateStr), (Length(UpdateStr)+1) * SizeOf(Char)) = ERROR_SUCCESS then
begin
Result := True;
end
From e239ec710a5e13cdabd7865f14bc05ddbf9c78ad Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Tue, 5 Nov 2024 17:20:12 +1000
Subject: [PATCH 41/43] feat(windows): Review comments
---
.../main/Keyman.System.DownloadUpdate.pas | 24 +---
.../main/Keyman.System.RemoteUpdateCheck.pas | 134 ++++++++++--------
.../main/Keyman.System.UpdateStateMachine.pas | 53 +++----
windows/src/desktop/kmshell/main/initprog.pas | 3 +-
4 files changed, 105 insertions(+), 109 deletions(-)
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
index 373ba513e5f..f91c8f6ea4d 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.DownloadUpdate.pas
@@ -25,26 +25,19 @@ TDownloadUpdate = class
FDownload: TDownloadUpdateParams;
(**
*
- * Performs updates download in the background, without displaying a GUI
- * progress bar.
+ * Performs updates download in the background.
* @params SavePath The path where the downloaded files will be saved.
- * Result A Boolean value indicating the overall result of the
+ *
+ *@returns A Boolean value indicating the overall result of the
* download process.
*)
- procedure DoDownloadUpdates(SavePath: string; Params: TUpdateCheckResponse; var Result: Boolean);
+ function DoDownloadUpdates(SavePath: string; Params: TUpdateCheckResponse): Boolean;
public
constructor Create;
destructor Destroy; override;
- (**
- * Performs updates download in the background, without displaying a GUI
- * progress bar. This function is similar to DownloadUpdates, but it runs in
- * the background.
- * @returns True if all updates were successfully downloaded, False if any
- * download failed.
- *)
function DownloadUpdates : Boolean;
// TODO-WINDOWS-UPDATES: verify filesizes match the ucr metadata so we know we don't have partial downloades.
@@ -91,12 +84,11 @@ destructor TDownloadUpdate.Destroy;
inherited Destroy;
end;
-procedure TDownloadUpdate.DoDownloadUpdates(SavePath: string; Params: TUpdateCheckResponse; var Result: Boolean);
+function TDownloadUpdate.DoDownloadUpdates(SavePath: string; Params: TUpdateCheckResponse): Boolean;
var
i : Integer;
http: THttpUploader;
fs: TFileStream;
- InstallerDownloaded: Boolean;
function DownloadFile(const url, savepath: string): Boolean;
begin
@@ -179,15 +171,13 @@ procedure TDownloadUpdate.DoDownloadUpdates(SavePath: string; Params: TUpdateChe
function TDownloadUpdate.DownloadUpdates: Boolean;
var
DownloadBackGroundSavePath : String;
- DownloadResult : Boolean;
ucr: TUpdateCheckResponse;
begin
DownloadBackGroundSavePath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath);
if TUpdateCheckStorage.LoadUpdateCacheData(ucr) then
begin
- DoDownloadUpdates(DownloadBackGroundSavePath, ucr, DownloadResult);
- KL.Log('DownloadUpdates.DownloadUpdatesBackground: DownloadResult = '+IntToStr(Ord(DownloadResult)));
- Result := DownloadResult;
+ Result := DoDownloadUpdates(DownloadBackGroundSavePath, ucr);
+ KL.Log('DownloadUpdates.DownloadUpdates: DownloadResult = '+IntToStr(Ord(Result)));
end
else
Result := False;
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas b/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas
index ba3e8b62eb4..9e4b0b007e2 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas
@@ -1,12 +1,13 @@
-{
+(**
* Keyman is copyright (C) SIL International. MIT License.
*
* Keyman.System.RemoteUpdateCheck: Checks for keyboard package and Keyman
- for Windows updates.
-}
-unit Keyman.System.RemoteUpdateCheck; // I3306
+ * for Windows updates.
+*)
+unit Keyman.System.RemoteUpdateCheck; // I3306
interface
+
uses
System.Classes,
System.SysUtils,
@@ -35,35 +36,41 @@ TRemoteUpdateCheck = class
FRemoteResult: TRemoteUpdateCheckResult;
FErrorMessage: string;
FShowErrors: Boolean;
- {
- Performs an online update check, including package retrieval and version
- query.
-
- This function checks if a week has passed since the last update check. It
- utilizes the kmcom API to retrieve the current packages. The function then
- performs an HTTP request to query the remote versions of these packages.
- The resulting information is stored in the FParams variable. Additionally,
- the function handles the main Keyman install package.
-
- @returns A TBackgroundUpdateResult indicating the result of the update
- check.
- }
+ (**
+ * Performs an online query of both the main keyman package and
+ * the keyboard packages. It utilizes the kmcom API to retrieve the current
+ * packages. The function then performs an HTTP request to query the remote
+ * versions of these packages.
+ * The resulting information is stored in the TUpdateCheckResponse
+ * variable and seralized to disk.
+ *
+ * @returns A TBackgroundUpdateResult indicating the result of the update
+ * check.
+ *)
function DoRun: TRemoteUpdateCheckResult;
public
- constructor Create(AForce : Boolean);
+ constructor Create(AForce: Boolean);
destructor Destroy; override;
function Run: TRemoteUpdateCheckResult;
property ShowErrors: Boolean read FShowErrors write FShowErrors;
end;
procedure LogMessage(LogMessage: string);
+
+(**
+ * This function checks if a week or CheckPeriod time has passed since the last
+ * update check.
+ *
+ * @returns True if it has been longer then CheckPeriod time since last update
+*)
function ConfigCheckContinue: Boolean;
implementation
uses
System.WideStrUtils,
+ System.Win.Registry,
Winapi.Windows,
Winapi.WinINet,
@@ -98,8 +105,9 @@ destructor TRemoteUpdateCheck.Destroy;
if (FErrorMessage <> '') and FShowErrors then
LogMessage(FErrorMessage);
- KL.Log('TRemoteUpdateCheck.Destroy: FErrorMessage = '+FErrorMessage);
- KL.Log('TRemoteUpdateCheck.Destroy: FRemoteResult = '+IntToStr(Ord(FRemoteResult)));
+ KL.Log('TRemoteUpdateCheck.Destroy: FErrorMessage = ' + FErrorMessage);
+ KL.Log('TRemoteUpdateCheck.Destroy: FRemoteResult = ' +
+ IntToStr(Ord(FRemoteResult)));
inherited Destroy;
end;
@@ -116,12 +124,12 @@ function TRemoteUpdateCheck.DoRun: TRemoteUpdateCheckResult;
i: Integer;
ucr: TUpdateCheckResponse;
pkg: IKeymanPackage;
- registry: TRegistryErrorControlled;
+ Registry: TRegistryErrorControlled;
http: THttpUploader;
- proceed : boolean;
+ proceed: Boolean;
begin
- {FProxyHost := '';
- FProxyPort := 0;}
+ { FProxyHost := '';
+ FProxyPort := 0; }
{ Check if user is currently online }
if not InternetGetConnectedState(@flags, 0) then
@@ -132,20 +140,20 @@ function TRemoteUpdateCheck.DoRun: TRemoteUpdateCheckResult;
proceed := ConfigCheckContinue;
if not proceed and not FForce then
- begin
- Result := wucNoUpdates;
- Exit;
- end;
-
+ begin
+ Result := wucNoUpdates;
+ Exit;
+ end;
try
- http := THTTPUploader.Create(nil);
+ http := THttpUploader.Create(nil);
try
http.Fields.Add('version', ansistring(CKeymanVersionInfo.Version));
http.Fields.Add('tier', ansistring(CKeymanVersionInfo.Tier));
- if FForce
- then http.Fields.Add('manual', '1')
- else http.Fields.Add('manual', '0');
+ if FForce then
+ http.Fields.Add('manual', '1')
+ else
+ http.Fields.Add('manual', '0');
for i := 0 to kmcom.Packages.Count - 1 do
begin
@@ -154,8 +162,10 @@ function TRemoteUpdateCheck.DoRun: TRemoteUpdateCheckResult;
// Due to limitations in PHP parsing of query string parameters names with
// space or period, we need to split the parameters up. The legacy pattern
// is still supported on the server side. Relates to #4886.
- http.Fields.Add(AnsiString('packageid_'+IntToStr(i)), AnsiString(pkg.ID));
- http.Fields.Add(AnsiString('packageversion_'+IntToStr(i)), AnsiString(pkg.Version));
+ http.Fields.Add(ansistring('packageid_' + IntToStr(i)),
+ ansistring(pkg.ID));
+ http.Fields.Add(ansistring('packageversion_' + IntToStr(i)),
+ ansistring(pkg.Version));
pkg := nil;
end;
@@ -167,7 +177,7 @@ function TRemoteUpdateCheck.DoRun: TRemoteUpdateCheckResult;
http.Request.HostName := API_Server;
http.Request.Protocol := API_Protocol;
http.Request.UrlPath := API_Path_UpdateCheck_Windows;
- //OnStatus :=
+ // OnStatus :=
http.Upload;
if http.Response.StatusCode = 200 then
begin
@@ -188,62 +198,66 @@ function TRemoteUpdateCheck.DoRun: TRemoteUpdateCheckResult;
http.Free;
end;
except
- on E:EHTTPUploader do
+ on E: EHTTPUploader do
begin
- if (E.ErrorCode = 12007) or (E.ErrorCode = 12029)
- then FErrorMessage := S_OnlineUpdate_UnableToContact
- else FErrorMessage := WideFormat(S_OnlineUpdate_UnableToContact_Error, [E.Message]);
+ if (E.ErrorCode = 12007) or (E.ErrorCode = 12029) then
+ FErrorMessage := S_OnlineUpdate_UnableToContact
+ else
+ FErrorMessage := WideFormat(S_OnlineUpdate_UnableToContact_Error,
+ [E.Message]);
Result := wucFailure;
end;
- on E:Exception do
+ on E: Exception do
begin
FErrorMessage := E.Message;
Result := wucFailure;
end;
end;
- registry := TRegistryErrorControlled.Create; // I2890
+ Registry := TRegistryErrorControlled.Create; // I2890
try
- if registry.OpenKey(SRegKey_KeymanDesktop_CU, True) then
- registry.WriteDateTime(SRegValue_LastUpdateCheckTime, Now);
+ if Registry.OpenKey(SRegKey_KeymanDesktop_CU, True) then
+ Registry.WriteDateTime(SRegValue_LastUpdateCheckTime, Now);
finally
- registry.Free;
+ Registry.Free;
end;
end;
- // temp wrapper for converting showmessage to logs don't know where
- // if nt using klog
- procedure LogMessage(LogMessage: string);
- begin
- KL.Log(LogMessage);
- end;
+// temp wrapper for converting showmessage to logs don't know where
+// if nt using klog
+procedure LogMessage(LogMessage: string);
+begin
+ KL.Log(LogMessage);
+end;
function ConfigCheckContinue: Boolean;
var
- registry: TRegistryErrorControlled;
+ Registry: TRegistryErrorControlled;
begin
-{ Verify that it has been at least CheckPeriod days since last update check }
+ { Verify that it has been at least CheckPeriod days since last update check }
Result := False;
try
- registry := TRegistryErrorControlled.Create; // I2890
+ Registry := TRegistryErrorControlled.Create; // I2890
try
- if registry.OpenKeyReadOnly(SRegKey_KeymanDesktop_CU) then
+ if Registry.OpenKeyReadOnly(SRegKey_KeymanDesktop_CU) then
begin
- if registry.ValueExists(SRegValue_CheckForUpdates) and not registry.ReadBool(SRegValue_CheckForUpdates) then
+ if Registry.ValueExists(SRegValue_CheckForUpdates) and
+ not Registry.ReadBool(SRegValue_CheckForUpdates) then
begin
Exit;
end;
- if registry.ValueExists(SRegValue_LastUpdateCheckTime) and (Now - registry.ReadDateTime(SRegValue_LastUpdateCheckTime) > CheckPeriod) then
+ if Registry.ValueExists(SRegValue_LastUpdateCheckTime) and
+ (Now - Registry.ReadDateTime(SRegValue_LastUpdateCheckTime) >
+ CheckPeriod) then
begin
Result := True;
end;
end;
finally
- registry.Free;
+ Registry.Free;
end;
except
- { we will not run the check if an error occurs reading the settings }
- on E:Exception do
+ on E: ERegistryException do
begin
Result := False;
LogMessage(E.Message);
diff --git a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
index eaf6b50bd83..a3c262d2bc1 100644
--- a/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
+++ b/windows/src/desktop/kmshell/main/Keyman.System.UpdateStateMachine.pas
@@ -99,6 +99,7 @@ implementation
uses
+ System.Win.Registry,
Winapi.Windows,
Winapi.WinINet,
ErrorControlledRegistry,
@@ -119,6 +120,7 @@ implementation
constructor TState.Create(Context: TUpdateStateMachine);
begin
+ inherited Create;
bucStateContext := Context;
end;
@@ -256,7 +258,7 @@ destructor TUpdateStateMachine.Destroy;
for lpState := Low(TUpdateState) to High(TUpdateState) do
begin
- FStateInstance[lpState].Free;
+ FreeAndNil(FStateInstance[lpState]);
end;
// TODO: #10210 remove debugging comments
@@ -289,7 +291,7 @@ function TUpdateStateMachine.SetRegistryState(Update: TUpdateState): Boolean;
Registry.WriteString(SRegValue_Update_State, UpdateStr);
Result := True;
except
- on E: Exception do
+ on E: ERegistryException do
begin
// TODO: #10210 Log to Sentry
KL.Log('Failed to write to registry: ' + E.Message);
@@ -328,7 +330,7 @@ function TUpdateStateMachine.CheckRegistryState: TUpdateState;
else
UpdateState := usIdle; // Default if out of bounds
except
- on E: Exception do
+ on E: ERegistryException do
begin
// TODO: #10210 Log to Sentry
KL.Log('Failed to write to registry: ' + E.Message);
@@ -357,7 +359,7 @@ function TUpdateStateMachine.GetAutomaticUpdates: Boolean; // I2329
not Registry.ValueExists(SRegValue_AutomaticUpdates) or
Registry.ReadBool(SRegValue_AutomaticUpdates);
except
- on E: Exception do
+ on E: ERegistryException do
begin
// TODO: #10210 Log to Sentry
KL.Log('Failed to read registery: ' + E.Message);
@@ -386,7 +388,7 @@ function TUpdateStateMachine.SetApplyNow(Value: Boolean): Boolean;
Registry.WriteBool(SRegValue_ApplyNow, Value);
Result := True;
except
- on E: Exception do
+ on E: ERegistryException do
begin
// TODO: #10210 Log to Sentry 'Failed to write '+SRegValue_ApplyNow+' to registry: ' + E.Message
KL.Log('Failed to write to registry: ' + E.Message);
@@ -410,7 +412,7 @@ function TUpdateStateMachine.GetApplyNow: Boolean;
Registry.ValueExists(SRegValue_ApplyNow) and
Registry.ReadBool(SRegValue_ApplyNow);
except
- on E: Exception do
+ on E: ERegistryException do
begin
KL.Log('Failed to read registry: ' + E.Message);
Result := False;
@@ -556,29 +558,28 @@ procedure IdleState.HandleCheck;
begin
{ ##### For Testing only just advancing to downloading #### }
- ChangeState(UpdateAvailableState);
+ //ChangeState(UpdateAvailableState);
+ // will keep here as there are more PR's #12621
{ #### End of Testing ### };
- { Make a HTTP request out and see if updates are available for now do
- this all in the Idle HandleCheck message. But could be broken into an
- seperate state of WaitngCheck RESP }
+ { // // TODO-WINDOWS-UPDATES Check how long a check takes then determine
+ if it needs to be broken into a seperate state of WaitngCheck RESP }
{ if Response not OK stay in the idle state and return }
- // If handle check event force check
- // CheckForUpdates := TRemoteUpdateCheck.Create(True);
- // try
- // Result:= CheckForUpdates.Run;
- // finally
- // CheckForUpdates.Free;
- // end;
+ // Handle_check event force check
+ CheckForUpdates := TRemoteUpdateCheck.Create(True);
+ try
+ Result:= CheckForUpdates.Run;
+ finally
+ CheckForUpdates.Free;
+ end;
{ Response OK and Update is available }
- // if Result = wucSuccess then
- // begin
- // ChangeState(UpdateAvailableState);
- // end;
-
+ if Result = wucSuccess then
+ begin
+ ChangeState(UpdateAvailableState);
+ end;
// else staty in idle state
end;
@@ -786,13 +787,6 @@ function DownloadingState.DownloadUpdatesBackground: Boolean;
try
DownloadResult := DownloadUpdate.DownloadUpdates;
Result := DownloadResult;
- // #TODO: #10210 workout when we need to refresh kmcom keyboards
- // if Result in [ wucSuccess] then
- // begin
- // kmcom.Keyboards.Refresh;
- // kmcom.Keyboards.Apply;
- // kmcom.Packages.Refresh;
- // end;
finally
DownloadUpdate.Free;
end;
@@ -845,7 +839,6 @@ function WaitingRestartState.HandleKmShell;
end
else
begin
- // TODO Pop up toast here to ask user if we want to continue
frmStartInstall := TfrmStartInstall.Create(nil);
try
if frmStartInstall.ShowModal = mrOk then
diff --git a/windows/src/desktop/kmshell/main/initprog.pas b/windows/src/desktop/kmshell/main/initprog.pas
index d8c19b63099..065d26802a0 100644
--- a/windows/src/desktop/kmshell/main/initprog.pas
+++ b/windows/src/desktop/kmshell/main/initprog.pas
@@ -438,8 +438,7 @@ procedure RunKMCOM(FMode: TKMShellMode; KeyboardFileNames: TStrings; FSilent, FF
ShowMessage(MsgFromId(SKOSNotSupported));
Exit;
end;
- // TODO: #10038 Will add this as part of the background update state machine
- // for now just verifing the download happens via -buc switch.
+
BUpdateSM := TUpdateStateMachine.Create(False);
try
if (FMode = fmBackgroundUpdateCheck) then
From 883092e2178e4eccd084a973c98c07e50d2f8d55 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Wed, 6 Nov 2024 15:53:45 +1000
Subject: [PATCH 42/43] feat(windows): fix installHelper
---
.../insthelper/Keyman.System.Install.EnginePostInstall.pas | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas b/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas
index 56d33c83392..6c756bbbac2 100644
--- a/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas
+++ b/windows/src/engine/insthelper/Keyman.System.Install.EnginePostInstall.pas
@@ -29,15 +29,13 @@ function ReportFailure(hInstall: MSIHANDLE; const func: string; code: UINT): UIN
function UpdateState: Boolean;
var
UpdateStr : UnicodeString;
- UpdatePBytes : PByte;
hk: Winapi.Windows.HKEY;
- updateData: Cardinal;
begin
Result := False;
UpdateStr := 'usPostInstall';
- if RegCreateKeyEx(HKEY_CURRENT_USER, PChar(SRegKey_KeymanEngine_CU), 0, NULL, KEY_ALL_ACCESS, NULL, &hk, NULL) = ERROR_SUCCESS then
+ if RegCreateKeyEx(HKEY_CURRENT_USER, PChar(SRegKey_KeymanEngine_CU), 0, nil, 0, KEY_ALL_ACCESS, nil, &hk, nil) = ERROR_SUCCESS then
begin
try
if RegSetValueEx(hk, PChar(SRegValue_Update_State), 0, REG_SZ, PChar(UpdateStr), (Length(UpdateStr)+1) * SizeOf(Char)) = ERROR_SUCCESS then
@@ -54,7 +52,7 @@ function UpdateState: Boolean;
end
else
begin
- // TODO-WINDOWS-UPDATES: error log creating key
+ //TODO-WINDOWS-UPDATES: error log creating key
end;
end;
From ab3227fb00ff44bf317588623d3c930ae0c4bbce Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Thu, 7 Nov 2024 15:29:14 +1000
Subject: [PATCH 43/43] feat(windows): review comments
---
windows/src/desktop/kmshell/main/UfrmMain.pas | 4 ++--
windows/src/desktop/kmshell/main/initprog.pas | 2 +-
windows/src/engine/keyman/main.pas | 6 +++---
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/windows/src/desktop/kmshell/main/UfrmMain.pas b/windows/src/desktop/kmshell/main/UfrmMain.pas
index 0403df92751..cb206e0291b 100644
--- a/windows/src/desktop/kmshell/main/UfrmMain.pas
+++ b/windows/src/desktop/kmshell/main/UfrmMain.pas
@@ -797,7 +797,7 @@ procedure TfrmMain.Support_ProxyConfig;
Free;
end;
end;
-// TODO: #10210 Remove Update
+// TODO-WINDOWS-UPDATES: #10210 Remove Update
procedure TfrmMain.Support_UpdateCheck;
begin
with TOnlineUpdateCheck.Create(Self, True, False) do
@@ -838,7 +838,7 @@ procedure TfrmMain.Update_CheckNow;
procedure TfrmMain.Update_ApplyNow;
var
- ShellPath, s: WideString;
+ ShellPath, s: string;
FResult: Boolean;
begin
ShellPath := TKeymanPaths.KeymanDesktopInstallPath(TKeymanPaths.S_KMShell);
diff --git a/windows/src/desktop/kmshell/main/initprog.pas b/windows/src/desktop/kmshell/main/initprog.pas
index 065d26802a0..b5a84792ce6 100644
--- a/windows/src/desktop/kmshell/main/initprog.pas
+++ b/windows/src/desktop/kmshell/main/initprog.pas
@@ -253,7 +253,7 @@ function Init(var FMode: TKMShellMode; KeyboardFileNames: TStrings; var FSilent,
else if s = '-?' then FMode := fmHelpKMShell
else if s = '-h' then FMode := fmHelp
else if s = '-t' then FMode := fmTextEditor
- //TODO: will remove -ouc not used
+ //TODO-WINDOWS-UPDATES: will remove -ouc not used
// -buc uses the Statemachine can be used for external scripts to force a check
else if s = '-ouc' then FMode := fmOnlineUpdateCheck
else if s = '-buc' then FMode := fmBackgroundUpdateCheck
diff --git a/windows/src/engine/keyman/main.pas b/windows/src/engine/keyman/main.pas
index 1b6ed25b302..79db9c8baa2 100644
--- a/windows/src/engine/keyman/main.pas
+++ b/windows/src/engine/keyman/main.pas
@@ -40,14 +40,14 @@ implementation
System.Win.Registry,
GetOsVersion,
+ Keyman.System.ExecutionHistory,
Keyman.System.Security,
Keyman.Winapi.VersionHelpers,
KeymanVersion,
+ Klog,
RegistryKeys,
UfrmKeyman7Main,
- UserMessages,
- Klog,
- Keyman.System.ExecutionHistory;
+ UserMessages;
function ValidateParameters(var FCommand: Integer): Boolean; forward;
function PassParametersToRunningInstance(FCommand: Integer): Boolean; forward;