diff --git a/mac/Keyman4MacIM/Keyman4MacIM.xcodeproj/project.pbxproj b/mac/Keyman4MacIM/Keyman4MacIM.xcodeproj/project.pbxproj index 9d63aac468..8f49e2e0f2 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM.xcodeproj/project.pbxproj +++ b/mac/Keyman4MacIM/Keyman4MacIM.xcodeproj/project.pbxproj @@ -31,6 +31,8 @@ 29B42A602728343B00EDD5D3 /* KMKeyboardHelpWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 29B42A622728343B00EDD5D3 /* KMKeyboardHelpWindowController.xib */; }; 29B4A0D52BF7675A00682049 /* KMLogs.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B4A0D32BF7675A00682049 /* KMLogs.m */; }; 29B6FB732BC39DD60074BF7F /* TextApiComplianceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B6FB722BC39DD60074BF7F /* TextApiComplianceTests.m */; }; + 29C1CDE22C5B2F8B003C23BB /* KMSettingsRepository.m in Sources */ = {isa = PBXBuildFile; fileRef = D861B03E2C5747F70003675E /* KMSettingsRepository.m */; }; + 29C1CDE32C5B2F8B003C23BB /* KMDataRepository.m in Sources */ = {isa = PBXBuildFile; fileRef = 29015ABC2C58D86F00CCBB94 /* KMDataRepository.m */; }; 37A245C12565DFA6000BBF92 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 37A245C02565DFA6000BBF92 /* Assets.xcassets */; }; 37AE5C9D239A7B770086CC7C /* qrcode.min.js in Resources */ = {isa = PBXBuildFile; fileRef = 37AE5C9C239A7B770086CC7C /* qrcode.min.js */; }; 37C2B0CB25FF2C350092E16A /* Help in Resources */ = {isa = PBXBuildFile; fileRef = 37C2B0CA25FF2C340092E16A /* Help */; }; @@ -1017,6 +1019,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 29C1CDE22C5B2F8B003C23BB /* KMSettingsRepository.m in Sources */, + 29C1CDE32C5B2F8B003C23BB /* KMDataRepository.m in Sources */, 2915DC512BFE35DB0051FC52 /* KMLogs.m in Sources */, 2992F4202A28482800E08929 /* PrivacyWindowController.m in Sources */, 2992F41F2A2847C900E08929 /* TextApiCompliance.m in Sources */, diff --git a/mac/Keyman4MacIM/Keyman4MacIM/KMDataRepository.h b/mac/Keyman4MacIM/Keyman4MacIM/KMDataRepository.h index 836b8b742b..a5af2ccb88 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/KMDataRepository.h +++ b/mac/Keyman4MacIM/Keyman4MacIM/KMDataRepository.h @@ -17,7 +17,8 @@ NS_ASSUME_NONNULL_BEGIN @property (readonly) NSURL *keymanKeyboardsDirectory; // keymanKeyboardsDirectory = '~/Library/Application Support/com.keyman.app/Keyman-Keyboards' + (KMDataRepository *)shared; -- (void)createDataDirectoriesIfNecessary; +- (void)createDataDirectoryIfNecessary; +- (void)createKeyboardsDirectoryIfNecessary; - (BOOL)migrateData; @end diff --git a/mac/Keyman4MacIM/Keyman4MacIM/KMDataRepository.m b/mac/Keyman4MacIM/Keyman4MacIM/KMDataRepository.m index 2143d8b7f5..3d0d0bacd7 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/KMDataRepository.m +++ b/mac/Keyman4MacIM/Keyman4MacIM/KMDataRepository.m @@ -74,7 +74,7 @@ NSString *const kKeymanSubdirectoryName = @"keyman.inputmethod.Keyman"; if (directoryError) { os_log_error([KMLogs startupLog], "error getting Application Support subdirectory: '%{public}@'", directoryError.localizedDescription); } else { - os_log_info([KMLogs startupLog], "Application Support subdirectory: '%{public}@'", applicationSupportUrl); + os_log_info([KMLogs startupLog], "Application Support subdirectory: '%{public}@'", applicationSupportUrl.path); _applicationSupportSubDirectory = applicationSupportUrl; } } @@ -98,18 +98,39 @@ NSString *const kKeymanSubdirectoryName = @"keyman.inputmethod.Keyman"; } /** - * creates Keyman data directories if they do not exist yet - * This includes 1) the main data subdirectory: keyman.inputmethod.Keyman - * and 2) its subdirectory, Keyman-Keyboards - * + * Creates Keyman data directory if it do not exist yet. This is the main data subdirectory: keyman.inputmethod.Keyman */ -- (void)createDataDirectoriesIfNecessary { +- (void)createDataDirectoryIfNecessary { + NSFileManager *fileManager = [NSFileManager defaultManager]; + BOOL isDir; + BOOL exists = [fileManager fileExistsAtPath:self.keymanDataDirectory.path isDirectory:&isDir]; + + if (!exists) { + NSError *createError = nil; + os_log_info([KMLogs startupLog], "createDataDirectoryIfNecessary, about to attempt createDirectoryAtPath for: '%{public}@'", self.keymanDataDirectory.path); + [fileManager createDirectoryAtPath:self.keymanDataDirectory.path withIntermediateDirectories:YES attributes:nil error:nil]; + if (createError) { + os_log_error([KMLogs startupLog], "error creating Keyman data directory: '%{public}@'", createError.localizedDescription); + } else { + os_log_info([KMLogs startupLog], "created Keyman data directory: '%{public}@'", self.keymanDataDirectory.path); + } + } else { + os_log_info([KMLogs startupLog], "Keyman data directory already exists: '%{public}@'", self.keymanDataDirectory.path); + } +} + +/** + * Creates Keyman keyboard directory if it does not exist yet. This is the 'Keyman-Keyboards' directory. + * It should not be created until after migrating because its existence would block migrating data from the old location. + */ +- (void)createKeyboardsDirectoryIfNecessary { NSFileManager *fileManager = [NSFileManager defaultManager]; BOOL isDir; BOOL exists = [fileManager fileExistsAtPath:self.keymanKeyboardsDirectory.path isDirectory:&isDir]; if (!exists) { NSError *createError = nil; + os_log_info([KMLogs startupLog], "createKeyboardsDirectoryIfNecessary, about to attempt createDirectoryAtPath for: '%{public}@'", self.keymanKeyboardsDirectory.path); [fileManager createDirectoryAtPath:self.keymanKeyboardsDirectory.path withIntermediateDirectories:YES attributes:nil error:nil]; if (createError) { os_log_error([KMLogs startupLog], "error creating Keyman-Keyboards directory: '%{public}@'", createError.localizedDescription); @@ -138,24 +159,30 @@ NSString *const kKeymanSubdirectoryName = @"keyman.inputmethod.Keyman"; - (BOOL)migrateData { BOOL didMoveData = NO; + NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *obsoleteKeymanKeyboardsDirectory = self.obsoleteKeymanKeyboardsDirectory.path; NSString *dataDirectory = self.keymanDataDirectory.path; os_log_info([KMLogs startupLog], "migrateData, move obsoleteKeymanKeyboardsDirectory: '%{public}@' to '%{public}@'", obsoleteKeymanKeyboardsDirectory, dataDirectory); - // delete, happens whether migration or not - //[self createKeyboardsDirectoriesIfNecessary]; - + BOOL isDir; + BOOL dataDirectoryExistsInNewLocation = ([fileManager fileExistsAtPath:self.keymanDataDirectory.path isDirectory:&isDir]); + os_log([KMLogs startupLog], "data directory exists in new location, %{public}@: %{public}@", self.keymanDataDirectory.path, dataDirectoryExistsInNewLocation?@"YES":@"NO"); + + BOOL keyboardsDirectoryExistsInNewLocation = ([fileManager fileExistsAtPath:self.keymanKeyboardsDirectory.path isDirectory:&isDir]); + os_log([KMLogs startupLog], "keyboards directory exists in new location, %{public}@: %{public}@", self.keymanKeyboardsDirectory.path, keyboardsDirectoryExistsInNewLocation?@"YES":@"NO"); + BOOL dataExistsInOldLocation = [self keyboardsExistInDocumentsFolder]; os_log([KMLogs startupLog], "obsolete keyman keyboards directory exists: %@", dataExistsInOldLocation?@"YES":@"NO"); if (dataExistsInOldLocation) { NSError *moveError = nil; - NSFileManager *fileManager = [NSFileManager defaultManager]; didMoveData = [fileManager moveItemAtURL:self.obsoleteKeymanKeyboardsDirectory - toURL:self.keymanDataDirectory + toURL:self.keymanKeyboardsDirectory error:&moveError]; if (moveError) { os_log_error([KMLogs startupLog], "error migrating data: '%{public}@'", moveError.localizedDescription); + } else { + os_log_error([KMLogs startupLog], "data migrated successfully to: '%{public}@'", self.keymanKeyboardsDirectory.path); } } diff --git a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m index ecd3e659e8..8352bf2a59 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m +++ b/mac/Keyman4MacIM/Keyman4MacIM/KMInputMethodAppDelegate.m @@ -182,7 +182,7 @@ NSString* _keymanDataPath = nil; }]; // [SentrySDK captureMessage:@"Starting Keyman [test message]"]; - + [self setDefaultKeymanMenuItems]; [self updateKeyboardMenuItems]; } @@ -532,7 +532,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef * Returns the root folder where keyboards are stored; currently the same * as the keymanDataPath, but may diverge in future versions (possibly a sub-folder) * - * Actually divering now, get this from KMDataRepository + * Actually diverting now, get this from KMDataRepository */ - (NSString *)keyboardsPath { if (_keyboardsPath == nil) { @@ -774,11 +774,11 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef } /** - * Prepare the app for all the things that need to be persisted: - * namely, the settings in UserDefaults and keyboard data on disk + * Prepare the app environment for all the things that need to be persisted: + * namely, the keyboard data on disk and the settings in UserDefaults */ - (void)preparePersistence { - [KMDataRepository.shared createDataDirectoriesIfNecessary]; + [KMDataRepository.shared createDataDirectoryIfNecessary]; if ([KMSettingsRepository.shared dataMigrationNeeded]) { BOOL movedData = [KMDataRepository.shared migrateData]; @@ -786,6 +786,8 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef //[KMDataRepository.shared migrateData]; [KMSettingsRepository.shared convertSettingsForMigration]; } + + [KMDataRepository.shared createKeyboardsDirectoryIfNecessary]; [KMSettingsRepository.shared createStorageFlagIfNecessary]; } diff --git a/mac/Keyman4MacIM/Keyman4MacIM/KMPackageReader.m b/mac/Keyman4MacIM/Keyman4MacIM/KMPackageReader.m index 7406488a8a..186ec8ab5a 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/KMPackageReader.m +++ b/mac/Keyman4MacIM/Keyman4MacIM/KMPackageReader.m @@ -69,6 +69,8 @@ typedef enum { return packageInfo; } +/* + // TODO: not used, delete - (NSString *)packageNameFromPackageInfo:(NSString *)path { NSString *packageName = nil; @@ -77,6 +79,7 @@ typedef enum { return packageName; } +*/ /** * read JSON file and load it into KMPackageInfo object diff --git a/mac/Keyman4MacIM/Keyman4MacIM/KMSettingsRepository.m b/mac/Keyman4MacIM/Keyman4MacIM/KMSettingsRepository.m index 84d6623d2c..d0776e01fc 100644 --- a/mac/Keyman4MacIM/Keyman4MacIM/KMSettingsRepository.m +++ b/mac/Keyman4MacIM/Keyman4MacIM/KMSettingsRepository.m @@ -11,14 +11,15 @@ */ #import "KMSettingsRepository.h" +//#import "KMDataRepository.h" #import "KMLogs.h" -NSString *const kStoreDataInLibraryKey = @"KMStoreDataInLibraryKey"; +NSString *const kStoreDataInLibraryKey = @"KMStoreDataInLibrary"; NSString *const kActiveKeyboardsKey = @"KMActiveKeyboardsKey"; NSString *const kSelectedKeyboardKey = @"KMSelectedKeyboardKey"; NSString *const kObsoletePathComponent = @"/Documents/"; -NSString *const kNewPathComponent = @"/Application Support/keyman.inputmethod.Keyman/"; +NSString *const kNewPathComponent = @"/Library/Application Support/keyman.inputmethod.Keyman/"; @implementation KMSettingsRepository diff --git a/mac/Keyman4MacIM/KeymanTests/InputMethodTests.m b/mac/Keyman4MacIM/KeymanTests/InputMethodTests.m index 8eb9d974ce..38994c351a 100644 --- a/mac/Keyman4MacIM/KeymanTests/InputMethodTests.m +++ b/mac/Keyman4MacIM/KeymanTests/InputMethodTests.m @@ -13,6 +13,9 @@ #import "KMInputMethodEventHandler.h" #import "AppleCompliantTestClient.h" #import "TextApiCompliance.h" +#import "KMSettingsRepository.h" +#import "KMDataRepository.h" +#import KMInputMethodEventHandler *testEventHandler = nil; @@ -85,4 +88,18 @@ KMInputMethodEventHandler *testEventHandler = nil; XCTAssertTrue(correctResult, @"insert or replacement range expected to be {1,2}"); } +- (void)testMigrateData_oldDataExists_logsLocations { + os_log_t startupLog = os_log_create("com.keyman.app", "data-migration"); + if ([KMSettingsRepository.shared dataMigrationNeeded]) { + os_log_info(startupLog, "data migration needed, calling migrateData"); + [KMDataRepository.shared migrateData]; + os_log_info(startupLog, "test: call migrateData again"); + [KMDataRepository.shared migrateData]; + } else { + os_log_info(startupLog, "data migration not needed"); + } + + XCTAssertTrue(YES, @"test failed"); +} + @end