mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-02 05:37:41 +00:00
feat(mac): migrate keyman packages individually to group containers
Some checks are pending
Keyman Build Summary / Summarize build status checks (push) Waiting to run
Some checks are pending
Keyman Build Summary / Summarize build status checks (push) Waiting to run
also rename Keyman-Keyboards directory to Keyman-Packages
This commit is contained in:
parent
10a2c8ddb3
commit
364ccc2d52
10 changed files with 257 additions and 93 deletions
|
|
@ -24,6 +24,12 @@ struct ConfigView: View {
|
|||
Button("debug") {
|
||||
settings.debug()
|
||||
}
|
||||
Button("log defaults") {
|
||||
settings.logSettings()
|
||||
}
|
||||
Button("clear defaults") {
|
||||
settings.clearSettings()
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.padding()
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@
|
|||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern NSString *const kKeymanGroupId;
|
||||
|
||||
@interface KMDataRepository : NSObject
|
||||
|
||||
// keyman18DataDirectory: '~/Library/Application Support/keyman.inputmethod.Keyman'
|
||||
@property (readonly) NSURL *keyman18DataDirectory;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,11 @@
|
|||
#import "KMDataRepository.h"
|
||||
#import "KMLogs.h"
|
||||
|
||||
// the name of the app group shared by the Keyman input method and the Keyman configuration app
|
||||
// an iOS-style app group of this name is defined for Keyman on developer.apple.com and linked
|
||||
// to the each app's provisioning profile
|
||||
NSString *const kKeymanGroupId = @"group.com.keyman";
|
||||
|
||||
@interface KMDataRepository ()
|
||||
@property (readonly) NSURL *applicationSupportSubDirectory;
|
||||
@property (readonly) NSURL *documentsSubDirectory;
|
||||
|
|
@ -22,7 +27,7 @@
|
|||
* Three directory trees are represented by the following properties, one in active use
|
||||
* and two that are obsolete.
|
||||
* The actively used directories, introduced in Keyman 19, are shared via the app group `group.com.keyman`:
|
||||
* 'Group Containers/group.com.keyman/Library/Application Support/Keyman-Keyboards/
|
||||
* 'Group Containers/group.com.keyman/Library/Application Support/Keyman-Packages/
|
||||
* The obsolete directories from Keyman 18 are:
|
||||
* applicationSupportSubDirectory: '~/Library/Application Support'
|
||||
* keyman18DataDirectory: '~/Library/Application Support/keyman.inputmethod.Keyman'
|
||||
|
|
@ -49,10 +54,7 @@ NSString *const kKeyboardsDirectoryName = @"Keyman-Keyboards";
|
|||
*/
|
||||
NSString *const kKeymanSubdirectoryName = @"keyman.inputmethod.Keyman";
|
||||
|
||||
//NSString *const kKeymanGroupId = @"3YE4W86L3G.com.keyman";
|
||||
NSString *const kKeymanGroupId = @"group.com.keyman";
|
||||
|
||||
NSString *const kContainerKeyboardsPartialPath = @"Library/Application Support/Keyman-Keyboards";
|
||||
NSString *const kContainerKeyboardsPartialPath = @"Library/Application Support/Keyman-Packages";
|
||||
|
||||
+ (KMDataRepository *)shared {
|
||||
static KMDataRepository *shared = nil;
|
||||
|
|
@ -63,6 +65,52 @@ NSString *const kContainerKeyboardsPartialPath = @"Library/Application Support/K
|
|||
return shared;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if URL is an existing directory that is not empty
|
||||
*/
|
||||
+ (BOOL)isExistingNonEmptyDirectory:(NSURL *)directoryUrl {
|
||||
return [KMDataRepository isExistingDirectory: directoryUrl] && ![KMDataRepository isEmptyDirectory: directoryUrl];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if URL is an existing directory that is empty
|
||||
*/
|
||||
+ (BOOL)isExistingEmptyDirectory:(NSURL *)directoryUrl {
|
||||
return [KMDataRepository isExistingDirectory: directoryUrl] && [KMDataRepository isEmptyDirectory: directoryUrl];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the directory is empty
|
||||
*/
|
||||
+ (BOOL)isExistingDirectory:(NSURL *)directoryUrl {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
BOOL nonEmptyDirectoryExists = false;
|
||||
BOOL isDirectory;
|
||||
BOOL urlExists = ([fileManager fileExistsAtPath:directoryUrl.path isDirectory:&isDirectory]);
|
||||
return urlExists && isDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the directory is empty
|
||||
*/
|
||||
+ (BOOL)isEmptyDirectory:(NSURL *)directoryUrl {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
BOOL isEmpty = true;
|
||||
|
||||
NSError *error = nil;
|
||||
NSArray *contents = [fileManager contentsOfDirectoryAtPath:directoryUrl.path error:&error];
|
||||
|
||||
if (error) {
|
||||
os_log_debug([KMLogs dataLog], "cannot read directory: %@", directoryUrl.path);
|
||||
} else if (contents.count > 0) {
|
||||
isEmpty = false;
|
||||
} else {
|
||||
isEmpty = true;
|
||||
}
|
||||
|
||||
return isEmpty;
|
||||
}
|
||||
|
||||
- (NSURL *)documentsSubDirectory {
|
||||
if (_documentsSubDirectory == nil) {
|
||||
NSError *directoryError = nil;
|
||||
|
|
@ -225,35 +273,24 @@ NSString *const kContainerKeyboardsPartialPath = @"Library/Application Support/K
|
|||
* otherwise we would not be attempting to migrate.
|
||||
*/
|
||||
- (BOOL)keyboardsExistInDocumentsDirectory {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
BOOL isDir;
|
||||
BOOL exists = ([fileManager fileExistsAtPath:self.keyman17KeyboardsDirectory.path isDirectory:&isDir]);
|
||||
return exists;
|
||||
return [KMDataRepository isExistingNonEmptyDirectory: self.keyman17KeyboardsDirectory];
|
||||
}
|
||||
|
||||
/**
|
||||
* Only called from migrateDataForKeyman19.
|
||||
* Checks to see if the keyboards directory exists in '~/Library/Application Support/keyman.inputmethod.Keyman'
|
||||
* Checks to see if the keyboards directory exists in '~/Library/Application Support/keyman.inputmethod.Keyman' and is not empty
|
||||
*/
|
||||
- (BOOL)keyboardsExistInInputMethodDataDirectory {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
BOOL isDir;
|
||||
BOOL exists = ([fileManager fileExistsAtPath:self.keyman18KeyboardsDirectory.path isDirectory:&isDir]);
|
||||
return exists;
|
||||
return [KMDataRepository isExistingNonEmptyDirectory: self.keyman18KeyboardsDirectory];
|
||||
}
|
||||
|
||||
// TODO: unused?
|
||||
/**
|
||||
* Checks to see if the keyboards directory exists in '~/Library/Group Containers/group.com.keyman/Library/Application Support'
|
||||
* Checks to see if the keyboards directory exists in '~/Library/Group Containers/group.com.keyman/Library/Application Support' and is not empty
|
||||
*/
|
||||
- (BOOL)keyboardsExistInGroupContainerDirectory {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
BOOL isDir;
|
||||
BOOL exists = ([fileManager fileExistsAtPath:self.keyman19KeyboardsDirectory.path isDirectory:&isDir]);
|
||||
return exists;
|
||||
return [KMDataRepository isExistingNonEmptyDirectory: self.keyman19KeyboardsDirectory];
|
||||
}
|
||||
|
||||
|
||||
// TODO: delete - no reason to move data more than once
|
||||
/**
|
||||
* Migrate the keyboards data from the old location in '~/Documents' to the location '~/Library/Application Support/keyman.inputmethod.Keyman'
|
||||
|
|
@ -283,6 +320,49 @@ NSString *const kContainerKeyboardsPartialPath = @"Library/Application Support/K
|
|||
return didMoveData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move all the packages contained in the directory
|
||||
*/
|
||||
+ (void)movePackages: (NSURL*)sourceDirectory to: (NSURL*)destinationDirectory {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSArray *keys = @[NSURLIsDirectoryKey];
|
||||
|
||||
// fetch the individual keyboard package directories within the keyboards directory
|
||||
NSArray *packageDirectories = [fileManager contentsOfDirectoryAtURL:sourceDirectory includingPropertiesForKeys:keys options:NSDirectoryEnumerationSkipsHiddenFiles error:nil];
|
||||
|
||||
// Iterate and filter for directories
|
||||
for (NSURL *packageUrl in packageDirectories) {
|
||||
NSNumber *isDirectory;
|
||||
[packageUrl getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil];
|
||||
|
||||
if ([isDirectory boolValue]) {
|
||||
NSString *packageName = packageUrl.lastPathComponent;
|
||||
NSURL *packageSourceUrl = [sourceDirectory URLByAppendingPathComponent:packageName isDirectory: true];
|
||||
NSURL *packageDestinationUrl = [destinationDirectory URLByAppendingPathComponent:packageName isDirectory: true];
|
||||
|
||||
os_log_info([KMLogs dataLog], "moving package directory: '%{public}@'", packageName);
|
||||
BOOL movedDirectory = [KMDataRepository moveDirectory: packageSourceUrl to: packageDestinationUrl];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a single directory
|
||||
*/
|
||||
+ (BOOL)moveDirectory: (NSURL*)sourceDirectory to: (NSURL*)destinationDirectory {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSError *moveError = nil;
|
||||
BOOL didMoveData = [fileManager moveItemAtURL:sourceDirectory
|
||||
toURL:destinationDirectory
|
||||
error:&moveError];
|
||||
if (moveError) {
|
||||
os_log_error([KMLogs dataLog], "data migration failed: '%{public}@'", moveError.localizedDescription);
|
||||
} else {
|
||||
os_log_info([KMLogs dataLog], "data migrated successfully to: '%{public}@'", destinationDirectory.path);
|
||||
}
|
||||
return didMoveData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate the keyboards data from the input method specific location in '~/Application Support/keyman.inputmethod.Keyman/'
|
||||
* to the shared location in '~/Library/Group Containers/group.com.keyman/Library/Application Support'
|
||||
|
|
@ -296,20 +376,15 @@ NSString *const kContainerKeyboardsPartialPath = @"Library/Application Support/K
|
|||
|
||||
// only move data if there is something to move
|
||||
if (dataExistsInOldLocation) {
|
||||
NSError *moveError = nil;
|
||||
didMoveData = [fileManager moveItemAtURL:self.keyman18KeyboardsDirectory
|
||||
toURL:self.keyman19KeyboardsDirectory
|
||||
error:&moveError];
|
||||
if (moveError) {
|
||||
os_log_error([KMLogs dataLog], "data migration failed: '%{public}@'", moveError.localizedDescription);
|
||||
} else {
|
||||
os_log_info([KMLogs dataLog], "data migrated successfully to: '%{public}@'", self.keyman19KeyboardsDirectory.path);
|
||||
}
|
||||
|
||||
[KMDataRepository.shared createKeyman19SharedDirectoriesIfNecessary];
|
||||
[KMDataRepository movePackages:[self keyman18KeyboardsDirectory] to:[self keyman19KeyboardsDirectory]];
|
||||
}
|
||||
|
||||
return didMoveData;
|
||||
}
|
||||
|
||||
|
||||
- (NSString*)buildFullPath:(NSString *)fromPartialPath {
|
||||
NSString *fullPath = [self.keyman18KeyboardsDirectory.path stringByAppendingString:fromPartialPath];
|
||||
os_log_debug([KMLogs dataLog], "buildFullPath: '%{public}@' fromPartialPath '%{public}@'",
|
||||
|
|
|
|||
|
|
@ -756,29 +756,23 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
- (void)prepareStorage {
|
||||
os_log_debug([KMLogs dataLog], "*** prepareStorage ***");
|
||||
|
||||
// TODO: create directory before copy during migration?
|
||||
// [KMDataRepository.shared createKeyman19SharedDirectoriesIfNecessary];
|
||||
// [KMDataRepository.shared createKeyman18DataDirectoryIfNecessary];
|
||||
|
||||
// Keyman 18 data migration: TODO: uncomment and modify for double migration
|
||||
// Keyman 18 data migration: TODO: uncomment and modify for double migration
|
||||
/*
|
||||
if ([KMSettingsRepository.shared keyman18DataMigrationNeeded]) {
|
||||
[KMDataRepository.shared migrateDataForKeyman18];
|
||||
[KMSettingsRepository.shared migrateSettingsForKeyman18];
|
||||
}
|
||||
*/
|
||||
|
||||
// Keyman 19 data migration
|
||||
|
||||
// if necessary, migrate settings and keyboard data for compatibility with Keyman 19
|
||||
if ([KMSettingsRepository.shared keyman19SettingsMigrationNeeded]) {
|
||||
[KMDataRepository.shared migrateDataForKeyman19];
|
||||
//[KMSettingsRepository.shared migrateSettingsForKeyman18];
|
||||
[KMSettingsRepository.shared migrateSettingsForKeyman19];
|
||||
}
|
||||
|
||||
[KMDataRepository.shared createKeyman19SharedDirectoriesIfNecessary];
|
||||
|
||||
// TODO: delete
|
||||
// TODO: delete, instead manage shared settings
|
||||
//[KMDataRepository.shared createKeyboardsDirectoryIfNecessary];
|
||||
[KMSettingsRepository.shared setDataModelVersionIfNecessary];
|
||||
//[KMSettingsRepository.shared setDataModelVersionIfNecessary];
|
||||
}
|
||||
|
||||
- (void)setDefaultKeymanMenuItems {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
- (BOOL)keyman19SettingsMigrationNeeded;
|
||||
- (void)migrateSettingsForKeyman18;
|
||||
- (void)setDataModelVersionIfNecessary;
|
||||
- (void)migrateSettingsForKeyman19;
|
||||
- (BOOL)migrateInputMethodSettingsToAppGroup;
|
||||
- (NSString *)readSelectedKeyboard;
|
||||
- (void)writeSelectedKeyboard:(NSString *)selectedKeyboard;
|
||||
- (NSArray *)readActiveKeyboards;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@
|
|||
#import "KMLogs.h"
|
||||
#import "KMDataRepository.h"
|
||||
|
||||
// same as app group ID, kKeymanGroupId
|
||||
//NSString *const kKeymanSuiteName = @"group.com.keyman";
|
||||
|
||||
NSString *const kActiveKeyboardsKey = @"KMActiveKeyboardsKey";
|
||||
NSString *const kSelectedKeyboardKey = @"KMSelectedKeyboardKey";
|
||||
NSString *const kPersistedOptionsKey = @"KMPersistedOptionsKey";
|
||||
|
|
@ -45,7 +48,8 @@ NSString *const kNewPathComponent = @"/Library/Application Support/keyman.inputm
|
|||
* directory instead of in the Documents directory.
|
||||
*/
|
||||
NSString *const kDataModelVersion = @"KMDataModelVersion";
|
||||
NSInteger const kVersionStoreDataInLibraryDirectory = 1;
|
||||
NSInteger const kVersionStoreDataInLibraryDirectory = 1; // introduced with Keyman 18
|
||||
NSInteger const kVersionStoreDataInGroupContainer = 2; // introduced with Keyman 19
|
||||
NSInteger const kCurrentDataModelVersionNumber = kVersionStoreDataInLibraryDirectory;
|
||||
|
||||
@implementation KMSettingsRepository
|
||||
|
|
@ -60,6 +64,100 @@ NSInteger const kCurrentDataModelVersionNumber = kVersionStoreDataInLibraryDirec
|
|||
return shared;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the keyboard data needs to be moved from pre-Keyman-18 location to the Keyman 18 location
|
||||
* This is true if
|
||||
* 1) the UserDefaults exist (indicating that this is not a new installation of Keyman) and
|
||||
* 2) the value for kVersionStoreDataInLibraryDirectory is < 1,
|
||||
*/
|
||||
- (BOOL)keyman18DataMigrationNeeded {
|
||||
BOOL keymanSettingsExist = [self settingsExist];
|
||||
os_log([KMLogs dataLog], "keyman settings exist: %{public}@", keymanSettingsExist ? @"YES" : @"NO" );
|
||||
|
||||
BOOL keyboardsStoredInLibrary = [self dataModelWithKeyboardsInLibrary];
|
||||
os_log([KMLogs dataLog], "settings indicate that keyboards are stored in ~/Library: %{public}@", keyboardsStoredInLibrary ? @"YES" : @"NO" );
|
||||
|
||||
BOOL migrationNeeded = keymanSettingsExist && !keyboardsStoredInLibrary;
|
||||
os_log([KMLogs dataLog], "dataMigrationNeeded: %{public}@", migrationNeeded ? @"YES" : @"NO" );
|
||||
|
||||
return migrationNeeded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the keyboard data needs to be moved from the Keyman 18 location to the Keyman 19 location
|
||||
* This is true if the UserDefaults exist in the old location for the input method
|
||||
*/
|
||||
- (BOOL)keyman19SettingsMigrationNeeded {
|
||||
BOOL keymanSettingsExistForInputMethod = [self inputMethodUserDefaultsExist];
|
||||
os_log([KMLogs dataLog], "keyman input method settings exist (for 18 and earlier): %{public}@", keymanSettingsExistForInputMethod ? @"YES" : @"NO" );
|
||||
|
||||
BOOL keyboardsStoredInLibrary = [self dataModelWithKeyboardsInLibrary];
|
||||
os_log([KMLogs dataLog], "settings indicate that keyboards are stored in ~/Library: %{public}@", keyboardsStoredInLibrary ? @"YES" : @"NO" );
|
||||
|
||||
BOOL migrationNeeded = keymanSettingsExistForInputMethod;
|
||||
os_log([KMLogs dataLog], "dataMigrationNeeded: %{public}@", migrationNeeded ? @"YES" : @"NO" );
|
||||
|
||||
return migrationNeeded;
|
||||
}
|
||||
|
||||
- (void)migrateSettingsForKeyman19 {
|
||||
[self migrateInputMethodSettingsToAppGroup];
|
||||
|
||||
NSUserDefaults *groupUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:kKeymanGroupId];
|
||||
|
||||
// set kDataModelVersion to indicate that we are using the group container
|
||||
[groupUserDefaults setInteger:kVersionStoreDataInGroupContainer forKey:kDataModelVersion];
|
||||
}
|
||||
|
||||
/**
|
||||
* Move userdefaults from app to app group
|
||||
* Read the settings in the input method's user defaults
|
||||
* Write them to the shared app group user defaults
|
||||
* Delete them from the input method's user defaults
|
||||
*/
|
||||
- (BOOL)migrateInputMethodSettingsToAppGroup {
|
||||
NSUserDefaults *appUserDefaults = [NSUserDefaults standardUserDefaults];
|
||||
NSUserDefaults *groupUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:kKeymanGroupId];
|
||||
|
||||
NSString *selectedKeyboard = [appUserDefaults stringForKey:kSelectedKeyboardKey];
|
||||
if (selectedKeyboard != nil) {
|
||||
[groupUserDefaults setObject:selectedKeyboard forKey:kSelectedKeyboardKey];
|
||||
}
|
||||
|
||||
NSArray * activeKeyboards = [appUserDefaults arrayForKey:kActiveKeyboardsKey];
|
||||
if (activeKeyboards != nil) {
|
||||
[groupUserDefaults setObject:activeKeyboards forKey:kActiveKeyboardsKey];
|
||||
}
|
||||
|
||||
if ([appUserDefaults objectForKey:kShowOskOnActivate] != nil) {
|
||||
BOOL showOsk = [appUserDefaults boolForKey:kShowOskOnActivate];
|
||||
[groupUserDefaults setBool:showOsk forKey:kShowOskOnActivate];
|
||||
}
|
||||
|
||||
if ([appUserDefaults objectForKey:kForceSentryError] != nil) {
|
||||
BOOL forceSentryError = [appUserDefaults boolForKey:kForceSentryError];
|
||||
[groupUserDefaults setBool:forceSentryError forKey:kForceSentryError];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
NSString *const kActiveKeyboardsKey = @"KMActiveKeyboardsKey";
|
||||
NSString *const kSelectedKeyboardKey = @"KMSelectedKeyboardKey";
|
||||
NSString *const kPersistedOptionsKey = @"KMPersistedOptionsKey";
|
||||
NSString *const kShowOskOnActivate = @"KMShowOskOnActivate";
|
||||
NSString *const kForceSentryError = @"KMForceSentryError";
|
||||
|
||||
*/
|
||||
|
||||
- (void)migrateSettingsForKeyman18 {
|
||||
os_log_debug([KMLogs dataLog], "converting settings in UserDefaults for migration");
|
||||
[self convertSelectedKeyboardPathForKeyman18Migration];
|
||||
[self convertActiveKeyboardArrayForKeyman18Migration];
|
||||
[self convertOptionsPathsForKeyman18Migration];
|
||||
}
|
||||
|
||||
- (void)setDataModelVersionIfNecessary {
|
||||
if (![self dataModelWithKeyboardsInLibrary]) {
|
||||
[[NSUserDefaults standardUserDefaults] setInteger:kVersionStoreDataInLibraryDirectory forKey:kDataModelVersion];
|
||||
|
|
@ -85,7 +183,6 @@ NSInteger const kCurrentDataModelVersionNumber = kVersionStoreDataInLibraryDirec
|
|||
return ([[NSUserDefaults standardUserDefaults] objectForKey:kSelectedKeyboardKey] != nil);
|
||||
}
|
||||
|
||||
|
||||
- (void)writeOptionForSelectedKeyboard:(NSString *)key withValue:(NSString*)value {
|
||||
NSDictionary *optionsMap = [self readOptionsForSelectedKeyboard];
|
||||
NSDictionary *newOptionsMap = nil;
|
||||
|
|
@ -134,43 +231,6 @@ NSInteger const kCurrentDataModelVersionNumber = kVersionStoreDataInLibraryDirec
|
|||
return dataModelVersion >= kVersionStoreDataInLibraryDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the keyboard data needs to be moved from pre-Keyman-18 location to the Keyman 18 location
|
||||
* This is true if
|
||||
* 1) the UserDefaults exist (indicating that this is not a new installation of Keyman) and
|
||||
* 2) the value for kVersionStoreDataInLibraryDirectory is < 1,
|
||||
*/
|
||||
- (BOOL)keyman18DataMigrationNeeded {
|
||||
BOOL keymanSettingsExist = [self settingsExist];
|
||||
os_log([KMLogs dataLog], "keyman settings exist: %{public}@", keymanSettingsExist ? @"YES" : @"NO" );
|
||||
|
||||
BOOL keyboardsStoredInLibrary = [self dataModelWithKeyboardsInLibrary];
|
||||
os_log([KMLogs dataLog], "settings indicate that keyboards are stored in ~/Library: %{public}@", keyboardsStoredInLibrary ? @"YES" : @"NO" );
|
||||
|
||||
BOOL migrationNeeded = keymanSettingsExist && !keyboardsStoredInLibrary;
|
||||
os_log([KMLogs dataLog], "dataMigrationNeeded: %{public}@", migrationNeeded ? @"YES" : @"NO" );
|
||||
|
||||
return migrationNeeded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the keyboard data needs to be moved from the Keyman 18 location to the Keyman 19 location
|
||||
* This is true if the UserDefaults exist in the old location for the input method
|
||||
*/
|
||||
- (BOOL)keyman19SettingsMigrationNeeded {
|
||||
BOOL keymanSettingsExistForInputMethod = [self inputMethodUserDefaultsExist];
|
||||
os_log([KMLogs dataLog], "keyman input method settings exist (for 18 and earlier): %{public}@", keymanSettingsExistForInputMethod ? @"YES" : @"NO" );
|
||||
|
||||
BOOL keyboardsStoredInLibrary = [self dataModelWithKeyboardsInLibrary];
|
||||
os_log([KMLogs dataLog], "settings indicate that keyboards are stored in ~/Library: %{public}@", keyboardsStoredInLibrary ? @"YES" : @"NO" );
|
||||
|
||||
BOOL migrationNeeded = keymanSettingsExistForInputMethod;
|
||||
os_log([KMLogs dataLog], "dataMigrationNeeded: %{public}@", migrationNeeded ? @"YES" : @"NO" );
|
||||
|
||||
return migrationNeeded;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)readSelectedKeyboard {
|
||||
return [[NSUserDefaults standardUserDefaults] objectForKey:kSelectedKeyboardKey];
|
||||
}
|
||||
|
|
@ -254,13 +314,6 @@ NSInteger const kCurrentDataModelVersionNumber = kVersionStoreDataInLibraryDirec
|
|||
return [userData removeObjectForKey:kPersistedOptionsKey];
|
||||
}
|
||||
|
||||
- (void)migrateSettingsForKeyman18 {
|
||||
os_log_debug([KMLogs dataLog], "converting settings in UserDefaults for migration");
|
||||
[self convertSelectedKeyboardPathForKeyman18Migration];
|
||||
[self convertActiveKeyboardArrayForKeyman18Migration];
|
||||
[self convertOptionsPathsForKeyman18Migration];
|
||||
}
|
||||
|
||||
- (void)convertSelectedKeyboardPathForKeyman18Migration {
|
||||
NSString *selectedKeyboardPath = [self readSelectedKeyboard];
|
||||
if (selectedKeyboardPath != nil) {
|
||||
|
|
|
|||
|
|
@ -52,7 +52,15 @@ public class SettingsContainer : ObservableObject {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func logSettings() {
|
||||
self.settingsRepository.logSettings()
|
||||
}
|
||||
|
||||
public func clearSettings() {
|
||||
self.settingsRepository.clearSettings()
|
||||
}
|
||||
|
||||
// TODO: extract method for keyboard search
|
||||
// TODO: throw error for keyboard not found or log and fail silently (should never happen)
|
||||
public func isKeyboardEnabled(packageId: UUID, keyboardId: String) -> Bool {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import Foundation
|
|||
* containerDirectory: '~/Library/Group Containers'
|
||||
* containerKeymanDirectory: '~/Library/Group Containers/group.com.keyman'
|
||||
* groupKeymanSupportDirectory: '~/Library/Group Containers/group.com.keyman/Library/Application Support'
|
||||
* keyman19KeyboardsDirectory: '~/Library/Group Containers/group.com.keyman/Library/Application Support/Keyman-Keyboards'
|
||||
* keyman19KeyboardsDirectory: '~/Library/Group Containers/group.com.keyman/Library/Application Support/Keyman-Packages'
|
||||
*/
|
||||
|
||||
public struct KeymanPaths {
|
||||
|
|
@ -43,7 +43,7 @@ public struct KeymanPaths {
|
|||
static private let keymanSubdirectoryName = "keyman.inputmethod.Keyman"
|
||||
|
||||
static private let containerPreferencesPartialPath = "Library/Preferences"
|
||||
static private let containerKeyboardsPartialPath = "Library/Application Support/Keyman-Keyboards"
|
||||
static private let containerKeyboardsPartialPath = "Library/Application Support/Keyman-Packages"
|
||||
|
||||
// keyman 17 and earlier
|
||||
let keyman17DocumentsDirectory: URL?
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public struct SettingsRepository {
|
|||
let kActiveKeyboardsKey = "KMActiveKeyboardsKey"
|
||||
let kSelectedKeyboardKey = "KMSelectedKeyboardKey"
|
||||
let kPersistedOptionsKey = "KMPersistedOptionsKey"
|
||||
let kDataModelVersionKey = "KMDataModelVersionKey"
|
||||
let kDataModelVersionKey = "KMDataModelVersion"
|
||||
let kShowOskOnActivateKey = "KMShowOskOnActivate"
|
||||
let kForceSentryError = "KMForceSentryError"
|
||||
|
||||
|
|
@ -68,4 +68,27 @@ public struct SettingsRepository {
|
|||
sharedDefaults.set(keyboardName, forKey: kSelectedKeyboardKey)
|
||||
}
|
||||
}
|
||||
|
||||
public func logSettings() {
|
||||
print("UserDefaults:")
|
||||
if let sharedDefaults = self.defaults {
|
||||
print("kSelectedKeyboardKey: \(sharedDefaults.value(forKey: kSelectedKeyboardKey) ?? "nil")")
|
||||
print("kDataModelVersionKey: \(sharedDefaults.value(forKey: kDataModelVersionKey) ?? "nil")")
|
||||
print("kForceSentryError: \(sharedDefaults.value(forKey: kForceSentryError) ?? "nil")")
|
||||
print("kShowOskOnActivateKey: \(sharedDefaults.value(forKey: kShowOskOnActivateKey) ?? "nil")")
|
||||
print("kActiveKeyboardsKey: \(sharedDefaults.value(forKey: kActiveKeyboardsKey) ?? "nil")")
|
||||
print("kPersistedOptionsKey: \(sharedDefaults.value(forKey: kPersistedOptionsKey) ?? "nil")")
|
||||
}
|
||||
}
|
||||
|
||||
public func clearSettings() {
|
||||
guard let sharedDefaults = self.defaults else {
|
||||
print("Group container UserDefaults not found.")
|
||||
return
|
||||
}
|
||||
|
||||
sharedDefaults.dictionaryRepresentation().keys.forEach { key in
|
||||
sharedDefaults.removeObject(forKey: key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ import Foundation
|
|||
@Test("Check Keyman 19 keyboards directory") func testKeyman19KeyboardsDirectory() async throws {
|
||||
#expect(true)
|
||||
let keyboardsDirectory = try #require(KeymanPaths().keyman19KeyboardsDirectory)
|
||||
#expect(keyboardsDirectory.absoluteString.hasSuffix("Group%20Containers/group.com.keyman/Library/Application%20Support/Keyman-Keyboards/"))
|
||||
#expect(keyboardsDirectory.absoluteString.hasSuffix("Group%20Containers/group.com.keyman/Library/Application%20Support/Keyman-Packages/"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue