Merge pull request #12144 from keymanapp/change/mac/2542-use-partial-keyboard-paths

change(mac): store partial path in UserDefaults
This commit is contained in:
Shawn Schantz 2024-08-30 12:36:17 +07:00 committed by GitHub
commit 0aa4be5584
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 296 additions and 185 deletions

View file

@ -8,6 +8,7 @@
#import "KMConfigurationWindowController.h"
#import "KMDownloadKBWindowController.h"
#import "KMDataRepository.h"
#import "KMLogs.h"
@interface KMConfigurationWindowController ()
@ -134,8 +135,10 @@
NSArray *pArray = (NSArray *)obj;
NSString *packageFolder = [self packageFolderFromPath:[pArray objectAtIndex:0]];
NSString *packageName = [self.AppDelegate packageNameFromPackageInfo:packageFolder];
os_log_debug([KMLogs uiLog], "tableContents, packageFolder: %{public}@, packageName: %{public}@", packageFolder, packageName);
[_tableContents addObject:[NSDictionary dictionaryWithObjectsAndKeys:packageName, @"HeaderTitle", nil]];
for (NSString *path in pArray) {
os_log_debug([KMLogs uiLog], "tableContents, path = '%{public}@'", path);
NSDictionary *info = [KMXFile keyboardInfoFromKmxFile:path];
if (!info) {
info = [[NSDictionary alloc] initWithObjectsAndKeys:
@ -150,6 +153,7 @@
}
else {
NSString *path = (NSString *)obj;
os_log_debug([KMLogs uiLog], "tableContents, path = '%{public}@'", path);
NSDictionary *info = [KMXFile keyboardInfoFromKmxFile:path];
if (!info) {
info = [[NSDictionary alloc] initWithObjectsAndKeys:
@ -241,22 +245,27 @@
BOOL isHeader = (headerTitle != nil);
BOOL isOthers = NO;
NSString *kmxFilePath = [self kmxFilePathAtIndex:row];
if (kmxFilePath != nil)
if (kmxFilePath != nil) {
isOthers = [[self packageFolderFromPath:kmxFilePath] isEqualToString:@"Others"];
else if (isHeader && [headerTitle isEqualToString:@"Others"])
}
else if (isHeader && [headerTitle isEqualToString:@"Others"]) {
isOthers = YES;
}
if ([identifier isEqualToString:@"Column1"]) {
KMConfigColumn1CellView *cellView = [tableView makeViewWithIdentifier:identifier owner:self];
if (isHeader)
if (isHeader) {
[cellView setHidden:YES];
}
else {
[cellView setHidden:NO];
cellView.imageView.objectValue = [info objectForKey:kKMKeyboardIconKey];
[cellView.checkBox setTag:row];
[cellView.checkBox setAction:@selector(checkBoxAction:)];
[cellView.checkBox setState:([self.activeKeyboards containsObject:[self kmxFilePathAtIndex:row]])?NSOnState:NSOffState];
NSString *kmxFilePath = [self kmxFilePathAtIndex:row];
NSString *partialPath = [KMDataRepository.shared trimToPartialPath:kmxFilePath];
os_log_debug([KMLogs uiLog], "tableView:viewForTableColumn, kmxFilePath = %{public}@ for row %li, partialPath = %{public}@", kmxFilePath, (long)row, partialPath);
[cellView.checkBox setState:([self.activeKeyboards containsObject:partialPath])?NSOnState:NSOffState];
}
return cellView;
@ -357,14 +366,16 @@
- (void)checkBoxAction:(id)sender {
NSButton *checkBox = (NSButton *)sender;
NSString *kmxFilePath = [self kmxFilePathAtIndex:checkBox.tag];
NSString *partialPath = [KMDataRepository.shared trimToPartialPath:kmxFilePath];
os_log_debug([KMLogs uiLog], "checkBoxAction, kmxFilePath = %{public}@ for checkBox.tag %li, partialPath = %{public}@", kmxFilePath, checkBox.tag, partialPath);
if (checkBox.state == NSOnState) {
os_log_debug([KMLogs uiLog], "Adding active keyboard: %{public}@", kmxFilePath);
[self.activeKeyboards addObject:kmxFilePath];
[self.activeKeyboards addObject:partialPath];
[self saveActiveKeyboards];
}
else if (checkBox.state == NSOffState) {
os_log_debug([KMLogs uiLog], "Disabling active keyboard: %{public}@", kmxFilePath);
[self.activeKeyboards removeObject:kmxFilePath];
[self.activeKeyboards removeObject:partialPath];
[self saveActiveKeyboards];
}
}

View file

@ -20,6 +20,9 @@ NS_ASSUME_NONNULL_BEGIN
- (void)createDataDirectoryIfNecessary;
- (void)createKeyboardsDirectoryIfNecessary;
- (BOOL)migrateData;
- (NSString*)buildFullPath:(NSString *)fromPartialPath;
- (NSString*)trimToPartialPath:(NSString *)fromFullPath;
- (NSString *)buildPartialPathFrom:(NSString *)keyboardSubdirectory keyboardFile:(NSString *)kmxFilename;
@end
NS_ASSUME_NONNULL_END

View file

@ -178,7 +178,7 @@ NSString *const kKeymanSubdirectoryName = @"keyman.inputmethod.Keyman";
BOOL didMoveData = NO;
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL dataExistsInOldLocation = [self keyboardsExistInObsoleteDirectory];
os_log([KMLogs dataLog], "obsolete keyman keyboards directory exists: %@", dataExistsInOldLocation?@"YES":@"NO");
os_log_debug([KMLogs dataLog], "obsolete keyman keyboards directory exists: %@", dataExistsInOldLocation?@"YES":@"NO");
// only move data if there is something to move
if (dataExistsInOldLocation) {
@ -196,4 +196,35 @@ NSString *const kKeymanSubdirectoryName = @"keyman.inputmethod.Keyman";
return didMoveData;
}
- (NSString*)buildFullPath:(NSString *)fromPartialPath {
NSString *fullPath = [self.keymanKeyboardsDirectory.path stringByAppendingString:fromPartialPath];
os_log_debug([KMLogs dataLog], "buildFullPath: '%{public}@' fromPartialPath '%{public}@'",
fullPath, fromPartialPath);
return fullPath;
}
- (NSString *)trimToPartialPath:(NSString *)fromFullPath {
NSString *partialPath = fromFullPath;
if(fromFullPath != nil) {
NSRange range = [fromFullPath rangeOfString:kKeyboardsDirectoryName];
if (range.length > 0) {
partialPath = [fromFullPath substringFromIndex:range.location + range.length];
os_log_debug([KMLogs dataLog], "trimToPartialPath: fromFullPath: '%{public}@' to partialPath: '%{public}@'", fromFullPath, partialPath);
}
}
return partialPath;
}
- (NSString *)buildPartialPathFrom:(NSString *)keyboardSubdirectory keyboardFile:(NSString *)kmxFilename {
NSMutableArray *pathComponents = [[NSMutableArray alloc] initWithCapacity:0];
[pathComponents addObject:@"/"];
[pathComponents addObject:keyboardSubdirectory];
[pathComponents addObject:kmxFilename];
NSString *keyboardPartialPath = [NSString pathWithComponents:pathComponents];
os_log_debug([KMLogs keyboardLog], "buildPartialPathFrom, keyboardSubdirectory: %{public}@, kmxFileName: %{public}@, keyboardPartialPath : %{public}@",
keyboardSubdirectory, kmxFilename, keyboardPartialPath);
return keyboardPartialPath;
}
@end

View file

@ -23,8 +23,6 @@
typedef void(^PostEventCallback)(CGEventRef eventToPost);
extern NSString *const kKMSelectedKeyboardKey;
extern NSString *const kKMActiveKeyboardsKey;
extern NSString *const kKeymanKeyboardDownloadCompletedNotification;
typedef struct {
@ -99,8 +97,7 @@ static const int KEYMAN_FIRST_KEYBOARD_MENUITEM_INDEX = 0;
- (NSMenu *)menu;
- (void)saveActiveKeyboards;
- (void)readPersistedOptions;
- (void)writePersistedOptions:(NSString *)storeKey withValue:(NSString* )value;
- (void)applyPersistedOptions;
- (void)showAboutWindow;
- (void)showOSK;
- (void)showConfigurationWindow;

View file

@ -6,15 +6,6 @@
// Copyright (c) 2017 SIL International. All rights reserved.
//
// *** TO INVESTIGATE ***
// Keyman4MacIM[6245]: IMK Stall detected, *please Report* your user scenario in <rdar://problem/16792073> - (activateServerWithReply:) block performed very slowly (0.00 secs)
// Keyman4MacIM[6245]: IMK Stall detected, *please Report* your user scenario in <rdar://problem/16792073> - (deactivateServerWithReply:) block performed very slowly (0.00 secs)
// Keyman4MacIM[6245]: IMK Stall detected, *please Report* your user scenario in <rdar://problem/16792073> - (menusDictionaryWithClientAsync:reply:) block performed very slowly (0.00 secs)
// Keyman4MacIM[6245]: IMK Stall detected, *please Report* your user scenario in <rdar://problem/16792073> - (modesWithClientAsync:reply:) block performed very slowly (0.00 secs)
// Keyman4MacIM[6245]: IMK Stall detected, *please Report* your user scenario in <rdar://problem/16792073> - (commitCompositionWithReply:) block performed very slowly (0.00 secs)
// Keyman4MacIM[6245]: IMK Stall detected, *please Report* your user scenario in <rdar://problem/16792073> - (hidePalettes) block performed very slowly (0.00 secs)
// Keyman4MacIM[6245]: IMK Stall detected, *please Report* your user scenario in <rdar://problem/16792073> - (sessionFinished) block performed very slowly (0.00 secs)
#import "KMInputMethodAppDelegate.h"
#import "KMSettingsRepository.h"
#import "KMDataRepository.h"
@ -28,21 +19,6 @@
#import "KMLogs.h"
@import Sentry;
/** NSUserDefaults keys */
NSString *const kKMSelectedKeyboardKey = @"KMSelectedKeyboardKey";
NSString *const kKMActiveKeyboardsKey = @"KMActiveKeyboardsKey";
/**
The following constant "KMSavedStoresKey" is left here for documentation
though we have abandoned stores written to UserDefaults with this key because
they used a less-reliable numeric key prior to integration with Keyman Core.
It is replaced by the renamed "KMPersistedOptionsKey" which directly
represents what it is saving.
*/
NSString *const kKMDeprecatedPersistedOptionsKey = @"KMSavedStoresKey";
NSString *const kKMPersistedOptionsKey = @"KMPersistedOptionsKey";
NSString *const kKMAlwaysShowOSKKey = @"KMAlwaysShowOSKKey";
NSString *const kKMUseVerboseLogging = @"KMUseVerboseLogging";
NSString *const kKeymanKeyboardDownloadCompletedNotification = @"kKeymanKeyboardDownloadCompletedNotification";
@implementation NSString (VersionNumbers)
@ -169,12 +145,9 @@ id _lastServerWithOSKShowing = nil;
[[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"NSApplicationCrashOnExceptions": @YES }];
[KMLogs reportLogStatus];
[self startSentry];
[self setDefaultKeymanMenuItems];
[self updateKeyboardMenuItems];
[self setPostLaunchKeymanSentryTags];
// [SentrySDK captureMessage:@"Starting Keyman [test message]"];
}
@ -412,7 +385,6 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
- (KMPackageReader *)packageReader {
if (_packageReader == nil) {
_packageReader = [[KMPackageReader alloc] init];
[_packageReader setDebugMode:self.debugMode];
}
return _packageReader;
@ -443,55 +415,17 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
[_oskWindow.window setTitle:self.oskWindowTitle];
}
- (void)readPersistedOptions {
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
NSDictionary *allPersistedOptions = [userData dictionaryForKey:kKMPersistedOptionsKey];
if (!allPersistedOptions) {
return;
}
NSDictionary *persistedOptionsForSelectedKeyboard = [allPersistedOptions objectForKey:_selectedKeyboard];
if (!persistedOptionsForSelectedKeyboard) {
os_log_info([KMLogs configLog], "no persisted options found in UserDefaults for keyboard %{public}@ ", _selectedKeyboard);
return;
}
- (void)applyPersistedOptions {
NSDictionary *selectedPersistedOptions = [[KMSettingsRepository shared] readOptionsForSelectedKeyboard];
// TODO: pass array instead of making repeated calls
for (NSString *key in persistedOptionsForSelectedKeyboard) {
NSString *value = [persistedOptionsForSelectedKeyboard objectForKey:key];
for (NSString *key in selectedPersistedOptions) {
NSString *value = [selectedPersistedOptions objectForKey:key];
os_log_info([KMLogs configLog], "persisted options found in UserDefaults for keyboard %{public}@, key: %{public}@, value: %{public}@", _selectedKeyboard, key, value);
[self.kme setCoreOptions:key withValue:value];
}
}
- (void)writePersistedOptions:(NSString *)storeKey withValue:(NSString* )value {
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
NSDictionary *allPersistedOptions = [userData dictionaryForKey:kKMPersistedOptionsKey];
NSDictionary *persistedOptionsForSelectedKeyboard;
if (allPersistedOptions) {
persistedOptionsForSelectedKeyboard = [allPersistedOptions objectForKey:_selectedKeyboard];
}
if (persistedOptionsForSelectedKeyboard) {
NSMutableDictionary *newSavedStores = [persistedOptionsForSelectedKeyboard mutableCopy];
[newSavedStores setObject:value forKey:storeKey];
persistedOptionsForSelectedKeyboard = newSavedStores;
} else {
persistedOptionsForSelectedKeyboard = [[NSDictionary alloc] initWithObjectsAndKeys:value, storeKey, nil];
}
if (allPersistedOptions) {
NSMutableDictionary *newAllSavedStores = [allPersistedOptions mutableCopy];
[newAllSavedStores setObject:persistedOptionsForSelectedKeyboard forKey:_selectedKeyboard];
allPersistedOptions = newAllSavedStores;
} else {
allPersistedOptions = [[NSDictionary alloc] initWithObjectsAndKeys:persistedOptionsForSelectedKeyboard, _selectedKeyboard, nil];
}
[userData setObject:allPersistedOptions forKey:kKMPersistedOptionsKey];
[userData synchronize];
}
- (NSString *)oskWindowTitle {
if (_keyboardName == nil || !_keyboardName.length)
return [NSString stringWithFormat:@"Keyman"];
@ -501,9 +435,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
- (void)setAlwaysShowOSK:(BOOL)alwaysShowOSK {
_alwaysShowOSK = alwaysShowOSK;
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setBool:alwaysShowOSK forKey:kKMAlwaysShowOSKKey];
[userData synchronize];
[[KMSettingsRepository shared] writeAlwaysShowOsk:alwaysShowOSK];
}
- (void)setUseVerboseLogging:(BOOL)useVerboseLogging {
@ -511,23 +443,17 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
_debugMode = useVerboseLogging;
if (_kme != nil)
[_kme setUseVerboseLogging:useVerboseLogging];
if (_packageReader != nil) {
[_packageReader setDebugMode:useVerboseLogging];
}
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setBool:useVerboseLogging forKey:kKMUseVerboseLogging];
[userData synchronize];
[[KMSettingsRepository shared] writeUseVerboseLogging:useVerboseLogging];
}
- (BOOL)alwaysShowOSK {
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
_alwaysShowOSK = [userData boolForKey:kKMAlwaysShowOSKKey];
_alwaysShowOSK = [[KMSettingsRepository shared] readAlwaysShowOsk];
return _alwaysShowOSK;
}
- (BOOL)useVerboseLogging {
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
return [userData boolForKey:kKMUseVerboseLogging];
return [[KMSettingsRepository shared] readUseVerboseLogging];
}
#pragma mark - Keyman Data
@ -544,12 +470,13 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
}
- (NSArray *)kmxFileList {
os_log_debug([KMLogs dataLog], "kmxFileList");
if (_kmxFileList == nil) {
NSArray *kmxFiles = [self KMXFiles];
os_log_debug([KMLogs dataLog], "creating kmxFileList");
NSArray *kmxFiles = [self getKmxFilesInKeyboardsDirectory];
_kmxFileList = [[NSMutableArray alloc] initWithCapacity:0];
NSMutableArray *others = nil;
for (NSString *filePath in kmxFiles) {
os_log_debug([KMLogs dataLog], "kmxFileList, filePath: %{public}@", filePath);
NSString *packageFolder = [self packageFolderFromPath:filePath];
NSInteger index = [self indexForPackageFolder:packageFolder];
if ([packageFolder isEqualToString:@"Others"]) {
@ -666,7 +593,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
- (NSArray *)keyboardNamesFromFolder:(NSString *)packageFolder {
os_log_debug([KMLogs dataLog], "keyboardNamesFromFolder, folder = %{public}@", packageFolder);
NSMutableArray *kbNames = [[NSMutableArray alloc] initWithCapacity:0];;
for (NSString *kmxFile in [self KMXFilesAtPath:packageFolder]) {
for (NSString *kmxFile in [self getKmxFilesAtPath:packageFolder]) {
NSDictionary * infoDict = [KMXFile keyboardInfoFromKmxFile:kmxFile];
if (infoDict != nil) {
NSString *name = [infoDict objectForKey:kKMKeyboardNameKey];
@ -681,52 +608,56 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
- (NSString *)selectedKeyboard {
if (_selectedKeyboard == nil) {
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
_selectedKeyboard = [userData objectForKey:kKMSelectedKeyboardKey];
_selectedKeyboard = [[KMSettingsRepository shared] readSelectedKeyboard];
}
os_log_debug([KMLogs dataLog], "selectedKeyboard = %{public}@", _selectedKeyboard);
return _selectedKeyboard;
}
- (void)setSelectedKeyboard:(NSString *)selectedKeyboard {
[[KMSettingsRepository shared] writeSelectedKeyboard:selectedKeyboard];
_selectedKeyboard = selectedKeyboard;
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setObject:_selectedKeyboard forKey:kKMSelectedKeyboardKey];
[userData synchronize];
}
- (NSMutableArray *)activeKeyboards {
if (!_activeKeyboards) {
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
_activeKeyboards = [[userData arrayForKey:kKMActiveKeyboardsKey] mutableCopy];
if (!_activeKeyboards)
_activeKeyboards = [[NSMutableArray alloc] initWithCapacity:0];
os_log_debug([KMLogs dataLog], "initializing activeKeyboards");
_activeKeyboards = [[KMSettingsRepository.shared readActiveKeyboards] mutableCopy];
}
return _activeKeyboards;
}
- (void)saveActiveKeyboards {
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setObject:_activeKeyboards forKey:kKMActiveKeyboardsKey];
[userData synchronize];
os_log_debug([KMLogs dataLog], "saveActiveKeyboards");
[KMSettingsRepository.shared writeActiveKeyboards:_activeKeyboards];
[self resetActiveKeyboards];
[self updateKeyboardMenuItems];
}
- (void)clearActiveKeyboards {
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setObject:nil forKey:kKMActiveKeyboardsKey];
[userData synchronize];
[KMSettingsRepository.shared clearActiveKeyboards];
[self updateKeyboardMenuItems];
}
- (void)addActiveKeyboard:(NSString *) partialPath {
if (![self.activeKeyboards containsObject:partialPath]) {
os_log_debug([KMLogs keyboardLog], "addActiveKeyboard, adding '%{public}@' to list of active keyboards: ", partialPath);
[self.activeKeyboards addObject:partialPath];
}
}
- (void)resetActiveKeyboards {
// Remove entries with missing files
NSMutableArray *pathsToRemove = [[NSMutableArray alloc] initWithCapacity:0];
for (NSString *path in self.activeKeyboards) {
if (![[NSFileManager defaultManager] fileExistsAtPath:path])
NSString *fullPath = [KMDataRepository.shared buildFullPath:path];
os_log_debug([KMLogs dataLog], "resetActiveKeyboards, checking fullPath: '%{public}@' for path: '%{public}@'", fullPath, path);
if (![[NSFileManager defaultManager] fileExistsAtPath:fullPath]) {
os_log_debug([KMLogs dataLog], "resetActiveKeyboards, need to remove non-existent path: %{public}@", fullPath);
[pathsToRemove addObject:path];
}
}
BOOL found = FALSE;
@ -749,9 +680,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
}
if (found) {
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setObject:_activeKeyboards forKey:kKMActiveKeyboardsKey];
[userData synchronize];
[KMSettingsRepository.shared writeActiveKeyboards:_activeKeyboards];
}
}
@ -814,6 +743,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
}
- (int)calculateNumberOfKeyboardMenuItems {
os_log_debug([KMLogs uiLog], "calculateNumberOfKeyboardMenuItems, entered");
if (self.activeKeyboards.count == 0) {
// if there are no active keyboards, then we will insert one placeholder menu item 'No Active Keyboards'
return 1;
@ -835,6 +765,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
}
- (void)addDynamicKeyboardMenuItems {
os_log_debug([KMLogs startupLog], "addDynamicKeyboardMenuItems, entered");
BOOL didSetSelectedKeyboard = NO;
NSInteger itag = KEYMAN_FIRST_KEYBOARD_MENUITEM_TAG;
NSString *keyboardMenuName = @"";
@ -846,7 +777,9 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
// loop through the active keyboards list and add them to the menu
for (NSString *path in self.activeKeyboards) {
NSDictionary *infoDict = [KMXFile keyboardInfoFromKmxFile:path];
NSString *fullPath = [KMDataRepository.shared buildFullPath:path];
os_log_debug([KMLogs dataLog], "addDynamicKeyboardMenuItems, path = '%{public}@', full path = '%{public}@'", path, fullPath);
NSDictionary *infoDict = [KMXFile keyboardInfoFromKmxFile:fullPath];
if (!infoDict) {
continue;
}
@ -876,10 +809,12 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
- (void) setSelectedKeyboard:(NSString*)keyboardName inMenuItem:(NSMenuItem*) menuItem {
KVKFile *kvk = nil;
NSString *fullPath = [KMDataRepository.shared buildFullPath:keyboardName];
os_log_debug([KMLogs dataLog], "setSelectedKeyboard, keyboardName = '%{public}@', full path = '%{public}@'", keyboardName, fullPath);
[menuItem setState:NSOnState];
KMXFile *kmx = [[KMXFile alloc] initWithFilePath:keyboardName];
KMXFile *kmx = [[KMXFile alloc] initWithFilePath:fullPath];
[self setKmx:kmx];
NSDictionary *kmxInfo = [KMXFile keyboardInfoFromKmxFile:keyboardName];
NSDictionary *kmxInfo = [KMXFile keyboardInfoFromKmxFile:fullPath];
NSString *kvkFilename = [kmxInfo objectForKey:kKMVisualKeyboardKey];
if (kvkFilename != nil) {
NSString *kvkFilePath = [self kvkFilePathFromFilename:kvkFilename];
@ -890,7 +825,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
[self setKvk:kvk];
[self setKeyboardName:[kmxInfo objectForKey:kKMKeyboardNameKey]];
[self setKeyboardIcon:[kmxInfo objectForKey:kKMKeyboardIconKey]];
[self readPersistedOptions];
[self applyPersistedOptions];
}
// defaults to the whatever keyboard happens to be first in the list
@ -931,10 +866,13 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
}
NSString *path = [self.activeKeyboards objectAtIndex:tag-KEYMAN_FIRST_KEYBOARD_MENUITEM_TAG];
KMXFile *kmx = [[KMXFile alloc] initWithFilePath:path];
NSString *fullPath = [KMDataRepository.shared buildFullPath:path];
os_log_debug([KMLogs dataLog], "setSelectedKeyboard, keyboardName = '%{public}@', full path = '%{public}@'", path, fullPath);
KMXFile *kmx = [[KMXFile alloc] initWithFilePath:fullPath];
[self setKmx:kmx];
KVKFile *kvk = nil;
NSDictionary *kmxInfo = [KMXFile keyboardInfoFromKmxFile:path];
NSDictionary *kmxInfo = [KMXFile keyboardInfoFromKmxFile:fullPath];
NSString *kvkFilename = [kmxInfo objectForKey:kKMVisualKeyboardKey];
if (kvkFilename != nil) {
NSString *kvkFilePath = [self kvkFilePathFromFilename:kvkFilename];
@ -948,17 +886,17 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
[self setKeyboardIcon:[kmxInfo objectForKey:kKMKeyboardIconKey]];
[self setContextBuffer:nil];
[self setSelectedKeyboard:path];
[self readPersistedOptions];
[self applyPersistedOptions];
if (kvk != nil && self.alwaysShowOSK)
[self showOSK];
}
- (NSArray *)KMXFiles {
return [self KMXFilesAtPath:self.keyboardsPath];
- (NSArray *)getKmxFilesInKeyboardsDirectory {
return [self getKmxFilesAtPath:self.keyboardsPath];
}
- (NSArray *)KMXFilesAtPath:(NSString *)path {
os_log_debug([KMLogs dataLog], "Reading KMXFiles at path %{public}@", path);
- (NSArray *)getKmxFilesAtPath:(NSString *)path {
os_log_debug([KMLogs dataLog], "getKmxFilesAtPath, path: '%{public}@'", path);
NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:path];
NSMutableArray *kmxFiles = [[NSMutableArray alloc] initWithCapacity:0];
NSString *filePath;
@ -973,23 +911,23 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
return kmxFiles;
}
- (NSArray *)KVKFiles {
- (NSArray *)getKvkFilesArray {
NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:self.keyboardsPath];
NSMutableArray *kvkFiles = [[NSMutableArray alloc] initWithCapacity:0];
NSMutableArray *kvkFilesArray = [[NSMutableArray alloc] initWithCapacity:0];
NSString *filePath;
while (filePath = (NSString *)[dirEnum nextObject]) {
NSString *extension = [[filePath pathExtension] lowercaseString];
if ([extension isEqualToString:@"kvk"])
[kvkFiles addObject:[self.keyboardsPath stringByAppendingPathComponent:filePath]];
[kvkFilesArray addObject:[self.keyboardsPath stringByAppendingPathComponent:filePath]];
}
return kvkFiles;
return kvkFilesArray;
}
- (NSString *)kvkFilePathFromFilename:(NSString *)kvkFilename {
NSString *kvkFilePath = nil;
NSArray *kvkFiles = [self KVKFiles];
for (NSString *filePath in kvkFiles) {
NSArray *kvkFilesArray = [self getKvkFilesArray];
for (NSString *filePath in kvkFilesArray) {
if ([[filePath lastPathComponent] isEqualToString:kvkFilename]) {
kvkFilePath = filePath;
break;
@ -1338,7 +1276,8 @@ extern const CGKeyCode kProcessPendingBuffer;
NSError *error = nil;
NSString *fileName = filePath.lastPathComponent;
NSString *folderName = [fileName stringByDeletingPathExtension];
os_log_debug([KMLogs keyboardLog], "unzipFile, folderName: %{public}@, fileName: %{public}@", folderName, fileName);
// First we unzip into a temp folder, and check kmp.json for the fileVersion
// before we continue installation. We don't want to overwrite existing
// package if it is there if the files are not compatible with the installed
@ -1349,9 +1288,9 @@ extern const CGKeyCode kProcessPendingBuffer;
ZipArchive *za = [[ZipArchive alloc] init];
if ([za UnzipOpenFile:filePath]) {
os_log_debug([KMLogs keyboardLog], "Unzipping %{public}@ to %{public}@", filePath, tempDestFolder);
os_log_debug([KMLogs keyboardLog], "unzipFile, Unzipping %{public}@ to %{public}@", filePath, tempDestFolder);
if ([[NSFileManager defaultManager] fileExistsAtPath:tempDestFolder]) {
os_log_debug([KMLogs keyboardLog], "The temp destination folder already exists. Overwriting...");
os_log_debug([KMLogs keyboardLog], "unzipFile, The temp destination folder already exists. Overwriting...");
}
didUnzip = [za UnzipFileTo:tempDestFolder overWrite:YES];
@ -1359,11 +1298,11 @@ extern const CGKeyCode kProcessPendingBuffer;
}
if (!didUnzip) {
os_log_error([KMLogs keyboardLog], "Failed to unzip file: %{public}@", filePath);
os_log_error([KMLogs keyboardLog], "unzipFile, Failed to unzip file: %{public}@", filePath);
return NO;
}
os_log_debug([KMLogs keyboardLog], "Unzipped file: %{public}@", filePath);
os_log_debug([KMLogs keyboardLog], "unzipFile, Unzipped file: %{public}@", filePath);
BOOL didInstall = [self verifyPackageVersionInTempFolder:tempDestFolder filePath:filePath];
@ -1371,10 +1310,10 @@ extern const CGKeyCode kProcessPendingBuffer;
// Remove existing package if it exists
if (didInstall && [[NSFileManager defaultManager] fileExistsAtPath:destFolder]) {
os_log_debug([KMLogs keyboardLog], "The destination folder already exists. Overwriting...");
os_log_debug([KMLogs keyboardLog], "unzipFile, The destination folder already exists. Overwriting...");
[[NSFileManager defaultManager] removeItemAtPath:destFolder error:&error];
if (error != nil) {
os_log_error([KMLogs keyboardLog], "Unable to remove destination folder %{public}@", destFolder);
os_log_error([KMLogs keyboardLog], "unzipFile, Unable to remove destination folder %{public}@", destFolder);
didInstall = NO;
}
}
@ -1387,7 +1326,7 @@ extern const CGKeyCode kProcessPendingBuffer;
if(didInstall) {
[[NSFileManager defaultManager] moveItemAtPath:tempDestFolder toPath:destFolder error:&error];
if (error != nil) {
os_log_error([KMLogs keyboardLog], "Unable to move temp folder %{public}@ to dest folder %{public}@", tempDestFolder, destFolder);
os_log_error([KMLogs keyboardLog], "unzipFile, Unable to move temp folder %{public}@ to dest folder %{public}@", tempDestFolder, destFolder);
didInstall = NO;
}
}
@ -1395,7 +1334,7 @@ extern const CGKeyCode kProcessPendingBuffer;
if(!didInstall) {
[[NSFileManager defaultManager] removeItemAtPath:tempDestFolder error:&error];
if (error != nil) {
os_log_error([KMLogs keyboardLog], "Unable to remove temp folder %{public}@", tempDestFolder);
os_log_error([KMLogs keyboardLog], "unzipFile, Unable to remove temp folder %{public}@", tempDestFolder);
}
return NO;
@ -1404,11 +1343,12 @@ extern const CGKeyCode kProcessPendingBuffer;
// Package has installed, now scan for keyboards and fonts
// TODO: we need to be reading the kmp.json data to determine keyboards to install
NSString * keyboardFolderPath = [self.keyboardsPath stringByAppendingPathComponent:folderName];
os_log_debug([KMLogs keyboardLog], "unzipFile, folderName: %{public}@, keyboardFolderPath: %{public}@", folderName, keyboardFolderPath);
[self installFontsAtPath:keyboardFolderPath];
for (NSString *kmxFile in [self KMXFilesAtPath:keyboardFolderPath]) {
os_log_debug([KMLogs keyboardLog], "Adding keyboard to list of active keyboards: %{public}@", kmxFile);
if (![self.activeKeyboards containsObject:kmxFile])
[self.activeKeyboards addObject:kmxFile];
for (NSString *kmxFile in [self getKmxFilesAtPath:keyboardFolderPath]) {
NSString *partialPath = [KMDataRepository.shared buildPartialPathFrom:folderName keyboardFile:[kmxFile lastPathComponent]];
[self addActiveKeyboard:partialPath];
}
[self saveActiveKeyboards];

View file

@ -418,7 +418,7 @@ NSString* const kEasterEggKmxName = @"EnglishSpanish.kmx";
NSString *value = [options objectForKey:key];
if(key && value) {
os_log_debug([KMLogs keyLog], "persistOptions, key: %{public}@, value: %{public}@", key, value);
[self.appDelegate writePersistedOptions:key withValue:value];
[[KMSettingsRepository shared] writeOptionForSelectedKeyboard:key withValue:value];
}
else {
os_log_debug([KMLogs keyLog], "invalid values in persistOptions, not writing to UserDefaults, key: %{public}@, value: %{public}@", key, value);

View file

@ -19,8 +19,6 @@ NS_ASSUME_NONNULL_BEGIN
@interface KMPackageReader : NSObject
@property (assign, nonatomic) BOOL debugMode;
- (instancetype)init;
- (KMPackageInfo *)loadPackageInfo:(NSString *)path;

View file

@ -44,9 +44,6 @@ typedef enum {
- (instancetype)init {
self = [super init];
if (self) {
_debugMode = NO;
}
return self;
}

View file

@ -17,6 +17,17 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)dataMigrationNeeded;
- (void)convertSettingsForMigration;
- (void)setDataModelVersionIfNecessary;
- (NSString *)readSelectedKeyboard;
- (void)writeSelectedKeyboard:(NSString *)selectedKeyboard;
- (NSArray *)readActiveKeyboards;
- (void)writeActiveKeyboards: (NSArray *) keyboards;
- (void)clearActiveKeyboards;
- (NSDictionary *)readOptionsForSelectedKeyboard;
- (void)writeOptionForSelectedKeyboard:(NSString *)key withValue:(NSString*)value;
- (BOOL)readAlwaysShowOsk;
- (void)writeAlwaysShowOsk:(BOOL)alwaysShowOsk;
- (BOOL)readUseVerboseLogging;
- (void)writeUseVerboseLogging:(BOOL)verboseLogging;
@end
NS_ASSUME_NONNULL_END

View file

@ -9,12 +9,24 @@
#import "KMSettingsRepository.h"
#import "KMLogs.h"
#import "KMDataRepository.h"
NSString *const kActiveKeyboardsKey = @"KMActiveKeyboardsKey";
NSString *const kSelectedKeyboardKey = @"KMSelectedKeyboardKey";
NSString *const kPersistedOptionsKey = @"KMPersistedOptionsKey";
NSString *const kAlwaysShowOSKKey = @"KMAlwaysShowOSKKey";
NSString *const kUseVerboseLogging = @"KMUseVerboseLogging";
NSString *const kObsoletePathComponent = @"/Documents/";
/**
* The following constant "KMSavedStoresKey" is left here for documentation
* though we have abandoned stores written to UserDefaults with this key because
* they used a less-reliable numeric key prior to integration with Keyman Core.
* It is replaced by the renamed "KMPersistedOptionsKey" which directly
* represents what it is saving.
*/
NSString *const kKMDeprecatedPersistedOptionsKey = @"KMSavedStoresKey";
NSString *const kObsoletePathComponent = @"/Documents/Keyman-Keyboards";
NSString *const kNewPathComponent = @"/Library/Application Support/keyman.inputmethod.Keyman/";
/**
@ -49,8 +61,46 @@ NSInteger const kCurrentDataModelVersionNumber = kVersionStoreDataInLibraryDirec
* If this method is called after applicationDidFinishLaunching, then it will always return true.
* If called from awakeFromNib, then it will return false when running for the first time.
*/
- (BOOL)settingsExist {
return [[NSUserDefaults standardUserDefaults] objectForKey:kSelectedKeyboardKey] != nil;
- (BOOL)settingsExist
{
return ([[NSUserDefaults standardUserDefaults] objectForKey:kSelectedKeyboardKey] != nil);
}
- (void)writeOptionForSelectedKeyboard:(NSString *)key withValue:(NSString*)value {
NSDictionary *optionsMap = [self readOptionsForSelectedKeyboard];
NSDictionary *newOptionsMap = nil;
// if we can read an existing options map, then add the specified key-value pair
if (optionsMap != nil) {
NSMutableDictionary *mutableOptionsMap = [optionsMap mutableCopy];
[mutableOptionsMap setObject:value forKey:key];
newOptionsMap = mutableOptionsMap;
} else {
// if no options map exists, create a new one add the specified key-value pair
newOptionsMap = [[NSDictionary alloc] initWithObjectsAndKeys:value, key, nil];
}
// write the options map for the selected keyboard to the dictionary of options
NSString *selectedKeyboard = [self readSelectedKeyboard];
os_log_info([KMLogs dataLog], "writeOptionForSelectedKeyboard, adding options map: %{public}@, to keyboard %{public}@", newOptionsMap, selectedKeyboard);
[self writeKeyboardOptionsMap: selectedKeyboard withOptions:newOptionsMap];
}
- (void)writeKeyboardOptionsMap:(NSString *)keyboardName withOptions:(NSDictionary*) optionsMap {
NSMutableDictionary *newFullOptionsMap = nil;
os_log_debug([KMLogs dataLog], "writeKeyboardOptionsMap, adding options map: %{public}@, to keyboard %{public}@", optionsMap, keyboardName);
NSDictionary *fullOptionsMap = [self readFullOptionsMap];
// if we can read the existing full options map, then add for the specified keyboard
if (fullOptionsMap != nil) {
newFullOptionsMap = [fullOptionsMap mutableCopy];
[newFullOptionsMap setObject:optionsMap forKey:keyboardName];
} else {
// otherwise, create the full options map and add for the specified keyboard
newFullOptionsMap = [[NSMutableDictionary alloc] initWithObjectsAndKeys:optionsMap, keyboardName, nil];
}
[self writeFullOptionsMap:newFullOptionsMap];
}
/**
@ -83,11 +133,11 @@ NSInteger const kCurrentDataModelVersionNumber = kVersionStoreDataInLibraryDirec
return migrationNeeded;
}
- (NSString *)selectedKeyboard {
- (NSString *)readSelectedKeyboard {
return [[NSUserDefaults standardUserDefaults] objectForKey:kSelectedKeyboardKey];
}
- (void)saveSelectedKeyboard:(NSString *)selectedKeyboard {
- (void)writeSelectedKeyboard:(NSString *)selectedKeyboard {
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setObject:selectedKeyboard forKey:kSelectedKeyboardKey];
}
@ -101,63 +151,116 @@ NSInteger const kCurrentDataModelVersionNumber = kVersionStoreDataInLibraryDirec
return activeKeyboards;
}
- (NSDictionary *)persistedOptions {
- (NSArray *)readActiveKeyboards {
os_log_debug([KMLogs dataLog], "KMSettingsRepository readActiveKeyboards");
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
NSArray *keyboardsArray = [userData arrayForKey:kActiveKeyboardsKey];
// if the kActiveKeyboardsKey does not exist, then create an empty array
if (!keyboardsArray) {
os_log_debug([KMLogs dataLog], "KMActiveKeyboardsKey key not found in NSUserDefualts");
keyboardsArray = [[NSArray alloc] init];
}
return keyboardsArray;
}
- (void)writeActiveKeyboards: (NSArray *) keyboards {
os_log_debug([KMLogs dataLog], "KMSettingsRepository writeActiveKeyboards");
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setObject:keyboards forKey:kActiveKeyboardsKey];
}
- (void)clearActiveKeyboards {
os_log_debug([KMLogs dataLog], "KMSettingsRepository clearActiveKeyboards");
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setObject:nil forKey:kActiveKeyboardsKey];
}
/**
* returns dictionary of persisted options for the single selected keyboard
*/
- (NSDictionary *)readOptionsForSelectedKeyboard {
NSDictionary *optionsMap = [self readFullOptionsMap];
NSString *selectedKeyboard = [self readSelectedKeyboard];
NSDictionary *selectedOptionsMap = [optionsMap objectForKey: selectedKeyboard];
if (selectedOptionsMap == nil) {
os_log_info([KMLogs dataLog], "no persisted options found in UserDefaults for keyboard %{public}@ ", selectedKeyboard);
} else {
for (NSString *key in selectedOptionsMap) {
NSString *value = [selectedOptionsMap objectForKey:key];
os_log_info([KMLogs dataLog], "option for keyboard %{public}@ key: %{public}@, value %{public}@", selectedKeyboard, key, value);
}
}
return selectedOptionsMap;
}
/**
* returns dictionary of all persisted options for all keyboards
* (options are stored in UserDefaults as a map of maps)
*/
- (NSDictionary *)readFullOptionsMap {
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
return [userData dictionaryForKey:kPersistedOptionsKey];
}
- (void)savePersistedOptions:(NSDictionary *) optionsDictionary {
- (void)writeFullOptionsMap:(NSDictionary *) fullOptionsMap {
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setObject:optionsDictionary forKey:kPersistedOptionsKey];
[userData setObject:fullOptionsMap forKey:kPersistedOptionsKey];
}
- (void)removePersistedOptions {
- (void)removeAllOptions {
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
return [userData removeObjectForKey:kPersistedOptionsKey];
}
- (void)convertSettingsForMigration {
os_log_debug([KMLogs dataLog], "converting settings in UserDefaults for migration");
[self convertSelectedKeyboardPathForMigration];
[self convertActiveKeyboardArrayForMigration];
[self convertPersistedOptionsPathsForMigration];
[self convertOptionsPathsForMigration];
}
- (void)convertSelectedKeyboardPathForMigration {
NSString *selectedKeyboardPath = [self selectedKeyboard];
NSString *selectedKeyboardPath = [self readSelectedKeyboard];
if (selectedKeyboardPath != nil) {
NSString *newPathString = [self convertOldKeyboardPath:selectedKeyboardPath];
NSString *newPathString = [self trimObsoleteKeyboardPath:selectedKeyboardPath];
if ([selectedKeyboardPath isNotEqualTo:newPathString]) {
[self saveSelectedKeyboard:newPathString];
os_log([KMLogs dataLog], "converted selected keyboard setting from '%{public}@' to '%{public}@'", selectedKeyboardPath, newPathString);
[self writeSelectedKeyboard:newPathString];
os_log_debug([KMLogs dataLog], "converted selected keyboard setting from '%{public}@' to '%{public}@'", selectedKeyboardPath, newPathString);
}
}
}
/**
* Convert the path of the keyboard designating the Documents folder to its new location
* in the Application Support folder
* To convert the keyboard path for the new location, just trim the parent directory from the path
* No need to repeatedly store the parent directory with the path of each keyboard
* If the old directory is not found in the string, then return the string unchanged
*/
- (NSString *)convertOldKeyboardPath:(NSString *)oldPath {
NSString *newPathString = @"";
- (NSString *)trimObsoleteKeyboardPath:(NSString *)oldPath {
NSString *newPath = oldPath;
if(oldPath != nil) {
newPathString = [oldPath stringByReplacingOccurrencesOfString:kObsoletePathComponent withString:kNewPathComponent];
NSRange range = [oldPath rangeOfString:kObsoletePathComponent];
if (range.length > 0) {
newPath = [oldPath substringFromIndex:range.location + range.length];
os_log_debug([KMLogs dataLog], "trimmed keyboard path from '%{public}@' to '%{public}@'", oldPath, newPath);
}
}
return newPathString;
return newPath;
}
- (void)convertActiveKeyboardArrayForMigration {
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
NSMutableArray *activeKeyboards = [self activeKeyboards];
NSMutableArray *keyboards = [self activeKeyboards];
NSMutableArray *convertedActiveKeyboards = [[NSMutableArray alloc] initWithCapacity:0];
BOOL didConvert = NO;
for (NSString *oldPath in activeKeyboards) {
NSString *newPath = [self convertOldKeyboardPath:oldPath];
for (NSString *oldPath in keyboards) {
NSString *newPath = [self trimObsoleteKeyboardPath:oldPath];
if ([oldPath isNotEqualTo:newPath]) {
[convertedActiveKeyboards addObject:newPath];
os_log([KMLogs dataLog], "converted active keyboard from old path '%{public}@' to '%{public}@'", oldPath, newPath);
os_log_debug([KMLogs dataLog], "converted active keyboard from old path '%{public}@' to '%{public}@'", oldPath, newPath);
// if we have adjusted at least one path, set flag
didConvert = YES;
} else {
@ -172,8 +275,8 @@ NSInteger const kCurrentDataModelVersionNumber = kVersionStoreDataInLibraryDirec
}
}
- (void)convertPersistedOptionsPathsForMigration {
NSDictionary * optionsMap = [self persistedOptions];
- (void)convertOptionsPathsForMigration {
NSDictionary * optionsMap = [self readFullOptionsMap];
NSMutableDictionary *mutableOptionsMap = nil;
BOOL optionsChanged = NO;
@ -184,7 +287,7 @@ NSInteger const kCurrentDataModelVersionNumber = kVersionStoreDataInLibraryDirec
os_log_info([KMLogs configLog], "persisted options found in UserDefaults with key = %{public}@", key);
}
for (NSString *key in optionsMap) {
NSString *newPathString = [self convertOldKeyboardPath:key];
NSString *newPathString = [self trimObsoleteKeyboardPath:key];
NSDictionary *optionsValue = [optionsMap objectForKey:key];
if ([key isNotEqualTo:newPathString]) {
@ -192,16 +295,36 @@ NSInteger const kCurrentDataModelVersionNumber = kVersionStoreDataInLibraryDirec
// insert options into new map with newly converted path as key
[mutableOptionsMap setObject:optionsValue forKey:newPathString];
os_log([KMLogs dataLog], "converted option key from '%{public}@' to '%{public}@'", key, newPathString);
os_log_debug([KMLogs dataLog], "converted option key from '%{public}@' to '%{public}@'", key, newPathString);
} else {
// retain options that did not need converting
[mutableOptionsMap setObject:optionsValue forKey:key];
}
}
if (optionsChanged) {
[self savePersistedOptions:mutableOptionsMap];
[self writeFullOptionsMap:mutableOptionsMap];
}
}
}
- (BOOL)readAlwaysShowOsk {
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
return [userData boolForKey:kAlwaysShowOSKKey];
}
- (void)writeAlwaysShowOsk:(BOOL)alwaysShowOsk {
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setBool:alwaysShowOsk forKey:kAlwaysShowOSKKey];
}
- (BOOL)readUseVerboseLogging {
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
return [userData boolForKey:kUseVerboseLogging];
}
- (void)writeUseVerboseLogging:(BOOL)verboseLogging {
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setBool:verboseLogging forKey:kUseVerboseLogging];
}
@end