mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-06 08:55:34 +00:00
4099 lines
192 KiB
Objective-C
4099 lines
192 KiB
Objective-C
//
|
|
// KMManager.m
|
|
// Keyman Engine
|
|
//
|
|
// Created by Patrick Weekes on 12-05-16.
|
|
// Updated by Serkan Kurt on 17/05/2013.
|
|
// Copyright (c) 2017 SIL International. All rights reserved.
|
|
//
|
|
|
|
#import "KMManager.h"
|
|
#import "KMManager+Internal.h"
|
|
#import "KMInputViewController.h"
|
|
#import "KMURLProtocol.h"
|
|
#import "KMTextField.h"
|
|
#import "KMTextView.h"
|
|
#import "KMPopoverView.h"
|
|
#import "KMKeyPreviewView.h"
|
|
#import "KMSubKeysView.h"
|
|
#import "KMKeyboardMenuView.h"
|
|
#import "KeyboardPickerViewController.h"
|
|
#import "UIView+Helpers.h"
|
|
#import <QuartzCore/QuartzCore.h>
|
|
#import <CoreText/CoreText.h>
|
|
#import <CommonCrypto/CommonDigest.h>
|
|
#import "UIButton+Helpers.h"
|
|
#import <objc/message.h>
|
|
|
|
#pragma mark - Constants
|
|
|
|
static BOOL enableWebKit = YES;
|
|
|
|
// Keyman system-wide keyboard
|
|
static BOOL kIsKeymanSystemKeyboard = NO;
|
|
|
|
// Group Identifier
|
|
static NSString *applicationGroupIdentifier = @"";
|
|
|
|
// Notifications
|
|
NSString *const kKeymanLanguagesUpdatedNotification = @"KeymanLanguagesUpdatedNotification";
|
|
NSString *const kKeymanLanguagesDownloadFailedNotification = @"kKeymanLanguagesDownloadFailedNotification";
|
|
NSString *const kKeymanKeyboardDownloadStartedNotification = @"kKeymanKeyboardDownloadStartedNotification";
|
|
NSString *const kKeymanKeyboardDownloadCompletedNotification = @"kKeymanKeyboardDownloadCompletedNotification";
|
|
NSString *const kKeymanKeyboardDownloadFailedNotification = @"kKeymanKeyboardDownloadFailedNotification";
|
|
NSString *const kKeymanKeyboardChangedNotification = @"kKeymanKeyboardChangedNotification";
|
|
NSString *const kKeymanKeyboardLoadedNotification = @"kKeymanKeyboardLoadedNotification";
|
|
NSString *const kKeymanKeyboardPickerDismissedNotification = @"kKeymanKeyboardPickerDismissedNotification";
|
|
NSString *const kKeymanKeyboardRemovedNotification = @"kKeymanKeyboardRemovedNotification";
|
|
NSString *const kKeymanSubKeysMenuWillShowNotification = @"kKeymanSubKeysMenuWillShowNotification";
|
|
NSString *const kKeymanSubKeysMenuDismissedNotification = @"kKeymanSubKeysMenuDismissedNotification";
|
|
NSString *const kKeymanDebugLogNotification = @"kKeymanDebugLogNotification";
|
|
|
|
// External dictionary keys
|
|
NSString *const kKeymanIdKey = @"id";
|
|
NSString *const kKeymanNameKey = @"name";
|
|
NSString *const kKeymanKeyboardIdKey = @"kbId";
|
|
NSString *const kKeymanLanguageIdKey = @"langId";
|
|
NSString *const kKeymanKeyboardNameKey = @"kbName";
|
|
NSString *const kKeymanLanguageNameKey = @"langName";
|
|
NSString *const kKeymanKeyboardVersionKey = @"version";
|
|
NSString *const kKeymanKeyboardKey = @"keyboard";
|
|
NSString *const kKeymanLanguageKeyboardsKey = @"keyboards";
|
|
NSString *const kKeymanKeyboardFileSizeKey = @"fileSize";
|
|
NSString *const kKeymanKeyboardGZipFileSizeKey = @"fileSizeGzip";
|
|
NSString *const kKeymanFontKey = @"font";
|
|
NSString *const kKeymanOskFontKey = @"oskFont";
|
|
NSString *const kKeymanFontFamilyKey = @"family";
|
|
NSString *const kKeymanFontSourceKey = @"source";
|
|
NSString *const kKeymanFontFilesKey = @"files";
|
|
NSString *const kKeymanFontFilenameKey = @"filename"; // Font filename is deprecated
|
|
NSString *const kKeymanFontNameKey = @"fontname";
|
|
NSString *const kKeymanFontRegisteredKey = @"fontregistered";
|
|
NSString *const kKeymanKeyboardFilenameKey = @"filename";
|
|
NSString *const kKeymanKeyboardModifiedKey = @"lastModified";
|
|
NSString *const kKeymanKeyboardRTLKey = @"rtl";
|
|
NSString *const kKeymanKeyboardInfoKey = @"keyboardInfo";
|
|
NSString *const kKeymanCustomKeyboardKey = @"CustomKeyboard";
|
|
|
|
// External user defaults keys
|
|
NSString *const kKeymanUserKeyboardsListKey = @"UserKeyboardsList";
|
|
NSString *const kKeymanUserCurrentKeyboardKey = @"UserCurrentKeyboard";
|
|
// Internal user defaults keys
|
|
NSString *const kKeymanEngineVersion = @"KeymanEngineVersion";
|
|
NSString *const kKeymanKeyboardPickerDisplayedKey = @"KeyboardPickerDisplayed"; // stores YES if the keyboard picker has been displayed at least once > BOOL
|
|
NSString *const kKeymanSynchronizeSWKeyboardKey = @"KeymanSynchronizeSWKeyboard";
|
|
|
|
// Views
|
|
static id webView = nil;
|
|
static KMPopoverView *helpBubbleView = nil;
|
|
static KMKeyPreviewView *keyPreviewView = nil;
|
|
static KMSubKeysView *subKeysView = nil;
|
|
static KMKeyboardMenuView *keyboardMenuView = nil;
|
|
|
|
// Arrays
|
|
static NSMutableArray *subKeyIDs = nil;
|
|
static NSMutableArray *subKeyTexts = nil;
|
|
static NSMutableArray *subKeys = nil;
|
|
|
|
// Key frames
|
|
static CGRect keyFrame;
|
|
static CGRect menuKeyFrame;
|
|
|
|
// Dictionaries
|
|
NSMutableDictionary *options_ = nil;
|
|
|
|
// Strings
|
|
static NSString *const kKeymanKeyboardChangeHelpText = @"Tap here to change keyboard";
|
|
static NSString *specialOSKFont = nil;
|
|
|
|
// URLs and Filenames
|
|
NSString *const kKeymanApiBaseURL = @"https://r.keymanweb.com/api/3.0/";
|
|
static NSString *const kKeymanApiRemoteURL = @"https://r.keymanweb.com/api/2.0/remote?url=";
|
|
static NSString *const kKeymanFileName = @"keyboard";
|
|
static NSString *const kKeymanFileExtension = @"html";
|
|
static NSString *const kKeymaniOSCodeFileName = @"keymanios.js";
|
|
static NSString *const kKeymanHostName = @"r.keymanweb.com";
|
|
#define kKeymanFullFileName [NSString stringWithFormat:@"%@.%@", kKeymanFileName, kKeymanFileExtension]
|
|
|
|
// External Default Keyboard Info
|
|
NSString *const kKeymanDefaultKeyboardID = @"european2";
|
|
NSString *const kKeymanDefaultLanguageID = @"eng";
|
|
NSString *const kKeymanDefaultKeyboardName = @"EuroLatin2 Keyboard";
|
|
NSString *const kKeymanDefaultLanguageName = @"English";
|
|
NSString *const kKeymanDefaultKeyboardRTL = @"N";
|
|
NSString *const kKeymanDefaultKeyboardFont = @"{\"family\":\"LatinWeb\",\"files\":[\"DejaVuSans.ttf\",\"DejaVuSans.mobileconfig\"]}";
|
|
|
|
// JSON keys for language REST calls
|
|
NSString *const kKeymanOptionsKey = @"options";
|
|
NSString *const kKeymanLanguageKey = @"language";
|
|
// *** TO DO: Check if it matches with the key in Keyman Cloud API ***
|
|
NSString *const kKeymanKeyboardCopyrightKey = @"copyright";
|
|
//
|
|
static NSString *const kKeymanLanguagesKey = @"languages";
|
|
static NSString *const kKeymanKeyboardBaseURIKey = @"keyboardBaseUri";
|
|
static NSString *const kKeymanFontBaseURIKey = @"fontBaseUri";
|
|
static NSString *const kKeymanKeyboardURIKey = @"uri";
|
|
|
|
// Other keys
|
|
static NSString *const kKeymanUpdateKey = @"update";
|
|
|
|
// UI In-App Keyboard Constants
|
|
static CGFloat kKeymanPhonePortraitInAppKeyboardHeight = 183.0f;
|
|
static CGFloat kKeymanPhoneLandscapeInAppKeyboardHeight = 183.0f;
|
|
static CGFloat kKeymanPadPortraitInAppKeyboardHeight = 385.0f;
|
|
static CGFloat kKeymanPadLandscapeInAppKeyboardHeight = 385.0f;
|
|
|
|
// UI System Keyboard Constants
|
|
static CGFloat kKeymanPhonePortraitSystemKeyboardHeight = 216.0f;
|
|
static CGFloat kKeymanPhoneLandscapeSystemKeyboardHeight = 162.0f;
|
|
static CGFloat kKeymanPadPortraitSystemKeyboardHeight = 264.0f;
|
|
static CGFloat kKeymanPadLandscapeSystemKeyboardHeight = 352.0f;
|
|
|
|
// Sub key colors
|
|
static UIColor *subKeyColor;
|
|
static UIColor *subKeyColorHighlighted;
|
|
|
|
BOOL didResizeToOrientation = NO;
|
|
|
|
#pragma mark - Private Interface
|
|
|
|
@interface KMManager ()
|
|
// Keyboard Info
|
|
+ (CGFloat)keyboardHeight;
|
|
+ (CGFloat)keyboardHeightWithOrientation:(UIInterfaceOrientation)orientation;
|
|
+ (CGFloat)keyboardWidth;
|
|
|
|
// Local file storage
|
|
- (void)copyWebFilesToLibrary;
|
|
- (NSString *)defaultKeymanDirectory;
|
|
- (NSString *)defaultLanguageDirectory;
|
|
- (NSString *)defaultFontDirectory;
|
|
- (NSString *)keyboardPathForID:(NSString *)keyboardID keyboardVersion:(NSString *)kbVersion;
|
|
|
|
// Keyman interaction
|
|
- (void)resizeKeyboard;
|
|
- (UIScrollView *)keymanScrollView;
|
|
|
|
// Misc
|
|
- (void)createKeyboardsInfo;
|
|
//- (NSDate *)dateFromKeymanDateString:(NSString *)dateString; // No longer used
|
|
@end
|
|
|
|
#pragma mark - Implementation
|
|
|
|
@implementation KMManager
|
|
|
|
CWL_SYNTHESIZE_SINGLETON_FOR_CLASS_WITH_ACCESSOR(KMManager, sharedInstance);
|
|
|
|
@synthesize debugPrintingOn = debugPrintingOn_;
|
|
@synthesize keymanHelpOn = keymanHelpOn_;
|
|
@synthesize canAddNewKeyboards = canAddNewKeyboards_;
|
|
@synthesize canRemoveKeyboards = canRemoveKeyboards_;
|
|
@synthesize canRemoveDefaultKeyboard = canRemoveDefaultKeyboard_;
|
|
@synthesize languageID = languageID_;
|
|
@synthesize keyboardID = keyboardID_;
|
|
@synthesize webDelegate = webDelegate_;
|
|
@synthesize inputDelegate = inputDelegate_;
|
|
@synthesize downloadQueue = downloadQueue_;
|
|
@synthesize sharedQueue = sharedQueue_;
|
|
@synthesize currentRequest = currentRequest_;
|
|
@synthesize reachability = reachability_;
|
|
@synthesize languages = languages_;
|
|
@synthesize options = options_;
|
|
@synthesize keyboardsInfo = keyboardsInfo_;
|
|
@synthesize keyboardsDictionary = keyboardsDictionary_;
|
|
@synthesize keymanFonts = keymanFonts_;
|
|
@synthesize shouldReloadKeyboard;
|
|
@synthesize didSynchronize;
|
|
|
|
#pragma mark - Object Admin
|
|
|
|
- (void)dealloc {
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
if(currentRequest_.userInfo) {
|
|
[currentRequest_.userInfo setValue:nil forKey:@"completionBlock"];
|
|
}
|
|
webView = nil;
|
|
subKeys = nil;
|
|
}
|
|
|
|
- (id)init {
|
|
if (self = [super init]) {
|
|
self.debugPrintingOn = NO;
|
|
self.keymanHelpOn = YES;
|
|
self.canAddNewKeyboards = YES;
|
|
self.canRemoveKeyboards = YES;
|
|
self.canRemoveDefaultKeyboard = NO;
|
|
subKeyColor = [UIColor colorWithRed:244.0/255.0 green:244.0/255.0 blue:244.0/255.0 alpha:1.0];
|
|
subKeyColorHighlighted = [UIColor colorWithRed:136.0/255.0 green:136.0/255.0 blue:255.0/255.0 alpha:1.0];
|
|
|
|
[NSURLProtocol registerClass:[KMURLProtocol class]];
|
|
NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];
|
|
NSDictionary *extensionInfo = [infoDict objectForKey:@"NSExtension"];
|
|
NSString *extensionID = [extensionInfo objectForKey:@"NSExtensionPointIdentifier"];
|
|
if ([extensionID isEqualToString:@"com.apple.keyboard-service"])
|
|
kIsKeymanSystemKeyboard = YES;
|
|
|
|
int iosVersion = [[[UIDevice currentDevice] systemVersion] intValue];
|
|
if (iosVersion < 9)
|
|
enableWebKit = NO;
|
|
|
|
if (!kIsKeymanSystemKeyboard) {
|
|
[self copyUserDefaultsToSharedContainer];
|
|
[self copyKeymanFilesToSharedContainer];
|
|
|
|
NSUserDefaults *userData = [self activeUserDefaults];
|
|
BOOL isKPDisplayed = [userData boolForKey:kKeymanKeyboardPickerDisplayedKey];
|
|
if (isKPDisplayed) {
|
|
self.keymanHelpOn = NO;
|
|
}
|
|
}
|
|
else {
|
|
self.keymanHelpOn = NO;
|
|
}
|
|
|
|
[self copyWebFilesToLibrary];
|
|
[self renameOldKeyboardFiles];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
|
|
|
|
/* No longer used
|
|
dateFormatter_ = [[NSDateFormatter alloc] init];
|
|
[self.dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZ"];
|
|
[self.dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
|
|
*/
|
|
|
|
NSString *kbVersion = [self latestKeyboardFileVersionWithID:kKeymanDefaultKeyboardID];
|
|
[self updateKeyboardVersionForID:kKeymanDefaultKeyboardID newKeyboardVersion:kbVersion];
|
|
|
|
[[self class] inputView]; // Pre-load keyboard
|
|
|
|
// Set UILongPressGestureRecognizer to show sub keys
|
|
UILongPressGestureRecognizer *hold = [[UILongPressGestureRecognizer alloc]
|
|
initWithTarget:self
|
|
action:@selector(holdAction:)];
|
|
hold.minimumPressDuration = 0.5f;
|
|
hold.delegate = (id)self;
|
|
[[[self class] inputView] addGestureRecognizer:hold];
|
|
|
|
self.reachability = [Reachability reachabilityWithHostName:kKeymanHostName];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityChanged:) name:kReachabilityChangedNotification object:self.reachability];
|
|
[self.reachability startNotifier];
|
|
|
|
/* KMHTTPDownloader only uses this for its delegate methods. So long as we don't
|
|
* set the queue running, this should be perfectly fine.
|
|
*/
|
|
self.sharedQueue = [[KMHTTPDownloader alloc] init:self];
|
|
|
|
[self registerCustomFonts];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
+ (BOOL)isKeymanSystemKeyboard {
|
|
return kIsKeymanSystemKeyboard;
|
|
}
|
|
|
|
+ (BOOL)isWebKitEnabled {
|
|
return (NSClassFromString(@"WKWebView") && enableWebKit);
|
|
}
|
|
|
|
#pragma mark - Keyboard Notifications
|
|
|
|
- (void)keyboardWillShow:(NSNotification *)notification {
|
|
[self dismissSubKeys]; // dismiss sub keys window
|
|
[self dismissKeyPreview]; // dismiss key preview window
|
|
|
|
[self resizeKeyboard];
|
|
|
|
if (self.keymanHelpOn) {
|
|
[helpBubbleView removeFromSuperview];
|
|
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(showHelpBubble) object:nil];
|
|
[self performSelector:@selector(showHelpBubble) withObject:nil afterDelay:1.5];
|
|
}
|
|
}
|
|
|
|
- (void)keyboardWillHide:(NSNotification *)notification {
|
|
[self dismissHelpBubble];
|
|
[self dismissSubKeys]; // dismiss sub keys view
|
|
[self dismissKeyPreview]; // dismiss key preview window
|
|
}
|
|
|
|
#pragma mark - Public Methods
|
|
|
|
- (BOOL)setKeyboardWithID:(NSString *)keyboardID languageID:(NSString *)languageID {
|
|
if (languageID != languageID_ || keyboardID != keyboardID_) {
|
|
[self KMLog:[NSString stringWithFormat:@"Setting language: %@_%@", languageID, keyboardID] checkDebugPrinting:YES];
|
|
if ([self usingTempFolder])
|
|
[self copyKeymanFilesToTemp];
|
|
|
|
if ([keyboardID length] && [languageID length]) {
|
|
eKMKeyboardState kbState = [[KMManager sharedInstance] stateForKeyboardWithID:keyboardID];
|
|
if (kbState == kKMKeyboardStateNeedsDownload) {
|
|
[self KMLog:[NSString stringWithFormat:@"Could not set keyboardID to %@ because the keyboard file does not exist", keyboardID] checkDebugPrinting:NO];
|
|
if ((keyboardID_ == nil || languageID_ == nil) && ![keyboardID isEqualToString:kKeymanDefaultKeyboardID]) {
|
|
[self setKeyboardWithID:kKeymanDefaultKeyboardID
|
|
languageID:kKeymanDefaultLanguageID
|
|
keyboardName:kKeymanDefaultKeyboardName
|
|
languageName:kKeymanDefaultLanguageName
|
|
font:kKeymanDefaultKeyboardFont
|
|
oskFont:nil];
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
languageID_ = [languageID copy];
|
|
keyboardID_ = [keyboardID copy];
|
|
NSString *key = [NSString stringWithFormat:@"%@_%@", self.languageID, self.keyboardID];
|
|
|
|
NSUserDefaults *userData = [self activeUserDefaults];
|
|
NSArray *userKeyboards = [userData arrayForKey:kKeymanUserKeyboardsListKey];
|
|
NSInteger index = [self indexForUserKeyboardWithID:self.keyboardID
|
|
languageID:self.languageID];
|
|
NSDictionary *kbDict = nil;
|
|
if (index >= 0)
|
|
kbDict = [userKeyboards objectAtIndex:index];
|
|
else
|
|
kbDict = [self.keyboardsDictionary objectForKey:key];
|
|
|
|
NSString *langName = [kbDict objectForKey:kKeymanLanguageNameKey];
|
|
NSString *kbName = [kbDict objectForKey:kKeymanKeyboardNameKey];
|
|
NSString *kbVersion = [self latestKeyboardFileVersionWithID:keyboardID];
|
|
if (kbVersion == nil)
|
|
kbVersion = @"1.0";
|
|
NSString *isRTL = [kbDict objectForKey:kKeymanKeyboardRTLKey];
|
|
|
|
if ([key isEqualToString:[NSString stringWithFormat:@"%@_%@", kKeymanDefaultLanguageID, kKeymanDefaultKeyboardID]]) {
|
|
if (langName == nil || kbName == nil) {
|
|
langName = kKeymanDefaultLanguageName;
|
|
kbName = kKeymanDefaultKeyboardName;
|
|
}
|
|
}
|
|
|
|
NSString *jsFont = [self jsFontForKeyboardID:keyboardID languageID:languageID];
|
|
NSString *jsOskFont = [self jsOskFontForKeyboardID:keyboardID languageID:languageID];
|
|
if (jsOskFont == nil || [jsOskFont isEqualToString:@"''"])
|
|
jsOskFont = jsFont;
|
|
|
|
langName = [langName stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"];
|
|
kbName = [kbName stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"];
|
|
|
|
if ([jsFont isEqualToString:@"''"])
|
|
jsFont = @"undefined";
|
|
if ([jsOskFont isEqualToString:@"''"])
|
|
jsOskFont = @"undefined";
|
|
NSString *jsString = [NSString stringWithFormat:@"setKeymanLanguage('%@','%@','%@','%@','%@',%@,%@);", kbName, self.keyboardID, langName, self.languageID, kbVersion, jsFont, jsOskFont];
|
|
[self KMLog:[NSString stringWithFormat:@"Evaluating JavaScript: %@", jsString] checkDebugPrinting:YES];
|
|
if ([[self class] isWKWebView:webView])
|
|
[(WKWebView *)webView evaluateJavaScript:jsString completionHandler:nil];
|
|
else
|
|
[(UIWebView *)webView stringByEvaluatingJavaScriptFromString:jsString];
|
|
|
|
if (kIsKeymanSystemKeyboard)
|
|
userData = [NSUserDefaults standardUserDefaults];
|
|
else
|
|
userData = [self activeUserDefaults];
|
|
|
|
[userData setObject:[NSDictionary dictionaryWithObjectsAndKeys:
|
|
self.keyboardID, kKeymanKeyboardIdKey,
|
|
self.languageID, kKeymanLanguageIdKey,
|
|
[kbName stringByReplacingOccurrencesOfString:@"\\'" withString:@"'"], kKeymanKeyboardNameKey,
|
|
[langName stringByReplacingOccurrencesOfString:@"\\'" withString:@"'"], kKeymanLanguageNameKey,
|
|
kbVersion, kKeymanKeyboardVersionKey,
|
|
(isRTL != nil)?isRTL:@"N", kKeymanKeyboardRTLKey,
|
|
jsFont, kKeymanFontKey,
|
|
nil] forKey:kKeymanUserCurrentKeyboardKey];
|
|
[userData synchronize];
|
|
|
|
if (self.keymanHelpOn) {
|
|
[helpBubbleView removeFromSuperview];
|
|
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(showHelpBubble) object:nil];
|
|
[self performSelector:@selector(showHelpBubble) withObject:nil afterDelay:1.5];
|
|
}
|
|
|
|
NSDictionary *kbInfo = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
keyboardID, kKeymanKeyboardIdKey,
|
|
languageID, kKeymanLanguageIdKey,
|
|
kbName, kKeymanKeyboardNameKey,
|
|
langName, kKeymanLanguageNameKey,
|
|
kbVersion, kKeymanKeyboardVersionKey,
|
|
jsFont, kKeymanFontKey,
|
|
nil];
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:kKeymanKeyboardChangedNotification object:self
|
|
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:kbInfo, kKeymanKeyboardInfoKey, nil]];
|
|
|
|
return YES;
|
|
}
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (BOOL)setKeyboardWithID:(NSString *)keyboardID languageID:(NSString *)languageID keyboardName:(NSString *)keyboardName languageName:(NSString *)languageName {
|
|
// if keyboardName or languageName is nil call setKeyboardWithID without these parameters
|
|
if (keyboardName == nil || languageName == nil)
|
|
return [self setKeyboardWithID:keyboardID languageID:languageID];
|
|
|
|
if (languageID != languageID_ || keyboardID != keyboardID_) {
|
|
[self KMLog:[NSString stringWithFormat:@"Setting language: %@_%@", languageID, keyboardID] checkDebugPrinting:YES];
|
|
if ([self usingTempFolder])
|
|
[self copyKeymanFilesToTemp];
|
|
|
|
if ([keyboardID length] && [languageID length]) {
|
|
eKMKeyboardState kbState = [[KMManager sharedInstance] stateForKeyboardWithID:keyboardID];
|
|
if (kbState == kKMKeyboardStateNeedsDownload) {
|
|
[self KMLog:[NSString stringWithFormat:@"Could not set keyboardID to %@ because the keyboard file does not exist", keyboardID] checkDebugPrinting:NO];
|
|
if ((keyboardID_ == nil || languageID_ == nil) && ![keyboardID isEqualToString:kKeymanDefaultKeyboardID]) {
|
|
[self setKeyboardWithID:kKeymanDefaultKeyboardID
|
|
languageID:kKeymanDefaultLanguageID
|
|
keyboardName:kKeymanDefaultKeyboardName
|
|
languageName:kKeymanDefaultLanguageName
|
|
font:kKeymanDefaultKeyboardFont
|
|
oskFont:nil];
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
languageID_ = [languageID copy];
|
|
keyboardID_ = [keyboardID copy];
|
|
NSString *key = [NSString stringWithFormat:@"%@_%@", self.languageID, self.keyboardID];
|
|
|
|
NSUserDefaults *userData = [self activeUserDefaults];
|
|
NSArray *userKeyboards = [userData arrayForKey:kKeymanUserKeyboardsListKey];
|
|
NSInteger index = [self indexForUserKeyboardWithID:self.keyboardID
|
|
languageID:self.languageID];
|
|
NSDictionary *kbDict = nil;
|
|
if (index >= 0)
|
|
kbDict = [userKeyboards objectAtIndex:index];
|
|
else
|
|
kbDict = [self.keyboardsDictionary objectForKey:key];
|
|
|
|
NSString *kbVersion = [self latestKeyboardFileVersionWithID:keyboardID];
|
|
if (kbVersion == nil)
|
|
kbVersion = @"1.0";
|
|
NSString *isRTL = [kbDict objectForKey:kKeymanKeyboardRTLKey];
|
|
|
|
NSString *jsFont = [self jsFontForKeyboardID:keyboardID languageID:languageID];
|
|
NSString *jsOskFont = [self jsOskFontForKeyboardID:keyboardID languageID:languageID];
|
|
if (jsOskFont == nil || [jsOskFont isEqualToString:@"''"])
|
|
jsOskFont = jsFont;
|
|
|
|
NSString *langName = [NSString stringWithString:[languageName stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"]];
|
|
NSString *kbName = [NSString stringWithString:[keyboardName stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"]];
|
|
|
|
if ([jsFont isEqualToString:@"''"])
|
|
jsFont = @"undefined";
|
|
if ([jsOskFont isEqualToString:@"''"])
|
|
jsOskFont = @"undefined";
|
|
NSString *jsString = [NSString stringWithFormat:@"setKeymanLanguage('%@','%@','%@','%@','%@',%@,%@);", kbName, self.keyboardID, langName, self.languageID, kbVersion, jsFont, jsOskFont];
|
|
[self KMLog:[NSString stringWithFormat:@"Evaluating JavaScript: %@", jsString] checkDebugPrinting:YES];
|
|
if ([[self class] isWKWebView:webView])
|
|
[(WKWebView *)webView evaluateJavaScript:jsString completionHandler:nil];
|
|
else
|
|
[(UIWebView *)webView stringByEvaluatingJavaScriptFromString:jsString];
|
|
|
|
if (kIsKeymanSystemKeyboard)
|
|
userData = [NSUserDefaults standardUserDefaults];
|
|
else
|
|
userData = [self activeUserDefaults];
|
|
|
|
[userData setObject:[NSDictionary dictionaryWithObjectsAndKeys:
|
|
self.keyboardID, kKeymanKeyboardIdKey,
|
|
self.languageID, kKeymanLanguageIdKey,
|
|
[kbName stringByReplacingOccurrencesOfString:@"\\'" withString:@"'"], kKeymanKeyboardNameKey,
|
|
[langName stringByReplacingOccurrencesOfString:@"\\'" withString:@"'"], kKeymanLanguageNameKey,
|
|
kbVersion, kKeymanKeyboardVersionKey,
|
|
(isRTL != nil)?isRTL:@"N", kKeymanKeyboardRTLKey,
|
|
jsFont, kKeymanFontKey,
|
|
nil] forKey:kKeymanUserCurrentKeyboardKey];
|
|
[userData synchronize];
|
|
|
|
if (self.keymanHelpOn) {
|
|
[helpBubbleView removeFromSuperview];
|
|
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(showHelpBubble) object:nil];
|
|
[self performSelector:@selector(showHelpBubble) withObject:nil afterDelay:1.5];
|
|
}
|
|
|
|
NSDictionary *kbInfo = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
keyboardID, kKeymanKeyboardIdKey,
|
|
languageID, kKeymanLanguageIdKey,
|
|
keyboardName, kKeymanKeyboardNameKey,
|
|
langName, kKeymanLanguageNameKey,
|
|
kbVersion, kKeymanKeyboardVersionKey,
|
|
jsFont, kKeymanFontKey,
|
|
nil];
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:kKeymanKeyboardChangedNotification
|
|
object:self
|
|
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:kbInfo, kKeymanKeyboardInfoKey, nil]];
|
|
return YES;
|
|
}
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (BOOL)setKeyboardWithID:(NSString *)keyboardID languageID:(NSString *)languageID keyboardName:(NSString *)keyboardName languageName:(NSString *)languageName font:(NSString *)font oskFont:(NSString *)oskFont {
|
|
// if the font and the oskFont are both nil call setKeyboardWithID without these parameters
|
|
if (font == nil && oskFont == nil)
|
|
return [self setKeyboardWithID:keyboardID languageID:languageID keyboardName:keyboardName languageName:languageName];
|
|
|
|
if (keyboardID != keyboardID_ || languageID != languageID_) {
|
|
[self KMLog:[NSString stringWithFormat:@"Setting language: %@_%@", languageID, keyboardID] checkDebugPrinting:YES];
|
|
if ([self usingTempFolder])
|
|
[self copyKeymanFilesToTemp];
|
|
|
|
if ([keyboardID length] && [languageID length]) {
|
|
eKMKeyboardState kbState = [[KMManager sharedInstance] stateForKeyboardWithID:keyboardID];
|
|
if (kbState == kKMKeyboardStateNeedsDownload) {
|
|
[self KMLog:[NSString stringWithFormat:@"Could not set keyboardID to %@ because the keyboard file does not exist", keyboardID] checkDebugPrinting:NO];
|
|
if ((keyboardID_ == nil || languageID_ == nil) && ![keyboardID isEqualToString:kKeymanDefaultKeyboardID]) {
|
|
[self setKeyboardWithID:kKeymanDefaultKeyboardID
|
|
languageID:kKeymanDefaultLanguageID
|
|
keyboardName:kKeymanDefaultKeyboardName
|
|
languageName:kKeymanDefaultLanguageName
|
|
font:kKeymanDefaultKeyboardFont
|
|
oskFont:nil
|
|
];
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
keyboardID_ = [keyboardID copy];
|
|
languageID_ = [languageID copy];
|
|
NSString *key = [NSString stringWithFormat:@"%@_%@", self.languageID, self.keyboardID];
|
|
|
|
NSUserDefaults *userData = [self activeUserDefaults];
|
|
NSArray *userKeyboards = [userData arrayForKey:kKeymanUserKeyboardsListKey];
|
|
NSInteger index = [self indexForUserKeyboardWithID:self.keyboardID
|
|
languageID:self.languageID];
|
|
NSDictionary *kbDict = nil;
|
|
if (index >= 0)
|
|
kbDict = [userKeyboards objectAtIndex:index];
|
|
else
|
|
kbDict = [self.keyboardsDictionary objectForKey:key];
|
|
|
|
NSString *kbVersion = [self latestKeyboardFileVersionWithID:keyboardID];
|
|
if (kbVersion == nil)
|
|
kbVersion = @"1.0";
|
|
|
|
NSString *jsFont = nil;
|
|
if ([font rangeOfString:@".ttf"].location != NSNotFound || [font rangeOfString:@".otf"].location != NSNotFound) {
|
|
if ([[font substringFromIndex:font.length-4] isEqualToString:@".ttf"] || [[font substringFromIndex:font.length-4] isEqualToString:@".otf"]) {
|
|
NSString *familyName = [NSString stringWithFormat:@"font_family_%@", [font substringToIndex:font.length-4]];
|
|
jsFont = ([font length])?[NSString stringWithFormat:@"{\"family\":\"%@\",\"files\":[\"%@\"]}", familyName, font]:@"''";
|
|
}
|
|
else
|
|
jsFont = ([font length])?font:@"''";
|
|
}
|
|
else
|
|
jsFont = @"''";
|
|
|
|
NSString *jsOskFont = nil;
|
|
if (oskFont == nil || [oskFont isEqualToString:@"''"]) {
|
|
jsOskFont = jsFont;
|
|
}
|
|
else {
|
|
if ([oskFont rangeOfString:@".ttf"].location != NSNotFound || [oskFont rangeOfString:@".otf"].location != NSNotFound) {
|
|
if ([[oskFont substringFromIndex:oskFont.length-4] isEqualToString:@".ttf"] || [[oskFont substringFromIndex:oskFont.length-4] isEqualToString:@".otf"]) {
|
|
NSString *familyName = [NSString stringWithFormat:@"font_family_%@", [oskFont substringToIndex:oskFont.length-4]];
|
|
jsOskFont = ([oskFont length])?[NSString stringWithFormat:@"{\"family\":\"%@\",\"files\":[\"%@\"]}", familyName, oskFont]:@"''";
|
|
}
|
|
else
|
|
jsOskFont = ([oskFont length])?oskFont:@"''";
|
|
}
|
|
else
|
|
jsOskFont = @"''";
|
|
}
|
|
|
|
NSString *isRTL = [kbDict objectForKey:kKeymanKeyboardRTLKey];
|
|
NSString *langName = [NSString stringWithString:[languageName stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"]];
|
|
NSString *kbName = [NSString stringWithString:[keyboardName stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"]];
|
|
|
|
if ([jsFont isEqualToString:@"''"])
|
|
jsFont = @"undefined";
|
|
if ([jsOskFont isEqualToString:@"''"])
|
|
jsOskFont = @"undefined";
|
|
NSString *jsString = [NSString stringWithFormat:@"setKeymanLanguage('%@','%@','%@','%@','%@',%@,%@);", kbName, self.keyboardID, langName, self.languageID, kbVersion, jsFont, jsOskFont];
|
|
[self KMLog:[NSString stringWithFormat:@"Evaluating JavaScript: %@", jsString] checkDebugPrinting:YES];
|
|
if ([[self class] isWKWebView:webView])
|
|
[(WKWebView *)webView evaluateJavaScript:jsString completionHandler:nil];
|
|
else
|
|
[(UIWebView *)webView stringByEvaluatingJavaScriptFromString:jsString];
|
|
|
|
if (kIsKeymanSystemKeyboard)
|
|
userData = [NSUserDefaults standardUserDefaults];
|
|
else
|
|
userData = [self activeUserDefaults];
|
|
|
|
[userData setObject:[NSDictionary dictionaryWithObjectsAndKeys:
|
|
self.keyboardID, kKeymanKeyboardIdKey,
|
|
self.languageID, kKeymanLanguageIdKey,
|
|
[kbName stringByReplacingOccurrencesOfString:@"\\'" withString:@"'"], kKeymanKeyboardNameKey,
|
|
[langName stringByReplacingOccurrencesOfString:@"\\'" withString:@"'"], kKeymanLanguageNameKey,
|
|
kbVersion, kKeymanKeyboardVersionKey,
|
|
(isRTL != nil)?isRTL:@"N", kKeymanKeyboardRTLKey,
|
|
jsFont, kKeymanFontKey,
|
|
nil] forKey:kKeymanUserCurrentKeyboardKey];
|
|
[userData synchronize];
|
|
|
|
if (self.keymanHelpOn) {
|
|
[helpBubbleView removeFromSuperview];
|
|
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(showHelpBubble) object:nil];
|
|
[self performSelector:@selector(showHelpBubble) withObject:nil afterDelay:1.5];
|
|
}
|
|
|
|
NSDictionary *kbInfo = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
keyboardID, kKeymanKeyboardIdKey,
|
|
languageID, kKeymanLanguageIdKey,
|
|
keyboardName, kKeymanKeyboardNameKey,
|
|
langName, kKeymanLanguageNameKey,
|
|
kbVersion, kKeymanKeyboardVersionKey,
|
|
jsFont, kKeymanFontKey,
|
|
nil];
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:kKeymanKeyboardChangedNotification
|
|
object:self
|
|
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:kbInfo, kKeymanKeyboardInfoKey, nil]];
|
|
|
|
return YES;
|
|
}
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (NSInteger)switchToNextKeyboard {
|
|
NSInteger index = [self currentKeyboardIndex];
|
|
if (index < 0)
|
|
return index;
|
|
|
|
NSUserDefaults *userData = [self activeUserDefaults];
|
|
NSArray *userKeyboards = [userData arrayForKey:kKeymanUserKeyboardsListKey];
|
|
NSUInteger len = [userKeyboards count];
|
|
if (len > 1) {
|
|
index++;
|
|
index = index%len;
|
|
NSDictionary *kb = [userKeyboards objectAtIndex:index];
|
|
NSString *kbId = [kb objectForKey:kKeymanKeyboardIdKey];
|
|
NSString *langId = [kb objectForKey:kKeymanLanguageIdKey];
|
|
NSString *kbName = [kb objectForKey:kKeymanKeyboardNameKey];
|
|
NSString *langName = [kb objectForKey:kKeymanLanguageNameKey];
|
|
NSString *font = [kb objectForKey:kKeymanFontKey];
|
|
NSString *oskFont = [kb objectForKey:kKeymanOskFontKey];
|
|
[self setKeyboardWithID:kbId languageID:langId keyboardName:kbName languageName:langName font:font oskFont:oskFont];
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
- (NSInteger)currentKeyboardIndex {
|
|
NSUserDefaults *userData = [self activeUserDefaults];
|
|
NSArray *userKeyboards = [userData arrayForKey:kKeymanUserKeyboardsListKey];
|
|
NSUInteger len = [userKeyboards count];
|
|
if (len < 1)
|
|
return -1;
|
|
|
|
NSDictionary *kb = nil;
|
|
NSInteger index = -1;
|
|
for (int i = 0; i < len; i++) {
|
|
kb = [userKeyboards objectAtIndex:i];
|
|
NSString *kbId = [kb objectForKey:kKeymanKeyboardIdKey];
|
|
NSString *langId = [kb objectForKey:kKeymanLanguageIdKey];
|
|
if ([keyboardID_ isEqualToString:kbId] && [languageID_ isEqualToString:langId]) {
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
- (void)setCanAddNewKeyboards:(BOOL)canAddNewKeyboards {
|
|
canAddNewKeyboards_ = canAddNewKeyboards;
|
|
if (!canAddNewKeyboards)
|
|
canRemoveKeyboards_ = NO;
|
|
}
|
|
|
|
- (BOOL)userKeyboardExistsWithID:(NSString *)kbID languageID:(NSString *)langID {
|
|
BOOL exists = NO;
|
|
NSArray *userKeyboards = [[self activeUserDefaults] arrayForKey:kKeymanUserKeyboardsListKey];
|
|
if (userKeyboards != nil && [userKeyboards count]) {
|
|
for (NSDictionary *kb in userKeyboards) {
|
|
if([kbID isEqualToString:[kb objectForKey:kKeymanKeyboardIdKey]] && [langID isEqualToString:[kb objectForKey:kKeymanLanguageIdKey]]) {
|
|
exists = YES;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return exists;
|
|
}
|
|
|
|
- (NSInteger)indexForUserKeyboardWithID:(NSString *)kbID languageID:(NSString *)langID {
|
|
BOOL exists = NO;
|
|
NSInteger index = -1;
|
|
NSArray *userKeyboards = [[self activeUserDefaults] arrayForKey:kKeymanUserKeyboardsListKey];
|
|
if (userKeyboards != nil && [userKeyboards count]) {
|
|
for (NSDictionary *kb in userKeyboards) {
|
|
index++;
|
|
if([kbID isEqualToString:[kb objectForKey:kKeymanKeyboardIdKey]] && [langID isEqualToString:[kb objectForKey:kKeymanLanguageIdKey]]) {
|
|
exists = YES;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (exists)
|
|
return index;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
- (NSDictionary *)currentKeyboardInfo {
|
|
if (keyboardID_ == nil || languageID_ == nil)
|
|
return nil;
|
|
|
|
NSArray *userKeyboards = [[self activeUserDefaults] arrayForKey:kKeymanUserKeyboardsListKey];
|
|
NSDictionary *kbInfo = nil;
|
|
for (NSDictionary *kb in userKeyboards) {
|
|
if([keyboardID_ isEqualToString:[kb objectForKey:kKeymanKeyboardIdKey]] && [languageID_ isEqualToString:[kb objectForKey:kKeymanLanguageIdKey]]) {
|
|
kbInfo = [NSDictionary dictionaryWithDictionary:kb];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (kbInfo == nil && keyboardsDictionary_ != nil) {
|
|
NSString *kbKey = [NSString stringWithFormat:@"%@_%@", languageID_, keyboardID_];
|
|
NSDictionary *kb = [keyboardsDictionary_ objectForKey:kbKey];
|
|
if (kb != nil)
|
|
kbInfo = [NSDictionary dictionaryWithDictionary:kb];
|
|
}
|
|
|
|
if (kbInfo == nil)
|
|
kbInfo = [NSDictionary dictionaryWithObjectsAndKeys:keyboardID_, kKeymanKeyboardIdKey,
|
|
languageID_, kKeymanLanguageIdKey, nil];
|
|
|
|
return kbInfo;
|
|
}
|
|
|
|
- (void)addKeyboardWithID:(NSString *)kbID languageID:(NSString *)langID keyboardName:(NSString *)kbName languageName:(NSString *)langName isRTL:(BOOL)isRTL isCustom:(BOOL)isCustom font:(NSString *)font oskFont:(NSString *)oskFont {
|
|
// Check if keyboard file exists
|
|
NSString *kbVersion = [self latestKeyboardFileVersionWithID:kbID];
|
|
if (kbVersion == nil) {
|
|
[self KMLog:[NSString stringWithFormat:@"Could not add keyboard with ID: %@ because the keyboard file does not exist", kbID] checkDebugPrinting:NO];
|
|
return;
|
|
}
|
|
|
|
// Get keyboards list if it exists in user defaults
|
|
NSMutableArray *userKeyboards = [NSMutableArray arrayWithArray:[[self activeUserDefaults] arrayForKey:kKeymanUserKeyboardsListKey]];
|
|
|
|
// If user defaults for keyboards list does not exist, start creating a new one.
|
|
if (userKeyboards == nil)
|
|
userKeyboards = [[NSMutableArray alloc] init];
|
|
|
|
// Get keyboard index if it exists in list.
|
|
NSInteger x = [self indexForUserKeyboardWithID:kbID languageID:langID];
|
|
|
|
// Add keyboard to the list if it doesn't exist, update otherwise.
|
|
if (x < 0) {
|
|
[userKeyboards addObject:[NSDictionary dictionaryWithObjectsAndKeys:
|
|
langID, kKeymanLanguageIdKey,
|
|
langName, kKeymanLanguageNameKey,
|
|
kbID, kKeymanKeyboardIdKey,
|
|
kbName, kKeymanKeyboardNameKey,
|
|
kbVersion, kKeymanKeyboardVersionKey,
|
|
isRTL?@"Y":@"N", kKeymanKeyboardRTLKey,
|
|
isCustom?@"Y":@"N", kKeymanCustomKeyboardKey,
|
|
font?font:@"''", kKeymanFontKey,
|
|
oskFont, kKeymanOskFontKey,
|
|
nil]];
|
|
}
|
|
else {
|
|
[userKeyboards replaceObjectAtIndex:x withObject:[NSDictionary dictionaryWithObjectsAndKeys:
|
|
langID, kKeymanLanguageIdKey,
|
|
langName, kKeymanLanguageNameKey,
|
|
kbID, kKeymanKeyboardIdKey,
|
|
kbName, kKeymanKeyboardNameKey,
|
|
kbVersion, kKeymanKeyboardVersionKey,
|
|
isRTL?@"Y":@"N", kKeymanKeyboardRTLKey,
|
|
isCustom?@"Y":@"N", kKeymanCustomKeyboardKey,
|
|
font?font:@"''", kKeymanFontKey,
|
|
oskFont, kKeymanOskFontKey,
|
|
nil]];
|
|
}
|
|
|
|
NSUserDefaults *userData = [self activeUserDefaults];
|
|
[userData setObject:userKeyboards forKey:kKeymanUserKeyboardsListKey];
|
|
[userData setObject:[NSArray arrayWithObject:[NSDate date]] forKey:kKeymanSynchronizeSWKeyboardKey];
|
|
[userData synchronize];
|
|
}
|
|
|
|
- (void)removeKeyboardWithID:(NSString *)kbID languageID:(NSString *)langID {
|
|
NSUserDefaults *userData = [self activeUserDefaults];
|
|
NSMutableArray *userKeyboards = [NSMutableArray arrayWithArray:[userData arrayForKey:kKeymanUserKeyboardsListKey]];
|
|
|
|
// If user defaults for keyboards list does not exist, do nothing.
|
|
if (userKeyboards == nil)
|
|
return;
|
|
|
|
// Check if the keyboard exists in list.
|
|
NSInteger index = [self indexForUserKeyboardWithID:kbID languageID:langID];
|
|
|
|
// Remove keyboard from the list if it exists
|
|
if (index >= 0) {
|
|
NSDictionary *kbDict = [NSDictionary dictionaryWithDictionary:[userKeyboards objectAtIndex:index]];
|
|
[userKeyboards removeObjectAtIndex:index];
|
|
[userData setObject:userKeyboards forKey:kKeymanUserKeyboardsListKey];
|
|
[userData setObject:[NSArray arrayWithObject:[NSDate date]] forKey:kKeymanSynchronizeSWKeyboardKey];
|
|
[userData synchronize];
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:kKeymanKeyboardRemovedNotification object:self
|
|
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:kbDict, kKeymanKeyboardInfoKey, nil]];
|
|
}
|
|
}
|
|
|
|
- (BOOL)removeKeyboardAtIndex:(NSUInteger)index {
|
|
NSUserDefaults *userData = [self activeUserDefaults];
|
|
NSMutableArray *userKeyboards = [[userData arrayForKey:kKeymanUserKeyboardsListKey] mutableCopy];
|
|
|
|
if (userKeyboards != nil) {
|
|
if (index < [userKeyboards count]) {
|
|
[userKeyboards removeObjectAtIndex:index];
|
|
[userData setObject:userKeyboards forKey:kKeymanUserKeyboardsListKey];
|
|
[userData setObject:[NSArray arrayWithObject:[NSDate date]] forKey:kKeymanSynchronizeSWKeyboardKey];
|
|
[userData synchronize];
|
|
return YES;
|
|
}
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (void)updateKeyboardVersionForID:(NSString *)kbID newKeyboardVersion:(NSString *)kbVersion {
|
|
NSUserDefaults *userData = [self activeUserDefaults];
|
|
NSMutableArray *userKeyboards = [[userData arrayForKey:kKeymanUserKeyboardsListKey] mutableCopy];
|
|
if (userKeyboards == nil)
|
|
return;
|
|
|
|
NSUInteger len = [userKeyboards count];
|
|
for (int i = 0; i < len; i++) {
|
|
NSDictionary *kb = [userKeyboards objectAtIndex:i];
|
|
if ([kbID isEqualToString:[kb objectForKey:kKeymanKeyboardIdKey]]) {
|
|
NSMutableDictionary *kbInfo = [kb mutableCopy];
|
|
[kbInfo setObject:kbVersion forKey:kKeymanKeyboardVersionKey];
|
|
[userKeyboards replaceObjectAtIndex:i withObject:kbInfo];
|
|
}
|
|
}
|
|
|
|
[userData setObject:userKeyboards forKey:kKeymanUserKeyboardsListKey];
|
|
[userData synchronize];
|
|
|
|
if (kIsKeymanSystemKeyboard)
|
|
userData = [NSUserDefaults standardUserDefaults];
|
|
else
|
|
userData = [self activeUserDefaults];
|
|
|
|
NSMutableDictionary *userKb = [[userData objectForKey:kKeymanUserCurrentKeyboardKey] mutableCopy];
|
|
if ([kbID isEqualToString:[userKb objectForKey:kKeymanKeyboardIdKey]]) {
|
|
[userKb setObject:kbVersion forKey:kKeymanKeyboardVersionKey];
|
|
[userData setObject:userKb forKey:kKeymanUserCurrentKeyboardKey];
|
|
}
|
|
|
|
[userData synchronize];
|
|
}
|
|
|
|
- (void)showKeyboardPickerInViewController:(UIViewController *)viewController shouldAddKeyboard:(BOOL)shouldAddKeyboard {
|
|
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad && [[[UIDevice currentDevice] systemVersion] intValue] >= 7) {
|
|
id firstResponder = [[[UIApplication sharedApplication] keyWindow] findFirstResponder];
|
|
if ([firstResponder isKindOfClass:[KMTextView class]])
|
|
[(KMTextView *)firstResponder dismissKeyboard];
|
|
else if ([firstResponder isKindOfClass:[KMTextField class]])
|
|
[(KMTextField *)firstResponder dismissKeyboard];
|
|
}
|
|
|
|
KeyboardPickerViewController *vc = [[KeyboardPickerViewController alloc] init];
|
|
UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:vc];
|
|
nc.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
|
|
nc.modalPresentationStyle = UIModalPresentationPageSheet;
|
|
[viewController presentViewController:nc animated:YES completion:^{
|
|
if (shouldAddKeyboard) {
|
|
[vc showAddKeyboard];
|
|
}
|
|
else {
|
|
NSUserDefaults *userData = [self activeUserDefaults];
|
|
[userData setBool:YES forKey:kKeymanKeyboardPickerDisplayedKey];
|
|
[userData synchronize];
|
|
self.keymanHelpOn = NO;
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)dismissKeyboardPicker:(UIViewController *)viewController {
|
|
[viewController dismissViewControllerAnimated:YES completion:nil];
|
|
|
|
if (shouldReloadKeyboard)
|
|
[self reloadKeyboard];
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:kKeymanKeyboardPickerDismissedNotification object:self];
|
|
}
|
|
|
|
- (void)reloadKeyboard {
|
|
NSURL *codeURL = [NSURL fileURLWithPath:[[[KMManager sharedInstance] activeKeymanDirectory] stringByAppendingPathComponent:kKeymanFullFileName]];
|
|
if ([[self class] isWKWebView:webView]) {
|
|
/*
|
|
[(WKWebView *)webView loadHTMLString:@"" baseURL:nil];
|
|
[(WKWebView *)webView stopLoading];
|
|
[(WKWebView *)webView setNavigationDelegate:nil];
|
|
[(WKWebView *)webView setNavigationDelegate:[KMManager sharedInstance]];*/
|
|
SEL sel = NSSelectorFromString(@"loadFileURL:allowingReadAccessToURL:");
|
|
if ([(WKWebView *)webView respondsToSelector:sel]) {
|
|
((id (*)(id, SEL, id, id))objc_msgSend)((WKWebView *)webView, sel, codeURL, [NSURL URLWithString:[[codeURL absoluteString] stringByDeletingLastPathComponent]]);
|
|
}
|
|
else {
|
|
// Copy Keyman files to the temp folder and load from there
|
|
if ([self copyKeymanFilesToTemp]) {
|
|
codeURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"keyman/%@", kKeymanFullFileName]]];
|
|
[(WKWebView *)webView loadRequest:[NSURLRequest requestWithURL:codeURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0]];
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/*
|
|
[(UIWebView *)webView loadHTMLString:@"" baseURL:nil];
|
|
[(UIWebView *)webView stopLoading];
|
|
[(UIWebView *)webView setDelegate:nil];
|
|
[(UIWebView *)webView setDelegate:[KMManager sharedInstance]];*/
|
|
[(UIWebView *)webView loadRequest:[NSURLRequest requestWithURL:codeURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0]];
|
|
}
|
|
}
|
|
|
|
- (void)resetKeyboard {
|
|
NSArray *userKeyboards = [[self activeUserDefaults] arrayForKey:kKeymanUserKeyboardsListKey];
|
|
NSInteger index = [self indexForUserKeyboardWithID:self.keyboardID languageID:self.languageID];
|
|
if (index >= 0) {
|
|
keyboardID_ = nil;
|
|
languageID_ = nil;
|
|
NSDictionary *kb = [userKeyboards objectAtIndex:index];
|
|
NSString *kbId = [kb objectForKey:kKeymanKeyboardIdKey];
|
|
NSString *langId = [kb objectForKey:kKeymanLanguageIdKey];
|
|
NSString *kbName = [kb objectForKey:kKeymanKeyboardNameKey];
|
|
NSString *langName = [kb objectForKey:kKeymanLanguageNameKey];
|
|
NSString *font = [kb objectForKey:kKeymanFontKey];
|
|
NSString *oskFont = [kb objectForKey:kKeymanOskFontKey];
|
|
[self setKeyboardWithID:kbId languageID:langId keyboardName:kbName languageName:langName font:font oskFont:oskFont];
|
|
}
|
|
else {
|
|
NSDictionary *kb = [userKeyboards objectAtIndex:0];
|
|
if (kb != nil) {
|
|
keyboardID_ = nil;
|
|
languageID_ = nil;
|
|
NSString *kbId = [kb objectForKey:kKeymanKeyboardIdKey];
|
|
NSString *langId = [kb objectForKey:kKeymanLanguageIdKey];
|
|
NSString *kbName = [kb objectForKey:kKeymanKeyboardNameKey];
|
|
NSString *langName = [kb objectForKey:kKeymanLanguageNameKey];
|
|
NSString *font = [kb objectForKey:kKeymanFontKey];
|
|
NSString *oskFont = [kb objectForKey:kKeymanOskFontKey];
|
|
[self setKeyboardWithID:kbId languageID:langId keyboardName:kbName languageName:langName font:font oskFont:oskFont];
|
|
}
|
|
else {
|
|
keyboardID_ = nil;
|
|
languageID_ = nil;
|
|
[self setKeyboardWithID:kKeymanDefaultKeyboardID
|
|
languageID:kKeymanDefaultLanguageID
|
|
keyboardName:kKeymanDefaultKeyboardName
|
|
languageName:kKeymanDefaultLanguageName
|
|
font:kKeymanDefaultKeyboardFont
|
|
oskFont:nil];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (BOOL)usingTempFolder {
|
|
if ([[self class] isWKWebView:webView]) {
|
|
SEL sel = NSSelectorFromString(@"loadFileURL:allowingReadAccessToURL:");
|
|
if (![(WKWebView *)webView respondsToSelector:sel]) {
|
|
return YES;
|
|
}
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (void)preloadLanguageFileAtPath:(NSString *)languagePath shouldOverwrite:(BOOL)shouldOverwrite {
|
|
if (![languagePath length])
|
|
return;
|
|
|
|
NSFileManager *fileMan = [NSFileManager defaultManager];
|
|
NSError *error = nil;
|
|
NSString *langDir = [self activeLanguageDirectory];
|
|
|
|
if (![langDir length]) {
|
|
[self KMLog:@"Could not find/create the Keyman language directory" checkDebugPrinting:NO];
|
|
return;
|
|
}
|
|
|
|
NSString *destinationPath = [langDir stringByAppendingPathComponent:[languagePath lastPathComponent]];
|
|
if (![fileMan fileExistsAtPath:destinationPath])
|
|
[fileMan copyItemAtPath:languagePath toPath:destinationPath error:&error];
|
|
else if (shouldOverwrite) {
|
|
[fileMan removeItemAtPath:destinationPath error:&error];
|
|
if (error == nil)
|
|
[fileMan copyItemAtPath:languagePath toPath:destinationPath error:&error];
|
|
}
|
|
|
|
if(error==nil)
|
|
[self addSkipBackupAttributeToItemAtPath:destinationPath];
|
|
else
|
|
[self KMLog:[NSString stringWithFormat:@"Error copying language file: %@", error] checkDebugPrinting:NO];
|
|
}
|
|
|
|
- (void)preloadFontFileAtPath:(NSString *)fontPath shouldOverwrite:(BOOL)shouldOverwrite {
|
|
if (![fontPath length])
|
|
return;
|
|
|
|
NSFileManager *fileMan = [NSFileManager defaultManager];
|
|
NSError *error = nil;
|
|
NSString *fontDir = [self activeFontDirectory];
|
|
|
|
if (![fontDir length]) {
|
|
[self KMLog:@"Could not find/create the Keyman font directory" checkDebugPrinting:NO];
|
|
return;
|
|
}
|
|
|
|
NSString *destinationPath = [fontDir stringByAppendingPathComponent:[fontPath lastPathComponent]];
|
|
if (![fileMan fileExistsAtPath:destinationPath])
|
|
[fileMan copyItemAtPath:fontPath toPath:destinationPath error:&error];
|
|
else if (shouldOverwrite) {
|
|
[fileMan removeItemAtPath:destinationPath error:&error];
|
|
if (error == nil)
|
|
[fileMan copyItemAtPath:fontPath toPath:destinationPath error:&error];
|
|
}
|
|
|
|
if (error != nil)
|
|
[self KMLog:[NSString stringWithFormat:@"Error copying font file: %@", error] checkDebugPrinting:NO];
|
|
else
|
|
[self addSkipBackupAttributeToItemAtPath:destinationPath];
|
|
}
|
|
|
|
- (void)fetchKeyboardsList {
|
|
[[self class] fetchKeyboardsWithCompletionBlock:nil];
|
|
}
|
|
|
|
- (NSString *)sdkVersion {
|
|
NSBundle *keymanBundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"Keyman" ofType:@"bundle"]];
|
|
NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:[keymanBundle pathForResource:@"KeymanEngine-Info" ofType:@"plist"]];
|
|
return [info objectForKey:@"CFBundleVersion"];
|
|
}
|
|
|
|
- (void)setKeymanHelpOn:(BOOL)keymanHelpOn {
|
|
// only in-app keyboard is allowed to enable/disable the help bubble, it is always disabled for system-wide keyboard
|
|
if (!kIsKeymanSystemKeyboard) {
|
|
keymanHelpOn_ = keymanHelpOn;
|
|
}
|
|
}
|
|
|
|
- (void)showHelpBubble {
|
|
// help bubble is always disabled for system-wide keyboard
|
|
if (kIsKeymanSystemKeyboard || keyboardMenuView != nil)
|
|
return;
|
|
|
|
NSString *jsString = [NSString stringWithFormat:@"langMenuPos();"];
|
|
if ([[self class] isWKWebView:webView]) {
|
|
[(WKWebView *)webView evaluateJavaScript:jsString completionHandler:^(id result, NSError *error) {
|
|
if (result) {
|
|
NSString *langMenuKeyPos = [NSString stringWithFormat:@"%@", result];
|
|
if ([langMenuKeyPos length]) {
|
|
NSArray *pos = [langMenuKeyPos componentsSeparatedByString: @","];
|
|
CGFloat px = [[pos objectAtIndex:0] floatValue];
|
|
CGFloat py = [[pos objectAtIndex:1] floatValue];
|
|
[self showHelpBubbleAtPoint:CGPointMake(px, py)];
|
|
}
|
|
}
|
|
}];
|
|
}
|
|
else {
|
|
NSString *langMenuKeyPos = [(UIWebView *)webView stringByEvaluatingJavaScriptFromString:jsString];
|
|
if ([langMenuKeyPos length]) {
|
|
NSArray *pos = [langMenuKeyPos componentsSeparatedByString: @","];
|
|
CGFloat px = [[pos objectAtIndex:0] floatValue];
|
|
CGFloat py = [[pos objectAtIndex:1] floatValue];
|
|
[self showHelpBubbleAtPoint:CGPointMake(px, py)];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)showHelpBubbleAtPoint:(CGPoint)point {
|
|
[helpBubbleView removeFromSuperview];
|
|
helpBubbleView = [[KMPopoverView alloc] initWithFrame:CGRectZero];
|
|
[helpBubbleView setBackgroundColor:[UIColor colorWithRed:253.0/255.0 green:244.0/255.0 blue:196.0/255.0 alpha:1.0]];
|
|
[helpBubbleView setBackgroundColor2:[UIColor colorWithRed:233.0/255.0 green:224.0/255.0 blue:176.0/255.0 alpha:1.0]];
|
|
[helpBubbleView setBorderColor:[UIColor colorWithRed:0.5 green:0.25 blue:0.25 alpha:1.0]];
|
|
|
|
CGFloat frameWidth = 90.0;
|
|
CGFloat frameHeight = 40.0 + helpBubbleView.arrowHeight;
|
|
CGFloat fontSize = 10.0;
|
|
|
|
BOOL isPad = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad;
|
|
if (isPad) {
|
|
frameWidth *= 1.5;
|
|
frameHeight *= 1.5;
|
|
fontSize *= 1.5;
|
|
}
|
|
|
|
CGRect inputViewFrame = [[[self class] inputView] frame];
|
|
CGFloat screenWidth = inputViewFrame.size.width;
|
|
|
|
BOOL isPortrait = YES;
|
|
if (kIsKeymanSystemKeyboard)
|
|
isPortrait = [KMInputViewController isPortrait];
|
|
else {
|
|
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
|
|
isPortrait = UIInterfaceOrientationIsPortrait(orientation);
|
|
}
|
|
|
|
CGFloat adjY;
|
|
if (isPortrait)
|
|
adjY = kIsKeymanSystemKeyboard?9.0:4.0;
|
|
else
|
|
adjY = kIsKeymanSystemKeyboard?3.0:4.0;
|
|
|
|
CGFloat px = point.x;
|
|
CGFloat py = point.y + adjY + (isPad?2.0:1.0);
|
|
CGFloat x = px - frameWidth/2;
|
|
CGFloat y = py - frameHeight;
|
|
|
|
if(x < 0)
|
|
x = 0;
|
|
else if((x+frameWidth) > screenWidth)
|
|
x = screenWidth - frameWidth;
|
|
|
|
[helpBubbleView setFrame:CGRectMake(x, y, frameWidth, frameHeight)];
|
|
if(x == 0)
|
|
[helpBubbleView setArrowPosX:px];
|
|
else if (x == (screenWidth - frameWidth))
|
|
[helpBubbleView setArrowPosX:(px - x)];
|
|
else
|
|
[helpBubbleView setArrowPosX:frameWidth/2];
|
|
|
|
UILabel *helpText = [[UILabel alloc] initWithFrame:CGRectMake(5, 0, frameWidth-10, frameHeight - helpBubbleView.arrowHeight)];
|
|
[helpText setBackgroundColor:[UIColor clearColor]];
|
|
[helpText setFont:[helpText.font fontWithSize:fontSize]];
|
|
[helpText setTextAlignment:NSTextAlignmentCenter];
|
|
[helpText setTextColor:[UIColor darkTextColor]];
|
|
[helpText setLineBreakMode:NSLineBreakByWordWrapping];
|
|
[helpText setNumberOfLines:0];
|
|
[helpText setText:kKeymanKeyboardChangeHelpText];
|
|
|
|
[helpBubbleView addSubview:helpText];
|
|
[webView addSubview:helpBubbleView];
|
|
}
|
|
|
|
#pragma mark - Internal Methods
|
|
|
|
+ (UIView *)inputView {
|
|
if ([self WKWebViewClass] && enableWebKit) {
|
|
if (webView == nil) {
|
|
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
|
|
WKPreferences *prefs = [[WKPreferences alloc] init];
|
|
[prefs setJavaScriptEnabled:YES];
|
|
[config setPreferences:prefs];
|
|
[config setSuppressesIncrementalRendering:NO];
|
|
WKUserContentController *userContentController = [[WKUserContentController alloc] init];
|
|
[userContentController addScriptMessageHandler:[self sharedInstance] name:@"keyman"];
|
|
[config setUserContentController:userContentController];
|
|
CGRect frame = CGRectMake(0.0f, 0.0f, [[self class] keyboardWidth], [[self class] keyboardHeight]);
|
|
webView = [[WKWebView alloc] initWithFrame:frame configuration:config];
|
|
//[(WKWebView *)webView setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight];
|
|
[(WKWebView *)webView setOpaque:NO];
|
|
[(WKWebView *)webView setBackgroundColor:[UIColor clearColor]];
|
|
[(WKWebView *)webView setNavigationDelegate:[self sharedInstance]];
|
|
|
|
KMManager *km = [self sharedInstance];
|
|
[km keymanScrollView].scrollEnabled = NO;
|
|
|
|
NSURL *codeURL = [NSURL fileURLWithPath:[[km activeKeymanDirectory] stringByAppendingPathComponent:kKeymanFullFileName]];
|
|
SEL sel = NSSelectorFromString(@"loadFileURL:allowingReadAccessToURL:");
|
|
if ([(WKWebView *)webView respondsToSelector:sel]) {
|
|
((id (*)(id, SEL, id, id))objc_msgSend)((WKWebView *)webView, sel, codeURL, [NSURL URLWithString:[[codeURL absoluteString] stringByDeletingLastPathComponent]]);
|
|
}
|
|
else {
|
|
// Copy Keyman files to the temp folder and load from there
|
|
if ([[self sharedInstance] copyKeymanFilesToTemp]) {
|
|
codeURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"keyman/%@", kKeymanFullFileName]]];
|
|
[(WKWebView *)webView loadRequest:[NSURLRequest requestWithURL:codeURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0]];
|
|
}
|
|
}
|
|
}
|
|
|
|
return webView;
|
|
}
|
|
else {
|
|
if (webView == nil) {
|
|
CGRect frame = CGRectMake(0.0f, 0.0f, [[self class] keyboardWidth], [[self class] keyboardHeight]);
|
|
webView = [[UIWebView alloc] initWithFrame:frame];
|
|
[(UIWebView *)webView setDataDetectorTypes:UIDataDetectorTypeNone];
|
|
[(UIWebView *)webView setOpaque:NO];
|
|
[(UIWebView *)webView setBackgroundColor:[UIColor clearColor]];
|
|
[(UIWebView *)webView setDelegate:[self sharedInstance]];
|
|
|
|
KMManager *km = [self sharedInstance];
|
|
[km keymanScrollView].scrollEnabled = NO;
|
|
|
|
NSURL *codeURL = [NSURL fileURLWithPath:[[km activeKeymanDirectory] stringByAppendingPathComponent:kKeymanFullFileName]];
|
|
[(UIWebView *)webView loadRequest:[NSURLRequest requestWithURL:codeURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0]];
|
|
}
|
|
|
|
return webView;
|
|
}
|
|
}
|
|
|
|
+ (Class)WKWebViewClass {
|
|
return NSClassFromString(@"WKWebView");
|
|
}
|
|
|
|
+ (BOOL)isWKWebView:(id)webView {
|
|
if (![self WKWebViewClass])
|
|
return NO;
|
|
|
|
return [webView isKindOfClass:[self WKWebViewClass]];
|
|
}
|
|
|
|
+ (void)setKMSelectionRange:(NSRange)range manually:(BOOL)manually {
|
|
if (range.location != NSNotFound) {
|
|
NSString *jsString = [NSString stringWithFormat:@"setCursorRange(%lu,%lu)", (unsigned long)range.location, (unsigned long)range.length];
|
|
UIView *webView = [self inputView];
|
|
if ([self isWKWebView:webView])
|
|
[(WKWebView *)webView evaluateJavaScript:jsString completionHandler:nil];
|
|
else
|
|
[(UIWebView *)webView stringByEvaluatingJavaScriptFromString:jsString];
|
|
}
|
|
}
|
|
|
|
+ (void)clearText {
|
|
[self setKMText:nil];
|
|
[self setKMSelectionRange:NSMakeRange(0, 0) manually:YES];
|
|
|
|
id firstResponder = [[[UIApplication sharedApplication] keyWindow] findFirstResponder];
|
|
[[self sharedInstance] KMLog:[NSString stringWithFormat:@"%@: 0x%x Cleared text.", [firstResponder class], (uint)firstResponder] checkDebugPrinting:YES];
|
|
}
|
|
|
|
+ (void)setKMText:(NSString *)text {
|
|
NSString *kmText = text?[text stringByReplacingOccurrencesOfString:@"\\" withString:@"\\u005C"]:@"";
|
|
kmText = [kmText stringByReplacingOccurrencesOfString:@"'" withString:@"\\u0027"];
|
|
kmText = [kmText stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"];
|
|
NSString *jsString = [NSString stringWithFormat:@"setKeymanVal('%@');", kmText];
|
|
UIView *webView = [self inputView];
|
|
if ([self isWKWebView:webView])
|
|
[(WKWebView *)webView evaluateJavaScript:jsString completionHandler:nil];
|
|
else
|
|
[(UIWebView *)webView stringByEvaluatingJavaScriptFromString:jsString];
|
|
}
|
|
|
|
// This function appears to fetch the keyboard metadata from r.keymanweb.com.
|
|
+ (void)fetchKeyboardsWithCompletionBlock:(KMFetchKeyboardsBlock)completionBlock {
|
|
__weak KMManager *_self = [KMManager sharedInstance];
|
|
if (_self.currentRequest != nil) return;
|
|
|
|
NSString *deviceType = ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)?@"iphone":@"ipad";
|
|
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@languages?device=%@", kKeymanApiBaseURL, deviceType]];
|
|
|
|
NSDictionary *userData = nil;
|
|
|
|
if(completionBlock != nil) {
|
|
userData = [NSDictionary dictionaryWithObjectsAndKeys:completionBlock, @"completionBlock", nil];
|
|
}
|
|
|
|
_self.currentRequest = [[KMHTTPDownloadRequest alloc] initWithURL:url downloadType:kmDownloadCachedData userInfo:userData];
|
|
|
|
[_self.sharedQueue addRequest:_self.currentRequest];
|
|
[_self.sharedQueue run];
|
|
}
|
|
|
|
- (NSArray *)keyboardsForIndex:(NSInteger)index {
|
|
NSArray *retVal = nil;
|
|
if (index < [self.languages count])
|
|
retVal = [[self.languages objectAtIndex:index] objectForKey:kKeymanLanguageKeyboardsKey];
|
|
|
|
return retVal;
|
|
}
|
|
|
|
- (NSDictionary *)keyboardInfoForLanguageIndex:(NSInteger)languageIndex keyboardIndex:(NSInteger)keyboardIndex {
|
|
NSDictionary *retVal = nil;
|
|
if (languageIndex < [self.languages count]) {
|
|
NSArray *keyboards = [[self.languages objectAtIndex:languageIndex] objectForKey:kKeymanLanguageKeyboardsKey];
|
|
if (keyboardIndex < [keyboards count])
|
|
retVal = [keyboards objectAtIndex:keyboardIndex];
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
- (void)downloadKeyboardFromUrl:(NSURL *)jsonUrl isDirect:(BOOL)isDirect {
|
|
NSDictionary *kbInfo = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
@"", kKeymanKeyboardIdKey,
|
|
@"", kKeymanLanguageIdKey,
|
|
@"", kKeymanKeyboardNameKey,
|
|
@"", kKeymanLanguageNameKey,
|
|
@"Y", kKeymanCustomKeyboardKey,
|
|
nil];
|
|
if ([self.reachability currentReachabilityStatus] == NotReachable) {
|
|
NSError *error = [NSError errorWithDomain:@"Keyman"
|
|
code:0
|
|
userInfo:[NSDictionary
|
|
dictionaryWithObject:@"No connection"
|
|
forKey:NSLocalizedDescriptionKey]];
|
|
[self downloadFailedForKeyboard:kbInfo error:error];
|
|
return;
|
|
}
|
|
|
|
NSError *error = nil;
|
|
BOOL failed = YES;
|
|
if (jsonUrl != nil) {
|
|
NSData *data = nil;
|
|
if (isDirect)
|
|
data = [[NSData alloc] initWithContentsOfURL:jsonUrl];
|
|
else {
|
|
NSString *deviceParam = ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)?@"iphone":@"ipad";
|
|
NSString *encodedUrl = [[jsonUrl absoluteString] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
|
NSURL *remoteURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@&device=%@", kKeymanApiRemoteURL, encodedUrl, deviceParam]];
|
|
data = [[NSData alloc] initWithContentsOfURL:remoteURL];
|
|
}
|
|
|
|
if (data != nil) {
|
|
id jsonObj = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
|
|
if (error == nil) {
|
|
NSDictionary *jsonDict = (NSDictionary *)jsonObj;
|
|
[self downloadKeyboardFromDictionary:jsonDict];
|
|
failed = NO;
|
|
}
|
|
}
|
|
else {
|
|
error = [NSError errorWithDomain:@"Keyman"
|
|
code:0
|
|
userInfo:[NSDictionary
|
|
dictionaryWithObject:@"Failed to fetch JSON file"
|
|
forKey:NSLocalizedDescriptionKey]];
|
|
}
|
|
}
|
|
|
|
if (failed) {
|
|
if (error == nil) {
|
|
error = [NSError errorWithDomain:@"Keyman"
|
|
code:0
|
|
userInfo:[NSDictionary
|
|
dictionaryWithObject:@"The keyboard could not be installed"
|
|
forKey:NSLocalizedDescriptionKey]];
|
|
}
|
|
[self downloadFailedForKeyboard:kbInfo error:error];
|
|
}
|
|
}
|
|
|
|
- (void)downloadKeyboardFromDictionary:(NSDictionary *)jsonDict {
|
|
NSDictionary *kbInfo = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
@"", kKeymanKeyboardIdKey,
|
|
@"", kKeymanLanguageIdKey,
|
|
@"", kKeymanKeyboardNameKey,
|
|
@"", kKeymanLanguageNameKey,
|
|
@"", kKeymanKeyboardVersionKey,
|
|
@"", kKeymanKeyboardRTLKey,
|
|
@"Y", kKeymanCustomKeyboardKey,
|
|
nil];
|
|
|
|
@try {
|
|
NSDictionary *options = [jsonDict objectForKey:kKeymanOptionsKey];
|
|
NSString *kbBaseUri = [options objectForKey:kKeymanKeyboardBaseURIKey];
|
|
NSString *fontBaseUri = [options objectForKey:kKeymanFontBaseURIKey];
|
|
|
|
NSDictionary *keyboard = [jsonDict objectForKey:kKeymanKeyboardKey];
|
|
NSString *fileName = [keyboard objectForKey:kKeymanKeyboardFilenameKey];
|
|
NSString *kbID = [keyboard objectForKey:kKeymanIdKey];
|
|
NSString *kbName = [keyboard objectForKey:kKeymanNameKey];
|
|
NSString *kbVersion = [keyboard objectForKey:kKeymanKeyboardVersionKey];
|
|
if (kbVersion == nil)
|
|
kbVersion = @"1.0";
|
|
NSString *isRTL = [keyboard objectForKey:kKeymanKeyboardRTLKey];
|
|
isRTL = (isRTL == nil)?@"N":@"Y";
|
|
NSArray *languages = [keyboard objectForKey:kKeymanLanguagesKey];
|
|
NSDictionary *kbFont = [keyboard objectForKey:kKeymanFontKey];
|
|
NSDictionary *kbOskFont = [keyboard objectForKey:kKeymanOskFontKey];
|
|
|
|
NSString *jsFont = [self jsFontFromFontDictionary:kbFont];
|
|
NSString *jsOskFont = kbOskFont?[self jsFontFromFontDictionary:kbOskFont]:nil;
|
|
|
|
if (keyboard == nil || fileName == nil || kbID == nil || kbName == nil || languages == nil) {
|
|
NSError *error = [NSError errorWithDomain:@"Keyman"
|
|
code:0
|
|
userInfo:[NSDictionary
|
|
dictionaryWithObject:@"The keyboard could not be installed"
|
|
forKey:NSLocalizedDescriptionKey]];
|
|
[self downloadFailedForKeyboard:kbInfo error:error];
|
|
return;
|
|
}
|
|
|
|
NSDictionary *keyboardInfo = nil;
|
|
NSMutableArray *keyboardsInfoArray = nil;
|
|
if ([languages count] > 1) {
|
|
keyboardsInfoArray = [NSMutableArray arrayWithCapacity:[languages count]];
|
|
for (NSDictionary *language in languages) {
|
|
NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:kbID, kKeymanKeyboardIdKey,
|
|
[language objectForKey:kKeymanIdKey], kKeymanLanguageIdKey,
|
|
kbName, kKeymanKeyboardNameKey,
|
|
[language objectForKey:kKeymanNameKey], kKeymanLanguageNameKey,
|
|
kbVersion, kKeymanKeyboardVersionKey,
|
|
isRTL, kKeymanKeyboardRTLKey,
|
|
@"Y", kKeymanCustomKeyboardKey,
|
|
jsFont, kKeymanFontKey,
|
|
jsOskFont, kKeymanOskFontKey, nil];
|
|
[keyboardsInfoArray addObject:info];
|
|
}
|
|
}
|
|
else {
|
|
keyboardInfo = [NSDictionary dictionaryWithObjectsAndKeys:kbID, kKeymanKeyboardIdKey,
|
|
[[languages objectAtIndex:0] objectForKey:kKeymanIdKey], kKeymanLanguageIdKey,
|
|
kbName, kKeymanKeyboardNameKey,
|
|
[[languages objectAtIndex:0] objectForKey:kKeymanNameKey], kKeymanLanguageNameKey,
|
|
kbVersion, kKeymanKeyboardVersionKey,
|
|
isRTL, kKeymanKeyboardRTLKey,
|
|
@"Y", kKeymanCustomKeyboardKey,
|
|
jsFont, kKeymanFontKey,
|
|
jsOskFont, kKeymanOskFontKey, nil];
|
|
}
|
|
|
|
if (keyboardsInfoArray != nil)
|
|
kbInfo = [NSDictionary dictionaryWithObjectsAndKeys:keyboardsInfoArray, kKeymanKeyboardInfoKey, nil];
|
|
else
|
|
kbInfo = [NSDictionary dictionaryWithObjectsAndKeys:keyboardInfo, kKeymanKeyboardInfoKey, nil];
|
|
|
|
if (self.downloadQueue != nil) {
|
|
// Download queue is active.
|
|
NSError *error = [NSError errorWithDomain:@"Keyman"
|
|
code:0
|
|
userInfo:[NSDictionary
|
|
dictionaryWithObject:@"Download queue is busy"
|
|
forKey:NSLocalizedDescriptionKey]];
|
|
[self downloadFailedForKeyboard:kbInfo error:error];
|
|
return;
|
|
}
|
|
|
|
if ([self.reachability currentReachabilityStatus] == NotReachable) {
|
|
NSError *error = [NSError errorWithDomain:@"Keyman"
|
|
code:0
|
|
userInfo:[NSDictionary
|
|
dictionaryWithObject:@"No internet connection"
|
|
forKey:NSLocalizedDescriptionKey]];
|
|
[self downloadFailedForKeyboard:kbInfo error:error];
|
|
return;
|
|
}
|
|
|
|
NSString *keyboardPath = [self keyboardPathForFilename:fileName keyboardVersion:kbVersion];
|
|
NSURL *keyboardURL = [NSURL URLWithString:[kbBaseUri stringByAppendingString:fileName]];
|
|
|
|
NSMutableArray *keyboardFontURLs;
|
|
id fontSource = nil;
|
|
if (kbFont != nil && kbFont.count) {
|
|
fontSource = [kbFont objectForKey:kKeymanFontSourceKey];
|
|
if (fontSource == nil)
|
|
fontSource = [kbFont objectForKey:kKeymanFontFilenameKey]; // Font filename is deprecated
|
|
if ([fontSource isKindOfClass:[NSArray class]]) {
|
|
keyboardFontURLs = [NSMutableArray arrayWithCapacity:[fontSource count]];
|
|
for (NSString *source in fontSource) {
|
|
if ([source rangeOfString:@".ttf" options:NSBackwardsSearch].location != NSNotFound || [source rangeOfString:@".otf" options:NSBackwardsSearch].location != NSNotFound) {
|
|
[keyboardFontURLs addObject:[NSURL URLWithString:[fontBaseUri stringByAppendingString:source]]];
|
|
}
|
|
}
|
|
}
|
|
else if ([fontSource isKindOfClass:[NSString class]]) {
|
|
keyboardFontURLs = [NSMutableArray arrayWithCapacity:1];
|
|
NSString *source = (NSString *)fontSource;
|
|
if ([source rangeOfString:@".ttf" options:NSBackwardsSearch].location != NSNotFound || [source rangeOfString:@".otf" options:NSBackwardsSearch].location != NSNotFound) {
|
|
[keyboardFontURLs addObject:[NSURL URLWithString:[fontBaseUri stringByAppendingString:source]]];
|
|
}
|
|
}
|
|
else {
|
|
NSError *error = [NSError errorWithDomain:@"Keyman" code:0 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Unexpected error: %@ is not a valid type to be processed.", [fontSource class]] forKey:NSLocalizedDescriptionKey]];
|
|
[self downloadFailedForKeyboard:kbInfo error:error];
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (kbOskFont != nil && kbOskFont.count) {
|
|
fontSource = [kbOskFont objectForKey:kKeymanFontSourceKey];
|
|
if (fontSource == nil)
|
|
fontSource = [kbOskFont objectForKey:kKeymanFontFilenameKey]; // Font filename is deprecated
|
|
if ([fontSource isKindOfClass:[NSArray class]]) {
|
|
if (keyboardFontURLs == nil)
|
|
keyboardFontURLs = [NSMutableArray arrayWithCapacity:[fontSource count]];
|
|
for (NSString *source in fontSource) {
|
|
if ([source rangeOfString:@".ttf" options:NSBackwardsSearch].location != NSNotFound || [source rangeOfString:@".otf" options:NSBackwardsSearch].location != NSNotFound) {
|
|
NSURL *fontUrl = [NSURL URLWithString:[fontBaseUri stringByAppendingString:source]];
|
|
if (![keyboardFontURLs containsObject:fontUrl])
|
|
[keyboardFontURLs addObject:fontUrl];
|
|
}
|
|
}
|
|
}
|
|
else if ([fontSource isKindOfClass:[NSString class]]) {
|
|
if (keyboardFontURLs == nil)
|
|
keyboardFontURLs = [NSMutableArray arrayWithCapacity:1];
|
|
NSString *source = (NSString *)fontSource;
|
|
if ([source rangeOfString:@".ttf" options:NSBackwardsSearch].location != NSNotFound || [source rangeOfString:@".otf" options:NSBackwardsSearch].location != NSNotFound) {
|
|
NSURL *fontUrl = [NSURL URLWithString:[fontBaseUri stringByAppendingString:source]];
|
|
if (![keyboardFontURLs containsObject:fontUrl])
|
|
[keyboardFontURLs addObject:fontUrl];
|
|
}
|
|
}
|
|
else {
|
|
NSError *error = [NSError errorWithDomain:@"Keyman" code:0 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Unexpected error: %@ is not a valid type to be processed.", [fontSource class]] forKey:NSLocalizedDescriptionKey]];
|
|
[self downloadFailedForKeyboard:kbInfo error:error];
|
|
return;
|
|
}
|
|
}
|
|
|
|
BOOL isUpdate = ([self latestKeyboardFileVersionWithID:kbID] != nil);
|
|
|
|
|
|
[self setDownloadQueue:[[KMHTTPDownloader alloc] init:self]];
|
|
NSDictionary *commonUserData = [NSDictionary dictionaryWithObjectsAndKeys:[kbInfo objectForKey:kKeymanKeyboardInfoKey], kKeymanKeyboardInfoKey,
|
|
[NSNumber numberWithBool:isUpdate], kKeymanUpdateKey, nil];
|
|
|
|
[self.downloadQueue setUserInfo:commonUserData];
|
|
|
|
KMHTTPDownloadRequest *request = [[KMHTTPDownloadRequest alloc] initWithURL:keyboardURL userInfo:commonUserData];
|
|
[request setDestinationFile:keyboardPath];
|
|
[request setTag:0];
|
|
|
|
[self.downloadQueue addRequest:request];
|
|
|
|
if (keyboardFontURLs != nil) {
|
|
for (int i=0; i<[keyboardFontURLs count]; i++) {
|
|
NSString *fontPath = [self fontPathForFilename:[[keyboardFontURLs objectAtIndex:i] lastPathComponent]];
|
|
|
|
request = [[KMHTTPDownloadRequest alloc] initWithURL:[[NSURL alloc] initWithString:fontPath] userInfo:commonUserData];
|
|
[request setDestinationFile:fontPath];
|
|
[request setTag:i+1];
|
|
[self.downloadQueue addRequest:request];
|
|
}
|
|
}
|
|
|
|
[self.downloadQueue run];
|
|
}
|
|
@catch (NSException *exception) {
|
|
if ([[exception description] rangeOfString:@"unrecognized selector"].location != NSNotFound) {
|
|
NSError *error = [NSError errorWithDomain:@"Keyman"
|
|
code:0
|
|
userInfo:[NSDictionary
|
|
dictionaryWithObject:@"Invalid JSON file"
|
|
forKey:NSLocalizedDescriptionKey]];
|
|
[self downloadFailedForKeyboard:kbInfo error:error];
|
|
}
|
|
else {
|
|
NSError *error = [NSError errorWithDomain:@"Keyman"
|
|
code:0
|
|
userInfo:[NSDictionary
|
|
dictionaryWithObject:[exception description]
|
|
forKey:NSLocalizedDescriptionKey]];
|
|
[self downloadFailedForKeyboard:kbInfo error:error];
|
|
}
|
|
}
|
|
@finally {
|
|
//
|
|
}
|
|
}
|
|
|
|
- (void)downloadKeyboardForLanguageIndex:(NSInteger)languageIndex keyboardIndex:(NSInteger)keyboardIndex isUpdate:(BOOL)isUpdate {
|
|
if (languageIndex >= [self.languages count]) return;
|
|
|
|
NSArray *keyboards = [[self.languages objectAtIndex:languageIndex] objectForKey:kKeymanLanguageKeyboardsKey];
|
|
if (keyboardIndex >= [keyboards count]) return;
|
|
|
|
NSDictionary *keyboardDict = [keyboards objectAtIndex:keyboardIndex];
|
|
NSString *kbID = [keyboardDict objectForKey:kKeymanIdKey];
|
|
NSString *langID = [[self.languages objectAtIndex:languageIndex] objectForKey:kKeymanIdKey];
|
|
|
|
[self downloadKeyboardWithID:kbID languageID:langID isUpdate:isUpdate];
|
|
}
|
|
|
|
- (void)downloadKeyboardWithID:(NSString *)keyboardID languageID:(NSString *)languageID isUpdate:(BOOL)isUpdate {
|
|
NSDictionary *kbInfo = [NSDictionary dictionaryWithObjectsAndKeys:keyboardID, kKeymanKeyboardIdKey,
|
|
languageID, kKeymanLanguageIdKey,
|
|
nil];
|
|
|
|
if (self.downloadQueue != nil) {
|
|
// Download queue is active.
|
|
return;
|
|
}
|
|
|
|
if ([self.reachability currentReachabilityStatus] == NotReachable) {
|
|
NSError *error = [NSError errorWithDomain:@"Keyman" code:0 userInfo:[NSDictionary dictionaryWithObject:@"No internet connection" forKey:NSLocalizedDescriptionKey]];
|
|
[self downloadFailedForKeyboard:kbInfo error:error];
|
|
return;
|
|
}
|
|
|
|
if (self.keyboardsInfo == nil) {
|
|
NSError *error = [NSError errorWithDomain:@"Keyman" code:0 userInfo:[NSDictionary dictionaryWithObject:@"Keyboard info has not yet been fetched. Call -[KMManager fetchKeyboardsList] first." forKey:NSLocalizedDescriptionKey]];
|
|
[self downloadFailedForKeyboard:kbInfo error:error];
|
|
return;
|
|
}
|
|
|
|
NSString *kbKey = [NSString stringWithFormat:@"%@_%@", languageID, keyboardID];
|
|
NSDictionary *keyboardDict = [self.keyboardsDictionary objectForKey:kbKey];
|
|
if (keyboardDict == nil) {
|
|
NSError *error = [NSError errorWithDomain:@"Keyman" code:0 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Keyboard not found with key: %@", kbKey] forKey:NSLocalizedDescriptionKey]];
|
|
[self downloadFailedForKeyboard:kbInfo error:error];
|
|
return;
|
|
}
|
|
|
|
NSString *urlStr = [[self.keyboardsInfo objectForKey:keyboardID] objectForKey:kKeymanKeyboardURIKey];
|
|
NSURL *keyboardURL = [NSURL URLWithString:urlStr];
|
|
NSMutableArray *keyboardFontURLs = nil;
|
|
NSString *kbFontStr = [keyboardDict objectForKey:kKeymanFontKey];
|
|
NSDictionary *kbFont = [NSJSONSerialization JSONObjectWithData:[kbFontStr dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:nil];
|
|
|
|
id fontSource = nil;
|
|
if (kbFont != nil) {
|
|
fontSource = [kbFont objectForKey:kKeymanFontFilesKey];
|
|
if ([fontSource isKindOfClass:[NSArray class]]) {
|
|
keyboardFontURLs = [NSMutableArray arrayWithCapacity:[fontSource count]];
|
|
for (NSString *source in fontSource) {
|
|
if ([source rangeOfString:@".ttf" options:NSBackwardsSearch].location != NSNotFound || [source rangeOfString:@".otf" options:NSBackwardsSearch].location != NSNotFound) {
|
|
[keyboardFontURLs addObject:[NSURL URLWithString:[NSString stringWithFormat:@"%@%@", [self.options objectForKey:kKeymanFontBaseURIKey], source]]];
|
|
}
|
|
}
|
|
}
|
|
else if ([fontSource isKindOfClass:[NSString class]]) {
|
|
keyboardFontURLs = [NSMutableArray arrayWithCapacity:1];
|
|
NSString *source = (NSString *)fontSource;
|
|
if ([source rangeOfString:@".ttf" options:NSBackwardsSearch].location != NSNotFound || [source rangeOfString:@".otf" options:NSBackwardsSearch].location != NSNotFound) {
|
|
[keyboardFontURLs addObject:[NSURL URLWithString:[NSString stringWithFormat:@"%@%@", [self.options objectForKey:kKeymanFontBaseURIKey], source]]];
|
|
}
|
|
}
|
|
else {
|
|
NSError *error = [NSError errorWithDomain:@"Keyman" code:0 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Unexpected error: %@ is not a valid type to be processed.", [fontSource class]] forKey:NSLocalizedDescriptionKey]];
|
|
[self downloadFailedForKeyboard:kbInfo error:error];
|
|
return;
|
|
}
|
|
}
|
|
|
|
NSString *kbOskFontStr = [keyboardDict objectForKey:kKeymanOskFontKey];
|
|
if (kbOskFontStr == nil)
|
|
kbOskFontStr = @"";
|
|
|
|
NSDictionary *kbOskFont = [NSJSONSerialization JSONObjectWithData:[kbOskFontStr dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:nil];
|
|
if (kbOskFont != nil) {
|
|
fontSource = [kbOskFont objectForKey:kKeymanFontFilesKey];
|
|
if ([fontSource isKindOfClass:[NSArray class]]) {
|
|
if (keyboardFontURLs == nil)
|
|
keyboardFontURLs = [NSMutableArray arrayWithCapacity:[fontSource count]];
|
|
for (NSString *source in fontSource) {
|
|
if ([source rangeOfString:@".ttf" options:NSBackwardsSearch].location != NSNotFound || [source rangeOfString:@".otf" options:NSBackwardsSearch].location != NSNotFound) {
|
|
NSURL *fontUrl = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", [self.options objectForKey:kKeymanFontBaseURIKey], source]];
|
|
if (![keyboardFontURLs containsObject:fontUrl])
|
|
[keyboardFontURLs addObject:fontUrl];
|
|
}
|
|
}
|
|
}
|
|
else if ([fontSource isKindOfClass:[NSString class]]) {
|
|
if (keyboardFontURLs == nil)
|
|
keyboardFontURLs = [NSMutableArray arrayWithCapacity:1];
|
|
NSString *source = (NSString *)fontSource;
|
|
if ([source rangeOfString:@".ttf" options:NSBackwardsSearch].location != NSNotFound || [source rangeOfString:@".otf" options:NSBackwardsSearch].location != NSNotFound) {
|
|
NSURL *fontUrl = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", [self.options objectForKey:kKeymanFontBaseURIKey], source]];
|
|
if (![keyboardFontURLs containsObject:fontUrl])
|
|
[keyboardFontURLs addObject:fontUrl];
|
|
}
|
|
}
|
|
else {
|
|
NSError *error = [NSError errorWithDomain:@"Keyman" code:0 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Unexpected error: %@ is not a valid type to be processed.", [fontSource class]] forKey:NSLocalizedDescriptionKey]];
|
|
[self downloadFailedForKeyboard:kbInfo error:error];
|
|
return;
|
|
}
|
|
}
|
|
|
|
[self setDownloadQueue:[[KMHTTPDownloader alloc] init:self]];
|
|
NSDictionary *commonUserData = [NSDictionary dictionaryWithObjectsAndKeys:kbInfo, kKeymanKeyboardInfoKey,
|
|
[NSNumber numberWithBool:isUpdate], kKeymanUpdateKey, nil];
|
|
[self.downloadQueue setUserInfo:commonUserData];
|
|
|
|
NSString *kbVersion = [keyboardDict objectForKey:kKeymanKeyboardVersionKey];
|
|
NSString *keyboardPath = [self keyboardPathForFilename:[keyboardURL lastPathComponent] keyboardVersion:kbVersion];
|
|
|
|
KMHTTPDownloadRequest *request = [[KMHTTPDownloadRequest alloc] initWithURL:keyboardURL userInfo:commonUserData];
|
|
[request setDestinationFile:keyboardPath];
|
|
[request setTag:0];
|
|
|
|
[self.downloadQueue addRequest:request];
|
|
|
|
if (keyboardFontURLs != nil) {
|
|
for (int i=0; i<[keyboardFontURLs count]; i++) {
|
|
NSString *fontPath = [self fontPathForFilename:[[keyboardFontURLs objectAtIndex:i] lastPathComponent]];
|
|
|
|
request = [[KMHTTPDownloadRequest alloc] initWithURL:[keyboardFontURLs objectAtIndex:i] userInfo:commonUserData];
|
|
[request setDestinationFile:fontPath];
|
|
[request setTag:i+1];
|
|
[self.downloadQueue addRequest:request];
|
|
}
|
|
}
|
|
|
|
[self.downloadQueue run];
|
|
}
|
|
|
|
- (void)downloadQueueFinished:(KMHTTPDownloader *)queue {
|
|
if (self.debugPrintingOn) {
|
|
NSArray *directoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[self activeFontDirectory] error:nil];
|
|
[self KMLog:[NSString stringWithFormat:@"Font Directory: %@", directoryContents] checkDebugPrinting:YES];
|
|
NSArray *directoryContents2 = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[self activeLanguageDirectory] error:nil];
|
|
[self KMLog:[NSString stringWithFormat:@"Language Directory: %@", directoryContents2] checkDebugPrinting:YES];
|
|
}
|
|
}
|
|
|
|
- (void)downloadRequestStarted:(KMHTTPDownloadRequest *)request {
|
|
// If we're downloading a new keyboard. The extra check is there to filter out other potential request types in the future.
|
|
if (request.tag == 0 && request.typeCode == kmDownloadFile) {
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:kKeymanKeyboardDownloadStartedNotification
|
|
object:self
|
|
userInfo:[request userInfo]];
|
|
}
|
|
}
|
|
|
|
- (void)downloadRequestFinished:(KMHTTPDownloadRequest *)request {
|
|
|
|
if(request.typeCode == kmDownloadFile) {
|
|
|
|
NSString *kbId = nil;
|
|
NSString *langId = nil;
|
|
NSString *kbVersion = nil;
|
|
id kbInfo = [[request userInfo] objectForKey:kKeymanKeyboardInfoKey];
|
|
if ([kbInfo isKindOfClass:[NSDictionary class]]) {
|
|
kbId = [(NSDictionary *)kbInfo objectForKey:kKeymanKeyboardIdKey];
|
|
langId = [(NSDictionary *)kbInfo objectForKey:kKeymanLanguageIdKey];
|
|
kbVersion = [(NSDictionary *)kbInfo objectForKey:kKeymanKeyboardVersionKey];
|
|
}
|
|
else {
|
|
kbId = [[(NSArray *)kbInfo objectAtIndex:0] objectForKey:kKeymanKeyboardIdKey];
|
|
langId = [[(NSArray *)kbInfo objectAtIndex:0] objectForKey:kKeymanLanguageIdKey];
|
|
kbVersion = [[(NSArray *)kbInfo objectAtIndex:0] objectForKey:kKeymanKeyboardVersionKey];
|
|
}
|
|
BOOL isUpdate = [[[request userInfo] objectForKey:kKeymanUpdateKey] boolValue];
|
|
|
|
int statusCode = (int)[request responseStatusCode];
|
|
if (statusCode == 200) { // The request has succeeded.
|
|
if ([self.downloadQueue requestsCount] == 0) { // Download queue finished.
|
|
[self setDownloadQueue:nil];
|
|
[self registerCustomFonts];
|
|
[self KMLog:[NSString stringWithFormat:@"Downloaded keyboard: %@.", kbId] checkDebugPrinting:YES];
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:kKeymanKeyboardDownloadCompletedNotification
|
|
object:self
|
|
userInfo:[request userInfo]];
|
|
if (isUpdate) {
|
|
shouldReloadKeyboard = YES;
|
|
[self reloadKeyboard];
|
|
}
|
|
|
|
NSUserDefaults *userData = [self activeUserDefaults];
|
|
[userData setObject:[NSArray arrayWithObject:[NSDate date]] forKey:kKeymanSynchronizeSWKeyboardKey];
|
|
[userData synchronize];
|
|
}
|
|
}
|
|
else { // Possible request error (400 Bad Request, 404 Not Found, etc.)
|
|
[self.downloadQueue cancelAllOperations];
|
|
[self setDownloadQueue:nil];
|
|
|
|
NSError *error = [NSError errorWithDomain:@"Keyman" code:0 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"%@ : %@", [request responseStatusMessage], [request url]] forKey:NSLocalizedDescriptionKey]];
|
|
[self KMLog:[NSString stringWithFormat:@"Keyboard download failed: %@.", error] checkDebugPrinting:YES];
|
|
|
|
if (!isUpdate) {
|
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
NSString *fileName = [[request url] lastPathComponent];
|
|
if ([fileName rangeOfString:@".js" options:NSBackwardsSearch].location != NSNotFound) {
|
|
NSString *kbPath = [self keyboardPathForFilename:fileName keyboardVersion:kbVersion];
|
|
if ([fileManager fileExistsAtPath:kbPath])
|
|
[fileManager removeItemAtPath:kbPath error:nil];
|
|
}
|
|
|
|
if ([fileName rangeOfString:@".ttf" options:NSBackwardsSearch].location != NSNotFound || [fileName rangeOfString:@".otf" options:NSBackwardsSearch].location != NSNotFound) {
|
|
NSString *kbPath = [self keyboardPathForID:kbId keyboardVersion:kbVersion];
|
|
if ([fileManager fileExistsAtPath:kbPath])
|
|
[fileManager removeItemAtPath:kbPath error:nil];
|
|
|
|
NSString *fontPath = [[self activeFontDirectory] stringByAppendingPathComponent:fileName];
|
|
if ([fileManager fileExistsAtPath:fontPath])
|
|
[fileManager removeItemAtPath:fontPath error:nil];
|
|
}
|
|
}
|
|
|
|
[self downloadFailedForKeyboard:[request userInfo] error:error];
|
|
}
|
|
} else if(request.typeCode == kmDownloadCachedData) {
|
|
// TODO: Implement.
|
|
|
|
if(request == self.currentRequest) {
|
|
@try {
|
|
NSError *e;
|
|
NSDictionary *responseDict = [NSJSONSerialization JSONObjectWithData:[self.currentRequest rawResponseData] options:NSJSONReadingMutableContainers error:&e];
|
|
self.options = [responseDict objectForKey:kKeymanOptionsKey];
|
|
|
|
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:kKeymanNameKey ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)];
|
|
self.languages = [[[responseDict objectForKey:kKeymanLanguagesKey] objectForKey:kKeymanLanguagesKey] sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]];
|
|
[self createKeyboardsInfo];
|
|
|
|
[self KMLog:[NSString stringWithFormat:@"Request completed -- %lu languages.", (unsigned long)[self.languages count]] checkDebugPrinting:YES];
|
|
|
|
KMFetchKeyboardsBlock completionBlock;
|
|
if(self.currentRequest.userInfo != nil) {
|
|
completionBlock = [self.currentRequest.userInfo objectForKey:@"completionBlock"];
|
|
}
|
|
|
|
self.currentRequest = nil;
|
|
|
|
if (completionBlock != nil) {
|
|
completionBlock(nil);
|
|
}
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:kKeymanLanguagesUpdatedNotification object:self];
|
|
}
|
|
@catch(NSException *exception) {
|
|
[self KMLog:[NSString stringWithFormat:@"Failed: %@.", exception.reason] checkDebugPrinting:YES];
|
|
|
|
NSError *error = [NSError errorWithDomain:@"Keyman" code:0 userInfo:[NSDictionary dictionaryWithObject:exception.reason forKey:NSLocalizedDescriptionKey]];
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:kKeymanLanguagesDownloadFailedNotification object:self userInfo:[NSDictionary dictionaryWithObject:error forKey:NSUnderlyingErrorKey]];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)downloadRequestFailed:(KMHTTPDownloadRequest *)request {
|
|
if(request.typeCode == kmDownloadFile) {
|
|
[self setDownloadQueue:nil];
|
|
|
|
NSError *error = [request error];
|
|
if (error == nil) error = [NSError errorWithDomain:@"Keyman" code:0 userInfo:nil];
|
|
[self KMLog:[NSString stringWithFormat:@"Keyboard download failed: %@.", error] checkDebugPrinting:YES];
|
|
|
|
NSString *kbId = nil;
|
|
NSString *kbVersion = nil;
|
|
id kbInfo = [[request userInfo] objectForKey:kKeymanKeyboardInfoKey];
|
|
if ([kbInfo isKindOfClass:[NSDictionary class]]) {
|
|
kbId = [(NSDictionary *)kbInfo objectForKey:kKeymanKeyboardIdKey];
|
|
kbVersion = [(NSDictionary *)kbInfo objectForKey:kKeymanKeyboardVersionKey];
|
|
}
|
|
else {
|
|
kbId = [[(NSArray *)kbInfo objectAtIndex:0] objectForKey:kKeymanKeyboardIdKey];
|
|
kbVersion = [[(NSArray *)kbInfo objectAtIndex:0] objectForKey:kKeymanKeyboardVersionKey];
|
|
}
|
|
|
|
BOOL isUpdate = [[[request userInfo] objectForKey:kKeymanUpdateKey] boolValue];
|
|
if (!isUpdate) {
|
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
NSString *fileName = [[request url] lastPathComponent];
|
|
if ([fileName rangeOfString:@".js" options:NSBackwardsSearch].location != NSNotFound) {
|
|
NSString *kbPath = [self keyboardPathForFilename:fileName keyboardVersion:kbVersion];
|
|
if ([fileManager fileExistsAtPath:kbPath])
|
|
[fileManager removeItemAtPath:kbPath error:nil];
|
|
}
|
|
|
|
if ([fileName rangeOfString:@".ttf" options:NSBackwardsSearch].location != NSNotFound || [fileName rangeOfString:@".otf" options:NSBackwardsSearch].location != NSNotFound) {
|
|
NSString *kbPath = [self keyboardPathForID:kbId keyboardVersion:kbVersion];
|
|
if ([fileManager fileExistsAtPath:kbPath])
|
|
[fileManager removeItemAtPath:kbPath error:nil];
|
|
}
|
|
}
|
|
|
|
[self downloadFailedForKeyboard:[request userInfo] error:error];
|
|
} else if(request.typeCode == kmDownloadCachedData) {
|
|
if(request == self.currentRequest) {
|
|
NSError *error = [self.currentRequest error];
|
|
|
|
[self KMLog:[NSString stringWithFormat:@"Failed: %@.", error] checkDebugPrinting:YES];
|
|
|
|
KMFetchKeyboardsBlock completionBlock;
|
|
if(self.currentRequest.userInfo != nil) {
|
|
completionBlock = [self.currentRequest.userInfo objectForKey:@"completionBlock"];
|
|
}
|
|
|
|
self.currentRequest = nil;
|
|
|
|
if (completionBlock != nil) {
|
|
completionBlock([NSDictionary dictionaryWithObject:error forKey:NSUnderlyingErrorKey]);
|
|
}
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:kKeymanLanguagesDownloadFailedNotification object:self userInfo:[NSDictionary dictionaryWithObject:error forKey:NSUnderlyingErrorKey]];
|
|
};
|
|
}
|
|
}
|
|
|
|
- (void)downloadFailedForKeyboard:(NSDictionary *)keyboardInfo error:(NSError *)error {
|
|
id kbInfo = [keyboardInfo objectForKey:kKeymanKeyboardInfoKey];
|
|
if (kbInfo == nil)
|
|
kbInfo = keyboardInfo;
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:kKeymanKeyboardDownloadFailedNotification
|
|
object:self
|
|
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
|
|
kbInfo, kKeymanKeyboardInfoKey,
|
|
error, NSUnderlyingErrorKey,
|
|
nil]];
|
|
}
|
|
|
|
- (eKMKeyboardState)stateForKeyboardWithID:(NSString *)keyboardID {
|
|
eKMKeyboardState retVal = kKMKeyboardStateNone;
|
|
|
|
NSString *latestVersion = [self latestKeyboardFileVersionWithID:keyboardID];
|
|
|
|
if ([[self keyboardIdForCurrentRequest] isEqualToString:keyboardID]) {
|
|
retVal = kKMKeyboardStateDownloading;
|
|
} else if (latestVersion == nil) {
|
|
retVal = kKMKeyboardStateNeedsDownload;
|
|
}
|
|
else {
|
|
// Check version
|
|
if (self.keyboardsInfo != nil) {
|
|
NSString *kbVersion = [[self.keyboardsInfo objectForKey:keyboardID] objectForKey:kKeymanKeyboardVersionKey];
|
|
if (kbVersion == nil)
|
|
kbVersion = @"1.0";
|
|
|
|
if ([self compareVersions:latestVersion v2:kbVersion] >= 0)
|
|
retVal = kKMKeyboardStateUpToDate;
|
|
else
|
|
retVal = kKMKeyboardStateNeedsUpdate;
|
|
}
|
|
else
|
|
retVal = kKMKeyboardStateUpToDate;
|
|
}
|
|
|
|
/* // Check modified date (No longer used)
|
|
NSDictionary *attrs = [fileMan attributesOfItemAtPath:filePath error:nil];
|
|
NSDate *localModDate = [attrs objectForKey:NSFileModificationDate];
|
|
|
|
NSDate *modDate = [self dateFromKeymanDateString:[[self.keyboardsInfo objectForKey:keyboardID] objectForKey:kKeymanKeyboardModifiedKey]];
|
|
if (modDate != nil && localModDate != nil && [localModDate timeIntervalSinceDate:modDate] < 0)
|
|
retVal = kKMKeyboardStateNeedsUpdate;
|
|
else
|
|
retVal = kKMKeyboardStateUpToDate;
|
|
*/
|
|
|
|
return retVal;
|
|
}
|
|
|
|
- (NSString *)latestKeyboardFileVersionWithID:(NSString *)keyboardID {
|
|
NSString *kbFileVersion = nil;
|
|
NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[self activeLanguageDirectory] error:nil];
|
|
for (NSString *filename in dirContents) {
|
|
NSInteger index = [filename rangeOfString:[NSString stringWithFormat:@"%@-", keyboardID]].location;
|
|
if (index == 0) {
|
|
NSInteger firstIndex = [filename rangeOfString:@"-" options:NSBackwardsSearch].location+1;
|
|
NSInteger lastIndex = [filename rangeOfString:@".js" options:NSBackwardsSearch].location;
|
|
NSRange range = NSMakeRange(firstIndex, lastIndex - firstIndex);
|
|
NSString *v = [filename substringWithRange:range];
|
|
if (kbFileVersion != nil) {
|
|
if ([self compareVersions:v v2:kbFileVersion] > 0)
|
|
kbFileVersion = [v copy];
|
|
}
|
|
else if ([self compareVersions:v v2:v] == 0)
|
|
kbFileVersion = [v copy];
|
|
}
|
|
}
|
|
|
|
return kbFileVersion;
|
|
}
|
|
|
|
- (NSInteger)compareVersions:(NSString *)v1 v2:(NSString *)v2 {
|
|
// returns;
|
|
// -2 if v1 or v2 is invalid
|
|
// 0 if v1 = v2
|
|
// -1 if v1 < v2
|
|
// 1 if v1 > v2
|
|
|
|
if (v1 == nil || v2 == nil)
|
|
return -2;
|
|
|
|
if (!v1.length || !v2.length)
|
|
return -2;
|
|
|
|
NSArray *v1Values = [v1 componentsSeparatedByString:@"."];
|
|
NSArray *v2Values = [v2 componentsSeparatedByString:@"."];
|
|
|
|
NSUInteger len = (v1Values.count >= v2Values.count?v1Values.count:v2Values.count);
|
|
for (int i = 0; i < len; i++) {
|
|
NSString *vStr1 = @"0";
|
|
if (i < v1Values.count)
|
|
vStr1 = [v1Values objectAtIndex:i];
|
|
|
|
NSString *vStr2 = @"0";
|
|
if (i < v2Values.count)
|
|
vStr2 = [v2Values objectAtIndex:i];
|
|
|
|
NSNumberFormatter *numFormatter = [[NSNumberFormatter alloc] init];
|
|
NSNumber *vInt1 = [numFormatter numberFromString:vStr1];
|
|
NSNumber *vInt2 = [numFormatter numberFromString:vStr2];
|
|
int iV1, iV2, iV1_, iV2_;
|
|
|
|
if (vInt1 != nil) { iV1 = [vInt1 intValue]; iV1_ = 0; }
|
|
else { iV1 = 0; iV1_ = 0; }
|
|
|
|
if (vInt2 != nil) { iV2 = [vInt2 intValue]; iV2_ = 0; }
|
|
else { iV2 = 0; iV2_ = 0; }
|
|
|
|
if (vInt1 == nil) {
|
|
if (i != (v1Values.count - 1))
|
|
return -2;
|
|
|
|
if ([vStr1.lowercaseString hasSuffix:@"b"]) {
|
|
NSNumber *vInt1_ = [numFormatter numberFromString:[vStr1 substringToIndex:vStr1.length-1]];
|
|
if (vInt1_ == nil)
|
|
return -2;
|
|
|
|
iV1 = [vInt1_ intValue];
|
|
iV1_ = -100;
|
|
} else if ([vStr1.lowercaseString hasSuffix:@"a"]) {
|
|
NSNumber *vInt1_ = [numFormatter numberFromString:[vStr1 substringToIndex:vStr1.length-1]];
|
|
if (vInt1_ == nil)
|
|
return -2;
|
|
|
|
iV1 = [vInt1_ intValue];
|
|
iV1_ = -200;
|
|
} else
|
|
return -2;
|
|
}
|
|
|
|
if (vInt2 == nil) {
|
|
if (i != (v2Values.count - 1))
|
|
return -2;
|
|
|
|
if ([vStr2.lowercaseString hasSuffix:@"b"]) {
|
|
NSNumber *vInt2_ = [numFormatter numberFromString:[vStr2 substringToIndex:vStr2.length-1]];
|
|
if (vInt2_ == nil)
|
|
return -2;
|
|
|
|
iV2 = [vInt2_ intValue];
|
|
iV2_ = -100;
|
|
}
|
|
else if ([vStr2.lowercaseString hasSuffix:@"a"]) {
|
|
NSNumber *vInt2_ = [numFormatter numberFromString:[vStr2 substringToIndex:vStr2.length-1]];
|
|
if (vInt2_ == nil)
|
|
return -2;
|
|
|
|
iV2 = [vInt2_ intValue];
|
|
iV2_ = -200;
|
|
}
|
|
else
|
|
return -2;
|
|
}
|
|
|
|
if (iV1 == iV2) {
|
|
if (iV1_ == iV2_)
|
|
continue;
|
|
if (iV1_ < iV2_)
|
|
return -1;
|
|
if (iV1_ > iV2_)
|
|
return 1;
|
|
}
|
|
else if (iV1 < iV2) {
|
|
return -1;
|
|
}
|
|
else if (iV1 > iV2) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
- (NSString *)keyboardIdForCurrentRequest {
|
|
NSString *retVal = nil;
|
|
if (self.currentRequest != nil) {
|
|
NSString *tmpStr = [[self.currentRequest.url absoluteString] lastPathComponent];
|
|
NSRange dotRange = [tmpStr rangeOfString:@".js" options:NSBackwardsSearch];
|
|
if (dotRange.location != NSNotFound) {
|
|
retVal = [tmpStr substringToIndex:dotRange.location];
|
|
}
|
|
}
|
|
else if (self.downloadQueue != nil) {
|
|
NSString *kbId;
|
|
id kbInfo = [[self.downloadQueue userInfo] objectForKey:kKeymanKeyboardInfoKey];
|
|
if ([kbInfo isKindOfClass:[NSDictionary class]])
|
|
kbId = [(NSDictionary *)kbInfo objectForKey:kKeymanKeyboardIdKey];
|
|
else
|
|
kbId = [[(NSArray *)kbInfo objectAtIndex:0] objectForKey:kKeymanKeyboardIdKey];
|
|
|
|
retVal = kbId;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
+ (BOOL)retinaScreen {
|
|
return ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)] && ([UIScreen mainScreen].scale == 2.0));
|
|
}
|
|
|
|
+ (CGFloat)keyboardHeight {
|
|
BOOL isPortrait = YES;
|
|
if (kIsKeymanSystemKeyboard)
|
|
isPortrait = [KMInputViewController isPortrait];
|
|
else {
|
|
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
|
|
isPortrait = UIInterfaceOrientationIsPortrait(orientation);
|
|
}
|
|
|
|
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
|
|
if (isPortrait) {
|
|
return kIsKeymanSystemKeyboard?kKeymanPadPortraitSystemKeyboardHeight:kKeymanPadPortraitInAppKeyboardHeight;
|
|
}
|
|
else {
|
|
return kIsKeymanSystemKeyboard?kKeymanPadLandscapeSystemKeyboardHeight:kKeymanPadLandscapeInAppKeyboardHeight;
|
|
}
|
|
}
|
|
else {
|
|
if (isPortrait) {
|
|
return kIsKeymanSystemKeyboard?kKeymanPhonePortraitSystemKeyboardHeight:kKeymanPhonePortraitInAppKeyboardHeight;
|
|
}
|
|
else {
|
|
return kIsKeymanSystemKeyboard?kKeymanPhoneLandscapeSystemKeyboardHeight:kKeymanPhoneLandscapeInAppKeyboardHeight;
|
|
}
|
|
}
|
|
}
|
|
|
|
#pragma mark - Private Methods
|
|
|
|
+ (CGFloat)keyboardHeightWithOrientation:(UIInterfaceOrientation)orientation {
|
|
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
|
|
if (UIInterfaceOrientationIsPortrait(orientation)) {
|
|
return kIsKeymanSystemKeyboard?kKeymanPadPortraitSystemKeyboardHeight:kKeymanPadPortraitInAppKeyboardHeight;
|
|
}
|
|
else {
|
|
return kIsKeymanSystemKeyboard?kKeymanPadLandscapeSystemKeyboardHeight:kKeymanPadLandscapeInAppKeyboardHeight;
|
|
}
|
|
}
|
|
else {
|
|
if (UIInterfaceOrientationIsPortrait(orientation)) {
|
|
return kIsKeymanSystemKeyboard?kKeymanPhonePortraitSystemKeyboardHeight:kKeymanPhonePortraitInAppKeyboardHeight;
|
|
}
|
|
else {
|
|
return kIsKeymanSystemKeyboard?kKeymanPhoneLandscapeSystemKeyboardHeight:kKeymanPhoneLandscapeInAppKeyboardHeight;
|
|
}
|
|
}
|
|
}
|
|
|
|
+ (CGFloat)keyboardWidth {
|
|
CGSize screenSize = [[UIScreen mainScreen] bounds].size;
|
|
int iosVersion = [[[UIDevice currentDevice] systemVersion] intValue];
|
|
if (iosVersion >= 8)
|
|
return screenSize.width;
|
|
|
|
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
|
|
if (UIInterfaceOrientationIsPortrait(orientation))
|
|
return screenSize.width;
|
|
else
|
|
return screenSize.height;
|
|
}
|
|
|
|
- (void)resizeKeyboard {
|
|
static CGFloat xWidth = 0;
|
|
static CGFloat xHeight = 0;
|
|
CGFloat kbWidth = [[self class] keyboardWidth];
|
|
CGFloat kbHeight = [[self class] keyboardHeight];
|
|
|
|
if (didResizeToOrientation && kIsKeymanSystemKeyboard && xWidth == kbWidth && xHeight == kbHeight) {
|
|
didResizeToOrientation = NO;
|
|
return;
|
|
}
|
|
else {
|
|
xWidth = kbWidth;
|
|
xHeight = kbHeight;
|
|
}
|
|
|
|
[webView setFrame:CGRectMake(0.0f, 0.0f, kbWidth, kbHeight)];
|
|
|
|
// Workaround for WKWebView bug with landscape orientation
|
|
if (kIsKeymanSystemKeyboard && [[self class] isWKWebView:webView])
|
|
[self performSelector:@selector(resizeDelay) withObject:self afterDelay:1.0];
|
|
|
|
NSInteger oskHeight = kbHeight;
|
|
if (!kIsKeymanSystemKeyboard)
|
|
oskHeight = oskHeight - oskHeight%20;
|
|
else
|
|
oskHeight = oskHeight - oskHeight%10;
|
|
|
|
NSString *jsString = [NSString stringWithFormat:@"setOskWidth(%ld);", (long)kbWidth];
|
|
if ([[self class] isWKWebView:webView])
|
|
[(WKWebView *)webView evaluateJavaScript:jsString completionHandler:nil];
|
|
else
|
|
[(UIWebView *)webView stringByEvaluatingJavaScriptFromString:jsString];
|
|
|
|
jsString = [NSString stringWithFormat:@"setOskHeight(%ld);", (long)oskHeight];
|
|
if ([[self class] isWKWebView:webView])
|
|
[(WKWebView *)webView evaluateJavaScript:jsString completionHandler:nil];
|
|
else
|
|
[(UIWebView *)webView stringByEvaluatingJavaScriptFromString:jsString];
|
|
}
|
|
|
|
- (void)resizeDelay {
|
|
// + 1000 to work around iOS bug with resizing on landscape orientation. Technically we only
|
|
// need this for landscape but it doesn't hurt to do it with both. 1000 is a big number that
|
|
// should hopefully work on all devices.
|
|
CGFloat kbWidth = [[self class] keyboardWidth];
|
|
CGFloat kbHeight = [[self class] keyboardHeight];
|
|
[webView setFrame:CGRectMake(0.0f, 0.0f, kbWidth, kbHeight + 1000)];
|
|
}
|
|
|
|
- (void)resizeKeyboardIfNeeded {
|
|
static CGFloat xWidth = 0;
|
|
static CGFloat xHeight = 0;
|
|
CGFloat kbWidth = [[self class] keyboardWidth];
|
|
CGFloat kbHeight = [[self class] keyboardHeight];
|
|
if (kbWidth != xWidth || kbHeight != xHeight) {
|
|
[self resizeKeyboard];
|
|
xWidth = kbWidth;
|
|
xHeight = kbHeight;
|
|
}
|
|
}
|
|
|
|
- (void)resizeKeyboardWithOrientation:(UIInterfaceOrientation)orientation {
|
|
CGFloat kbWidth = [[self class] keyboardWidth];
|
|
CGFloat kbHeight = [[self class] keyboardHeightWithOrientation:orientation];
|
|
[webView setFrame:CGRectMake(0.0f, 0.0f, kbWidth, kbHeight)];
|
|
|
|
NSInteger oskHeight = kbHeight;
|
|
if (!kIsKeymanSystemKeyboard)
|
|
oskHeight = oskHeight - oskHeight%20;
|
|
else
|
|
oskHeight = oskHeight - oskHeight%10;
|
|
|
|
NSString *jsString = [NSString stringWithFormat:@"setOskWidth(%ld);", (long)kbWidth];
|
|
if ([[self class] isWKWebView:webView])
|
|
[(WKWebView *)webView evaluateJavaScript:jsString completionHandler:nil];
|
|
else
|
|
[(UIWebView *)webView stringByEvaluatingJavaScriptFromString:jsString];
|
|
|
|
jsString = [NSString stringWithFormat:@"setOskHeight(%ld);", (long)oskHeight];
|
|
if ([[self class] isWKWebView:webView])
|
|
[(WKWebView *)webView evaluateJavaScript:jsString completionHandler:nil];
|
|
else
|
|
[(UIWebView *)webView stringByEvaluatingJavaScriptFromString:jsString];
|
|
}
|
|
|
|
- (UIScrollView *)keymanScrollView {
|
|
if ([[self class] isWKWebView:webView])
|
|
return [(WKWebView *)webView scrollView];
|
|
else
|
|
return [(UIWebView *)webView scrollView];
|
|
}
|
|
|
|
- (BOOL)canAccessSharedContainer {
|
|
if ([self sharedKeymanDirectory] == nil)
|
|
return NO;
|
|
|
|
if (!kIsKeymanSystemKeyboard)
|
|
return YES;
|
|
|
|
NSFileManager *fileMan = [NSFileManager defaultManager];
|
|
NSString *sharedKeymanDir = [NSString stringWithString:[self sharedKeymanDirectory]];
|
|
NSString *path = [sharedKeymanDir stringByAppendingPathComponent:kKeymanFullFileName];
|
|
BOOL canAccess = [fileMan fileExistsAtPath:path];
|
|
|
|
return canAccess;
|
|
}
|
|
|
|
- (void)copyUserDefaultsToSharedContainer {
|
|
NSUserDefaults *sharedUserData = nil;
|
|
if ([[NSUserDefaults standardUserDefaults] respondsToSelector:@selector(initWithSuiteName:)])
|
|
sharedUserData = [[NSUserDefaults alloc] initWithSuiteName:applicationGroupIdentifier];
|
|
|
|
if (sharedUserData == nil)
|
|
return;
|
|
|
|
NSUserDefaults *defaultUserData = [NSUserDefaults standardUserDefaults];
|
|
if ([sharedUserData objectForKey:kKeymanUserKeyboardsListKey] == nil)
|
|
[sharedUserData setObject:[defaultUserData objectForKey:kKeymanUserKeyboardsListKey]
|
|
forKey:kKeymanUserKeyboardsListKey];
|
|
|
|
if ([sharedUserData objectForKey:kKeymanUserCurrentKeyboardKey] == nil)
|
|
[sharedUserData setObject:[defaultUserData objectForKey:kKeymanUserCurrentKeyboardKey]
|
|
forKey:kKeymanUserCurrentKeyboardKey];
|
|
|
|
if ([sharedUserData objectForKey:kKeymanEngineVersion] == nil)
|
|
[sharedUserData setObject:[defaultUserData objectForKey:kKeymanEngineVersion]
|
|
forKey:kKeymanEngineVersion];
|
|
|
|
if ([sharedUserData objectForKey:kKeymanKeyboardPickerDisplayedKey] == nil)
|
|
[sharedUserData setObject:[defaultUserData objectForKey:kKeymanKeyboardPickerDisplayedKey]
|
|
forKey:kKeymanKeyboardPickerDisplayedKey];
|
|
|
|
[sharedUserData synchronize];
|
|
}
|
|
|
|
- (void)copyUserDefaultsFromSharedContainer {
|
|
NSUserDefaults *sharedUserData = nil;
|
|
if ([[NSUserDefaults standardUserDefaults] respondsToSelector:@selector(initWithSuiteName:)])
|
|
sharedUserData = [[NSUserDefaults alloc] initWithSuiteName:applicationGroupIdentifier];
|
|
|
|
if (sharedUserData == nil)
|
|
return;
|
|
|
|
NSUserDefaults *defaultUserData = [NSUserDefaults standardUserDefaults];
|
|
if ([sharedUserData objectForKey:kKeymanUserKeyboardsListKey] != nil)
|
|
[defaultUserData setObject:[sharedUserData objectForKey:kKeymanUserKeyboardsListKey]
|
|
forKey:kKeymanUserKeyboardsListKey];
|
|
|
|
if ([sharedUserData objectForKey:kKeymanEngineVersion] != nil)
|
|
[defaultUserData setObject:[sharedUserData objectForKey:kKeymanEngineVersion]
|
|
forKey:kKeymanEngineVersion];
|
|
|
|
[defaultUserData synchronize];
|
|
}
|
|
|
|
- (BOOL)addSkipBackupAttributeToItemAtPath:(NSString *) filePathString
|
|
{
|
|
NSURL* URL= [NSURL fileURLWithPath: filePathString];
|
|
assert([[NSFileManager defaultManager] fileExistsAtPath: [URL path]]);
|
|
|
|
NSError *error = nil;
|
|
BOOL success = [URL setResourceValue: [NSNumber numberWithBool: YES]
|
|
forKey: NSURLIsExcludedFromBackupKey error: &error];
|
|
if(!success){
|
|
NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
- (BOOL)copyAndExcludeFromBackup:(NSString*)pathToCopy destinationPath:(NSString*)destinationPath {
|
|
NSFileManager *fileMan = [NSFileManager defaultManager];
|
|
BOOL isDirectory;
|
|
BOOL fileExists = [fileMan fileExistsAtPath:pathToCopy isDirectory:&isDirectory];
|
|
NSError *error;
|
|
if (fileExists && !isDirectory) {
|
|
// copy if destination does not exist or replace if source is newer
|
|
if (![fileMan fileExistsAtPath:destinationPath])
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:nil];
|
|
else if ([self compareFileModDates:pathToCopy destinationPath:destinationPath] > 0) {
|
|
[fileMan removeItemAtPath:destinationPath error:&error];
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:&error];
|
|
}
|
|
else
|
|
return false;
|
|
[self addSkipBackupAttributeToItemAtPath:destinationPath];
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
- (void)copyKeymanFilesToSharedContainer {
|
|
if ([self sharedKeymanDirectory] == nil)
|
|
return;
|
|
|
|
NSFileManager *fileMan = [NSFileManager defaultManager];
|
|
NSString *defaultKeymanDir = [NSString stringWithString:[self defaultKeymanDirectory]];
|
|
NSString *sharedKeymanDir = [NSString stringWithString:[self sharedKeymanDirectory]];
|
|
NSString *defaultLangDir = [NSString stringWithString:[self defaultLanguageDirectory]];
|
|
NSString *sharedLangDir = [NSString stringWithString:[self sharedLanguageDirectory]];
|
|
NSString *defaultFontDir = [NSString stringWithString:[self defaultFontDirectory]];
|
|
NSString *sharedFontDir = [NSString stringWithString:[self sharedFontDirectory]];
|
|
//NSError *error;
|
|
|
|
//BOOL isDirectory;
|
|
NSArray *dirContents = [fileMan contentsOfDirectoryAtPath:defaultKeymanDir error:nil];
|
|
for (NSString *file in dirContents){
|
|
NSString *pathToCopy = [defaultKeymanDir stringByAppendingPathComponent:file];
|
|
NSString *destinationPath = [sharedKeymanDir stringByAppendingPathComponent:file];
|
|
|
|
[self copyAndExcludeFromBackup:pathToCopy destinationPath:destinationPath];
|
|
/* BOOL fileExists = [fileMan fileExistsAtPath:pathToCopy isDirectory:&isDirectory];
|
|
if (fileExists && !isDirectory) {
|
|
// copy if destination does not exist or replace if source is newer
|
|
if (![fileMan fileExistsAtPath:destinationPath])
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:nil];
|
|
else if ([self compareFileModDates:pathToCopy destinationPath:destinationPath] > 0) {
|
|
[fileMan removeItemAtPath:destinationPath error:&error];
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:&error];
|
|
}
|
|
}*/
|
|
}
|
|
|
|
dirContents = [fileMan contentsOfDirectoryAtPath:defaultLangDir error:nil];
|
|
for (NSString *file in dirContents){
|
|
NSString *pathToCopy = [defaultLangDir stringByAppendingPathComponent:file];
|
|
NSString *destinationPath = [sharedLangDir stringByAppendingPathComponent:file];
|
|
|
|
[self copyAndExcludeFromBackup:pathToCopy destinationPath:destinationPath];
|
|
/* BOOL fileExists = [fileMan fileExistsAtPath:pathToCopy isDirectory:&isDirectory];
|
|
if (fileExists && !isDirectory) {
|
|
// copy if destination does not exist or replace if source is newer
|
|
if (![fileMan fileExistsAtPath:destinationPath])
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:nil];
|
|
else if ([self compareFileModDates:pathToCopy destinationPath:destinationPath] > 0) {
|
|
[fileMan removeItemAtPath:destinationPath error:&error];
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:&error];
|
|
}
|
|
}*/
|
|
}
|
|
|
|
dirContents = [fileMan contentsOfDirectoryAtPath:defaultFontDir error:nil];
|
|
for (NSString *file in dirContents){
|
|
NSString *pathToCopy = [defaultFontDir stringByAppendingPathComponent:file];
|
|
NSString *destinationPath = [sharedFontDir stringByAppendingPathComponent:file];
|
|
[self copyAndExcludeFromBackup:pathToCopy destinationPath:destinationPath];
|
|
/* BOOL fileExists = [fileMan fileExistsAtPath:pathToCopy isDirectory:&isDirectory];
|
|
if (fileExists && !isDirectory) {
|
|
// copy if destination does not exist or replace if source is newer
|
|
if (![fileMan fileExistsAtPath:destinationPath])
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:nil];
|
|
else if ([self compareFileModDates:pathToCopy destinationPath:destinationPath] > 0) {
|
|
[fileMan removeItemAtPath:destinationPath error:&error];
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:&error];
|
|
}
|
|
}*/
|
|
}
|
|
}
|
|
|
|
- (void)copyKeymanFilesFromSharedContainer {
|
|
if ([self sharedKeymanDirectory] == nil)
|
|
return;
|
|
|
|
NSFileManager *fileMan = [NSFileManager defaultManager];
|
|
NSString *defaultKeymanDir = [NSString stringWithString:[self defaultKeymanDirectory]];
|
|
NSString *sharedKeymanDir = [NSString stringWithString:[self sharedKeymanDirectory]];
|
|
NSString *defaultLangDir = [NSString stringWithString:[self defaultLanguageDirectory]];
|
|
NSString *sharedLangDir = [NSString stringWithString:[self sharedLanguageDirectory]];
|
|
NSString *defaultFontDir = [NSString stringWithString:[self defaultFontDirectory]];
|
|
NSString *sharedFontDir = [NSString stringWithString:[self sharedFontDirectory]];
|
|
|
|
NSArray *dirContents = [fileMan contentsOfDirectoryAtPath:sharedKeymanDir error:nil];
|
|
for (NSString *file in dirContents){
|
|
NSString *pathToCopy = [sharedKeymanDir stringByAppendingPathComponent:file];
|
|
NSString *destinationPath = [defaultKeymanDir stringByAppendingPathComponent:file];
|
|
[self copyAndExcludeFromBackup:pathToCopy destinationPath:destinationPath];
|
|
/*BOOL fileExists = [fileMan fileExistsAtPath:pathToCopy isDirectory:&isDirectory];
|
|
if (fileExists && !isDirectory) {
|
|
// copy if destination does not exist or replace if source is newer
|
|
if (![fileMan fileExistsAtPath:destinationPath])
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:nil];
|
|
else if ([self compareFileModDates:pathToCopy destinationPath:destinationPath] > 0) {
|
|
[fileMan removeItemAtPath:destinationPath error:&error];
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:&error];
|
|
}
|
|
}*/
|
|
}
|
|
|
|
dirContents = [fileMan contentsOfDirectoryAtPath:sharedLangDir error:nil];
|
|
for (NSString *file in dirContents){
|
|
NSString *pathToCopy = [sharedLangDir stringByAppendingPathComponent:file];
|
|
NSString *destinationPath = [defaultLangDir stringByAppendingPathComponent:file];
|
|
[self copyAndExcludeFromBackup:pathToCopy destinationPath:destinationPath];
|
|
/* BOOL fileExists = [fileMan fileExistsAtPath:pathToCopy isDirectory:&isDirectory];
|
|
if (fileExists && !isDirectory) {
|
|
// copy if destination does not exist or replace if source is newer
|
|
if (![fileMan fileExistsAtPath:destinationPath])
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:nil];
|
|
else if ([self compareFileModDates:pathToCopy destinationPath:destinationPath] > 0) {
|
|
[fileMan removeItemAtPath:destinationPath error:&error];
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:&error];
|
|
}
|
|
}*/
|
|
}
|
|
|
|
dirContents = [fileMan contentsOfDirectoryAtPath:sharedFontDir error:nil];
|
|
for (NSString *file in dirContents){
|
|
NSString *pathToCopy = [sharedFontDir stringByAppendingPathComponent:file];
|
|
NSString *destinationPath = [defaultFontDir stringByAppendingPathComponent:file];
|
|
[self copyAndExcludeFromBackup:pathToCopy destinationPath:destinationPath];
|
|
/*BOOL fileExists = [fileMan fileExistsAtPath:pathToCopy isDirectory:&isDirectory];
|
|
if (fileExists && !isDirectory) {
|
|
// copy if destination does not exist or replace if source is newer
|
|
if (![fileMan fileExistsAtPath:destinationPath])
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:nil];
|
|
else if ([self compareFileModDates:pathToCopy destinationPath:destinationPath] > 0) {
|
|
[fileMan removeItemAtPath:destinationPath error:&error];
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:&error];
|
|
}
|
|
}*/
|
|
}
|
|
|
|
[self registerCustomFonts];
|
|
}
|
|
|
|
- (void)copyWebFilesToLibrary {
|
|
|
|
NSString *libraryDirectory = [self activeKeymanDirectory];
|
|
if (![libraryDirectory length]) {
|
|
[self KMLog:@"Could not locate library directory! Could not copy Keyman files." checkDebugPrinting:NO];
|
|
return;
|
|
}
|
|
|
|
NSError *error = nil;
|
|
NSBundle *keymanBundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"Keyman" ofType:@"bundle"]];
|
|
NSString *pathToCopy = [keymanBundle pathForResource:kKeymanFileName ofType:kKeymanFileExtension];
|
|
NSString *destinationPath = nil;
|
|
if ([pathToCopy length]) {
|
|
destinationPath = [libraryDirectory stringByAppendingPathComponent:kKeymanFullFileName];
|
|
// copy if destination does not exist or replace if source is newer
|
|
|
|
[self copyAndExcludeFromBackup:pathToCopy destinationPath:destinationPath];
|
|
|
|
/*if (![fileMan fileExistsAtPath:destinationPath]) {
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:nil];
|
|
NSURL *URL = [NSURL fileURLWithPath:destinationPath];
|
|
[URL setResourceValue:@(YES) forKey:NSURLIsExcludedFromBackupKey error:nil];
|
|
}
|
|
else if ([self compareFileModDates:pathToCopy destinationPath:destinationPath] > 0) {
|
|
[fileMan removeItemAtPath:destinationPath error:nil];
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:&error];
|
|
NSURL *URL = [NSURL fileURLWithPath:destinationPath];
|
|
[URL setResourceValue:@(YES) forKey:NSURLIsExcludedFromBackupKey error:nil];
|
|
}*/
|
|
}
|
|
else {
|
|
error = [NSError errorWithDomain:@"Keyman" code:0 userInfo:[NSDictionary dictionaryWithObject:@"Could not locate Keyman html file for copying." forKey:NSLocalizedDescriptionKey]];
|
|
}
|
|
|
|
if (error == nil) {
|
|
pathToCopy = [keymanBundle pathForResource:kKeymaniOSCodeFileName ofType:nil];
|
|
if ([pathToCopy length]) {
|
|
destinationPath = [libraryDirectory stringByAppendingPathComponent:[pathToCopy lastPathComponent]];
|
|
|
|
[self copyAndExcludeFromBackup:pathToCopy destinationPath:destinationPath];
|
|
/*// copy if destination does not exist or replace if source is newer
|
|
if (![fileMan fileExistsAtPath:destinationPath]) {
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:nil];
|
|
//NSURL *URL = [NSURL fileURLWithPath:destinationPath];
|
|
//[URL setResourceValue:@(YES) forKey:NSURLIsExcludedFromBackupKey error:nil];
|
|
}
|
|
else if ([self compareFileModDates:pathToCopy destinationPath:destinationPath] > 0) {
|
|
[fileMan removeItemAtPath:destinationPath error:nil];
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:&error];
|
|
//NSURL *URL = [NSURL fileURLWithPath:destinationPath];
|
|
//[URL setResourceValue:@(YES) forKey:NSURLIsExcludedFromBackupKey error:nil];
|
|
}*/
|
|
}
|
|
else {
|
|
error = [NSError errorWithDomain:@"Keyman" code:0 userInfo:[NSDictionary dictionaryWithObject:@"Could not locate Keyman javascript for copying." forKey:NSLocalizedDescriptionKey]];
|
|
}
|
|
}
|
|
|
|
if (error == nil) {
|
|
pathToCopy = [keymanBundle pathForResource:[NSString stringWithFormat:@"%@-1.6.js", kKeymanDefaultKeyboardID] ofType:nil];
|
|
if ([pathToCopy length]) {
|
|
destinationPath = [[self activeLanguageDirectory] stringByAppendingPathComponent:[pathToCopy lastPathComponent]];
|
|
[self copyAndExcludeFromBackup:pathToCopy destinationPath:destinationPath];
|
|
/*// copy if destination does not exist or replace if source is newer
|
|
if (![fileMan fileExistsAtPath:destinationPath]) {
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:nil];
|
|
//NSURL *URL = [NSURL fileURLWithPath:destinationPath];
|
|
//[URL setResourceValue:@(YES) forKey:NSURLIsExcludedFromBackupKey error:nil];
|
|
}
|
|
else if ([self compareFileModDates:pathToCopy destinationPath:destinationPath] > 0) {
|
|
[fileMan removeItemAtPath:destinationPath error:nil];
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:&error];
|
|
//NSURL *URL = [NSURL fileURLWithPath:destinationPath];
|
|
//[URL setResourceValue:@(YES) forKey:NSURLIsExcludedFromBackupKey error:nil];
|
|
}*/
|
|
}
|
|
else {
|
|
error = [NSError errorWithDomain:@"Keyman" code:0 userInfo:[NSDictionary dictionaryWithObject:@"Could not locate default Keyman keyboard for copying." forKey:NSLocalizedDescriptionKey]];
|
|
}
|
|
}
|
|
|
|
if (error == nil) {
|
|
pathToCopy = [keymanBundle pathForResource:@"DejaVuSans" ofType:@"ttf"];
|
|
if ([pathToCopy length]) {
|
|
destinationPath = [[self activeFontDirectory] stringByAppendingPathComponent:[pathToCopy lastPathComponent]];
|
|
[self copyAndExcludeFromBackup:pathToCopy destinationPath:destinationPath];
|
|
/*// copy if destination does not exist or replace if source is newer
|
|
if (![fileMan fileExistsAtPath:destinationPath]) {
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:nil];
|
|
//NSURL *URL = [NSURL fileURLWithPath:destinationPath];
|
|
//[URL setResourceValue:@(YES) forKey:NSURLIsExcludedFromBackupKey error:nil];
|
|
}
|
|
else if ([self compareFileModDates:pathToCopy destinationPath:destinationPath] > 0) {
|
|
[fileMan removeItemAtPath:destinationPath error:nil];
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:&error];
|
|
//NSURL *URL = [NSURL fileURLWithPath:destinationPath];
|
|
//[URL setResourceValue:@(YES) forKey:NSURLIsExcludedFromBackupKey error:nil];
|
|
}*/
|
|
}
|
|
else {
|
|
error = [NSError errorWithDomain:@"Keyman" code:0 userInfo:[NSDictionary dictionaryWithObject:@"Could not locate DejaVuSans.ttf for copying." forKey:NSLocalizedDescriptionKey]];
|
|
}
|
|
}
|
|
|
|
if (error == nil) {
|
|
pathToCopy = [keymanBundle pathForResource:@"kmwosk" ofType:@"css"];
|
|
if ([pathToCopy length]) {
|
|
destinationPath = [libraryDirectory stringByAppendingPathComponent:[pathToCopy lastPathComponent]];
|
|
[self copyAndExcludeFromBackup:pathToCopy destinationPath:destinationPath];
|
|
/*// copy if destination does not exist or replace if source is newer
|
|
if (![fileMan fileExistsAtPath:destinationPath]) {
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:nil];
|
|
}
|
|
else if ([self compareFileModDates:pathToCopy destinationPath:destinationPath] > 0) {
|
|
[fileMan removeItemAtPath:destinationPath error:nil];
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:&error];
|
|
}*/
|
|
}
|
|
else {
|
|
error = [NSError errorWithDomain:@"Keyman" code:0 userInfo:[NSDictionary dictionaryWithObject:@"Could not locate kmwosk.css for copying." forKey:NSLocalizedDescriptionKey]];
|
|
}
|
|
}
|
|
|
|
if (error == nil) {
|
|
pathToCopy = [keymanBundle pathForResource:@"keymanweb-osk" ofType:@"ttf"];
|
|
if ([pathToCopy length]) {
|
|
destinationPath = [libraryDirectory stringByAppendingPathComponent:[pathToCopy lastPathComponent]];
|
|
[self copyAndExcludeFromBackup:pathToCopy destinationPath:destinationPath];
|
|
/*// copy if destination does not exist or replace if source is newer
|
|
if (![fileMan fileExistsAtPath:destinationPath])
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:nil];
|
|
else if ([self compareFileModDates:pathToCopy destinationPath:destinationPath] > 0) {
|
|
[fileMan removeItemAtPath:destinationPath error:nil];
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:&error];
|
|
}*/
|
|
}
|
|
else {
|
|
error = [NSError errorWithDomain:@"Keyman" code:0 userInfo:[NSDictionary dictionaryWithObject:@"Could not locate keymanweb-osk.ttf for copying." forKey:NSLocalizedDescriptionKey]];
|
|
}
|
|
}
|
|
|
|
if (error != nil)
|
|
[self KMLog:[NSString stringWithFormat:@"Error copying Keyman files: %@", error] checkDebugPrinting:NO];
|
|
}
|
|
|
|
- (NSInteger)compareFileModDates:(NSString *)srcPath destinationPath:(NSString *)desPath {
|
|
// returns;
|
|
// -2 on error
|
|
// 0 if srcModDate = desModDate
|
|
// -1 if srcModDate < desModDate
|
|
// 1 if srcModDate > desModDate
|
|
NSError *e;
|
|
NSDictionary *srcAttrs = [[NSFileManager defaultManager] attributesOfItemAtPath:srcPath error:&e];
|
|
if (e != nil) return -2;
|
|
NSDictionary *desAttrs = [[NSFileManager defaultManager] attributesOfItemAtPath:desPath error:&e];
|
|
if (e != nil) return -2;
|
|
|
|
NSDate *srcModDate = [srcAttrs fileModificationDate];
|
|
NSDate *desModDate = [desAttrs fileModificationDate];
|
|
NSComparisonResult result = [srcModDate compare:desModDate];
|
|
if (result == NSOrderedDescending)
|
|
return 1;
|
|
else if (result == NSOrderedAscending)
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
- (void)renameOldKeyboardFiles {
|
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
NSString *langDir = [self activeLanguageDirectory];
|
|
NSArray *dirContents = [fileManager contentsOfDirectoryAtPath:langDir error:nil];
|
|
for (NSString *filename in dirContents) {
|
|
if ([filename rangeOfString:@"-"].location == NSNotFound) {
|
|
if ([filename isEqualToString:@"us.js"]) {
|
|
NSString *filePath = [[self activeLanguageDirectory] stringByAppendingPathComponent:filename];
|
|
[fileManager removeItemAtPath:filePath error:nil];
|
|
|
|
NSUserDefaults *userData = [self activeUserDefaults];
|
|
NSDictionary *curKB = [userData objectForKey:kKeymanUserCurrentKeyboardKey];
|
|
if ([[curKB objectForKey:kKeymanKeyboardIdKey] isEqualToString:@"us"]) {
|
|
[userData removeObjectForKey:kKeymanUserCurrentKeyboardKey];
|
|
[userData synchronize];
|
|
}
|
|
}
|
|
else {
|
|
NSString *newFilename = [NSString stringWithFormat:@"%@-1.0.js", [filename substringToIndex:[filename length]-3]];
|
|
NSString *filePath = [langDir stringByAppendingPathComponent:filename];
|
|
NSString *newFilePath = [langDir stringByAppendingPathComponent:newFilename];
|
|
[fileManager moveItemAtPath:filePath toPath:newFilePath error:nil];
|
|
}
|
|
}
|
|
else {
|
|
NSString *oldFilename = [NSString stringWithFormat:@"%@.js", [filename substringToIndex:[filename rangeOfString:@"-"].location]];
|
|
NSString *oldFilePath = [langDir stringByAppendingPathComponent:oldFilename];
|
|
if ([fileManager fileExistsAtPath:oldFilePath])
|
|
[fileManager removeItemAtPath:oldFilePath error:nil];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (NSString *)defaultKeymanDirectory {
|
|
NSString *keymanDir = nil;
|
|
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
|
|
if ([paths count]) {
|
|
keymanDir = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"keyman"];
|
|
if ([keymanDir length]) {
|
|
[[NSFileManager defaultManager] createDirectoryAtPath:keymanDir withIntermediateDirectories:YES attributes:nil error:nil];
|
|
|
|
}
|
|
}
|
|
|
|
return keymanDir;
|
|
}
|
|
|
|
- (NSString *)defaultLanguageDirectory {
|
|
NSString *langDir = nil;
|
|
langDir = [[self defaultKeymanDirectory] stringByAppendingPathComponent:@"languages"];
|
|
if ([langDir length]) {
|
|
[[NSFileManager defaultManager] createDirectoryAtPath:langDir withIntermediateDirectories:YES attributes:nil error:nil];
|
|
}
|
|
|
|
return langDir;
|
|
}
|
|
|
|
- (NSString *)defaultFontDirectory {
|
|
NSString *fontDir = nil;
|
|
fontDir = [[self defaultKeymanDirectory] stringByAppendingPathComponent:@"fonts"];
|
|
if ([fontDir length]) {
|
|
[[NSFileManager defaultManager] createDirectoryAtPath:fontDir withIntermediateDirectories:YES attributes:nil error:nil];
|
|
}
|
|
|
|
return fontDir;
|
|
}
|
|
|
|
+ (void)setApplicationGroupIdentifier:(NSString *)groupID {
|
|
applicationGroupIdentifier = [NSString stringWithString:groupID];
|
|
}
|
|
|
|
- (NSString *)sharedContainerPath {
|
|
NSString *path = nil;
|
|
NSFileManager *fileMan = [NSFileManager defaultManager];
|
|
|
|
if ([fileMan respondsToSelector:@selector(containerURLForSecurityApplicationGroupIdentifier:)])
|
|
path = [[fileMan containerURLForSecurityApplicationGroupIdentifier:applicationGroupIdentifier] path];
|
|
|
|
return path;
|
|
}
|
|
|
|
- (NSString *)sharedKeymanDirectory {
|
|
NSString *sharedKeymanDirectory = nil;
|
|
NSString *sharedPath = [self sharedContainerPath];
|
|
if (sharedPath != nil) {
|
|
sharedKeymanDirectory = [sharedPath stringByAppendingPathComponent:@"keyman"];
|
|
if ([sharedKeymanDirectory length]) {
|
|
[[NSFileManager defaultManager] createDirectoryAtPath:sharedKeymanDirectory withIntermediateDirectories:YES attributes:nil error:nil];
|
|
}
|
|
}
|
|
|
|
return sharedKeymanDirectory;
|
|
}
|
|
|
|
- (NSString *)sharedLanguageDirectory {
|
|
NSString *sharedLanguageDirectory = nil;
|
|
sharedLanguageDirectory = [[self sharedKeymanDirectory] stringByAppendingPathComponent:@"languages"];
|
|
if ([sharedLanguageDirectory length]) {
|
|
[[NSFileManager defaultManager] createDirectoryAtPath:sharedLanguageDirectory withIntermediateDirectories:YES attributes:nil error:nil];
|
|
}
|
|
|
|
return sharedLanguageDirectory;
|
|
}
|
|
|
|
- (NSString *)sharedFontDirectory {
|
|
NSString *sharedFontDirectory = nil;
|
|
sharedFontDirectory = [[self sharedKeymanDirectory] stringByAppendingPathComponent:@"fonts"];
|
|
if ([sharedFontDirectory length]) {
|
|
[[NSFileManager defaultManager] createDirectoryAtPath:sharedFontDirectory withIntermediateDirectories:YES attributes:nil error:nil];
|
|
}
|
|
|
|
return sharedFontDirectory;
|
|
}
|
|
|
|
- (NSString *)activeKeymanDirectory {
|
|
NSString *activeKeymanDirectory = nil;
|
|
if ([self canAccessSharedContainer])
|
|
activeKeymanDirectory = [self sharedKeymanDirectory];
|
|
else
|
|
activeKeymanDirectory = [self defaultKeymanDirectory];
|
|
|
|
return activeKeymanDirectory;
|
|
}
|
|
|
|
- (NSString *)activeLanguageDirectory {
|
|
NSString *activeLanguageDirectory = nil;
|
|
if ([self canAccessSharedContainer])
|
|
activeLanguageDirectory = [self sharedLanguageDirectory];
|
|
else
|
|
activeLanguageDirectory = [self defaultLanguageDirectory];
|
|
|
|
return activeLanguageDirectory;
|
|
}
|
|
|
|
- (NSString *)activeFontDirectory {
|
|
NSString *activeFontDirectory = nil;
|
|
if ([self canAccessSharedContainer])
|
|
activeFontDirectory = [self sharedFontDirectory];
|
|
else
|
|
activeFontDirectory = [self defaultFontDirectory];
|
|
|
|
return activeFontDirectory;
|
|
}
|
|
|
|
- (NSUserDefaults *)activeUserDefaults {
|
|
NSUserDefaults *userDefaults = nil;
|
|
if ([self canAccessSharedContainer])
|
|
userDefaults = [[NSUserDefaults alloc] initWithSuiteName:applicationGroupIdentifier];
|
|
else
|
|
userDefaults = [NSUserDefaults standardUserDefaults];
|
|
|
|
return userDefaults;
|
|
}
|
|
|
|
- (NSString *)keyboardPathForID:(NSString *)keyboardID keyboardVersion:(NSString *)kbVersion {
|
|
NSString *retVal = nil;
|
|
|
|
if ([keyboardID length]) {
|
|
if (kbVersion == nil || ![kbVersion length]) {
|
|
kbVersion = [self latestKeyboardFileVersionWithID:keyboardID];
|
|
if (kbVersion == nil)
|
|
return retVal;
|
|
}
|
|
|
|
retVal = [[[self activeLanguageDirectory] stringByAppendingPathComponent:keyboardID] stringByAppendingString:[NSString stringWithFormat:@"-%@%@", kbVersion, @".js"]];
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
- (NSString *)keyboardPathForFilename:(NSString *)filename keyboardVersion:(NSString *)kbVersion {
|
|
NSString *retVal = nil;
|
|
if ([filename length]) {
|
|
if ([filename rangeOfString:@"-" options:NSBackwardsSearch].location == NSNotFound) {
|
|
NSString *fn = [NSString stringWithFormat:@"%@-%@.js", [filename substringToIndex:filename.length-3], (kbVersion != nil)?kbVersion:@"1.0"];
|
|
retVal = [[self activeLanguageDirectory] stringByAppendingPathComponent:fn];
|
|
}
|
|
else
|
|
retVal = [[self activeLanguageDirectory] stringByAppendingPathComponent:filename];
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
- (NSString *)fontPathForFilename:(NSString *)filename {
|
|
NSString *retVal = nil;
|
|
if ([filename length]) {
|
|
retVal = [[self activeFontDirectory] stringByAppendingPathComponent:filename];
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
- (void)createKeyboardsInfo {
|
|
NSString *langId = nil;
|
|
NSString *kbID = nil;
|
|
NSString *kbVersion = nil;
|
|
NSString *isRTL = nil;
|
|
NSString *dictKey = nil;
|
|
NSString *kbBaseUri = [self.options objectForKey:kKeymanKeyboardBaseURIKey];
|
|
NSString *kbUri = nil;
|
|
NSDictionary *langDict = nil;
|
|
NSDictionary *kbDict = nil;
|
|
NSArray *keyboards = nil;
|
|
|
|
keyboardsInfo_ = [[NSMutableDictionary alloc] initWithCapacity:50];
|
|
keyboardsDictionary_ = [[NSMutableDictionary alloc] initWithCapacity:50];
|
|
for (langDict in self.languages) {
|
|
keyboards = [langDict objectForKey:kKeymanLanguageKeyboardsKey];
|
|
for (kbDict in keyboards) {
|
|
langId = [langDict objectForKey:kKeymanIdKey];
|
|
kbID = [kbDict objectForKey:kKeymanIdKey];
|
|
kbVersion = [kbDict objectForKey:kKeymanKeyboardVersionKey];
|
|
if (kbVersion == nil)
|
|
kbVersion = @"1.0";
|
|
|
|
isRTL = [kbDict objectForKey:kKeymanKeyboardRTLKey];
|
|
isRTL = (isRTL == nil)?@"N":@"Y";
|
|
|
|
dictKey = [NSString stringWithFormat:@"%@_%@",langId, kbID];
|
|
|
|
if ([self.keyboardsInfo objectForKey:kbID] == nil) {
|
|
kbUri = [NSString stringWithFormat:@"%@%@", kbBaseUri, [kbDict objectForKey:kKeymanKeyboardFilenameKey]];
|
|
[self.keyboardsInfo setObject:[NSDictionary dictionaryWithObjectsAndKeys:
|
|
[kbDict objectForKey:kKeymanNameKey], kKeymanKeyboardNameKey,
|
|
kbVersion, kKeymanKeyboardVersionKey,
|
|
isRTL, kKeymanKeyboardRTLKey,
|
|
[kbDict objectForKey:kKeymanKeyboardModifiedKey], kKeymanKeyboardModifiedKey,
|
|
[kbDict objectForKey:kKeymanKeyboardFileSizeKey], kKeymanKeyboardFileSizeKey,
|
|
kbUri, kKeymanKeyboardURIKey,
|
|
[kbDict objectForKey:kKeymanFontKey], kKeymanFontKey,
|
|
nil]
|
|
forKey:kbID];
|
|
}
|
|
|
|
if ([self.keyboardsDictionary objectForKey:dictKey] == nil) {
|
|
NSDictionary *oskFont = [kbDict objectForKey:kKeymanOskFontKey];
|
|
[keyboardsDictionary_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
|
|
[kbDict objectForKey:kKeymanIdKey], kKeymanKeyboardIdKey,
|
|
[langDict objectForKey:kKeymanIdKey], kKeymanLanguageIdKey,
|
|
[kbDict objectForKey:kKeymanNameKey], kKeymanKeyboardNameKey,
|
|
[langDict objectForKey:kKeymanNameKey], kKeymanLanguageNameKey,
|
|
kbVersion, kKeymanKeyboardVersionKey,
|
|
isRTL, kKeymanKeyboardRTLKey,
|
|
[self jsFontFromFontDictionary:[kbDict objectForKey:kKeymanFontKey]], kKeymanFontKey,
|
|
oskFont?[self jsFontFromFontDictionary:oskFont]:nil, kKeymanOskFontKey,
|
|
nil]
|
|
forKey:dictKey];
|
|
}
|
|
}
|
|
}
|
|
|
|
[self updateUserKeyboardsList];
|
|
}
|
|
|
|
- (void)updateUserKeyboardsList {
|
|
if (keyboardsDictionary_ == nil || keyboardsDictionary_.count == 0)
|
|
return;
|
|
|
|
NSUserDefaults *userData = [self activeUserDefaults];
|
|
CGFloat lastVersion = [self floatValueForVersionNumber:[userData objectForKey:kKeymanEngineVersion]];
|
|
CGFloat currentVersion = [self floatValueForVersionNumber:[self sdkVersion]];
|
|
|
|
if (lastVersion == currentVersion)
|
|
return;
|
|
|
|
NSMutableArray *userKbList = [[userData arrayForKey:kKeymanUserKeyboardsListKey] mutableCopy];
|
|
NSUInteger len = userKbList.count;
|
|
for (int i = 1; i < len; i++) {
|
|
NSString *kbID = [[userKbList objectAtIndex:i] objectForKey:kKeymanKeyboardIdKey];
|
|
NSString *langID = [[userKbList objectAtIndex:i] objectForKey:kKeymanLanguageIdKey];
|
|
NSMutableDictionary *kb = [[keyboardsDictionary_ objectForKey:[NSString stringWithFormat:@"%@_%@", langID, kbID]] mutableCopy];
|
|
if (kb != nil) {
|
|
[kb setObject:[self latestKeyboardFileVersionWithID:kbID] forKey:kKeymanKeyboardVersionKey];
|
|
[kb setObject:@"N" forKey:kKeymanCustomKeyboardKey];
|
|
[userKbList replaceObjectAtIndex:i withObject:kb];
|
|
}
|
|
else {
|
|
kb = [[userKbList objectAtIndex:i] mutableCopy];
|
|
[kb setObject:@"Y" forKey:kKeymanCustomKeyboardKey];
|
|
[userKbList replaceObjectAtIndex:i withObject:kb];
|
|
}
|
|
}
|
|
|
|
[userData setObject:userKbList forKey:kKeymanUserKeyboardsListKey];
|
|
[userData setObject:[self sdkVersion] forKey:kKeymanEngineVersion];
|
|
[userData synchronize];
|
|
}
|
|
|
|
- (CGFloat)floatValueForVersionNumber:(NSString *)versionNumber {
|
|
if (versionNumber == nil)
|
|
return 1.0f;
|
|
|
|
NSArray *numbers = [versionNumber componentsSeparatedByString:@"."];
|
|
NSMutableString *floatStr = [NSMutableString stringWithString:@""];
|
|
NSUInteger length = numbers.count;
|
|
for (int i = 0; i < length; i++) {
|
|
if (i == 0) {
|
|
[floatStr appendString:[NSString stringWithFormat:@"%@.", [numbers objectAtIndex:0]]];
|
|
if (length == 1)
|
|
[floatStr appendString:@"0"];
|
|
}
|
|
else {
|
|
[floatStr appendString:[numbers objectAtIndex:i]];
|
|
}
|
|
}
|
|
|
|
return [floatStr floatValue];
|
|
}
|
|
|
|
/* No longer used
|
|
- (NSDate *)dateFromKeymanDateString:(NSString *)dateString {
|
|
NSDate *retVal = nil;
|
|
if ([dateString length]) {
|
|
// format date
|
|
retVal = [self.dateFormatter dateFromString:dateString];
|
|
}
|
|
return retVal;
|
|
}
|
|
*/
|
|
|
|
- (void)reachabilityChanged:(NSNotification *)notification {
|
|
if (self.debugPrintingOn) {
|
|
NSString *reachStr = @"Not Reachable";
|
|
NetworkStatus status = [self.reachability currentReachabilityStatus];
|
|
if (status == ReachableViaWiFi) reachStr = @"Reachable Via WiFi";
|
|
if (status == ReachableViaWWAN) reachStr = @"Reachable Via WWan";
|
|
[self KMLog:[NSString stringWithFormat:@"Reachability changed to '%@'", reachStr] checkDebugPrinting:YES];
|
|
}
|
|
}
|
|
|
|
#pragma mark - WKNavigationDelegate
|
|
|
|
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
|
|
NSString *fragment = message.body;
|
|
if ([fragment hasPrefix:@"ios-log:"]) {
|
|
NSString *requestString = [fragment stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
|
NSString *logString = [[requestString componentsSeparatedByString:@":#iOS#"] objectAtIndex:1];
|
|
[self KMLog:[NSString stringWithFormat:@"WebView: %@", logString] checkDebugPrinting:NO];
|
|
}
|
|
else {
|
|
[self.webDelegate updatedFragment:fragment];
|
|
[self.inputDelegate updatedFragment:fragment];
|
|
[self updatedFragment:fragment];
|
|
}
|
|
}
|
|
|
|
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
|
|
NSString *lastComponent = [[webView URL] lastPathComponent];
|
|
NSString *fragment = [[webView URL] fragment];
|
|
if ([lastComponent isEqualToString:kKeymanFullFileName] && [fragment length] == 0) {
|
|
[self KMLog:@"Loaded keyboard." checkDebugPrinting:YES];
|
|
|
|
[self resizeKeyboard];
|
|
|
|
NSString *deviceType = ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)?@"AppleMobile":@"AppleTablet";
|
|
[webView evaluateJavaScript:[NSString stringWithFormat:@"setDeviceType('%@');", deviceType] completionHandler:nil];
|
|
if ((self.keyboardID == nil || self.languageID == nil) && !shouldReloadKeyboard) {
|
|
NSString *kbID = kKeymanDefaultKeyboardID;
|
|
NSString *langID = kKeymanDefaultLanguageID;
|
|
NSString *kbName = kKeymanDefaultKeyboardName;
|
|
NSString *langName = kKeymanDefaultLanguageName;
|
|
NSString *font = kKeymanDefaultKeyboardFont;
|
|
NSString *oskFont = nil;
|
|
|
|
NSUserDefaults *userData = nil;
|
|
if (kIsKeymanSystemKeyboard)
|
|
userData = [NSUserDefaults standardUserDefaults];
|
|
else
|
|
userData = [self activeUserDefaults];
|
|
|
|
NSArray *userKeyboards = [[self activeUserDefaults] arrayForKey:kKeymanUserKeyboardsListKey];
|
|
|
|
if (userKeyboards != nil && [userKeyboards count] > 0) {
|
|
kbID = [[userKeyboards objectAtIndex:0] objectForKey:kKeymanKeyboardIdKey];
|
|
langID = [[userKeyboards objectAtIndex:0] objectForKey:kKeymanLanguageIdKey];
|
|
kbName = [[userKeyboards objectAtIndex:0] objectForKey:kKeymanKeyboardNameKey];
|
|
langName = [[userKeyboards objectAtIndex:0] objectForKey:kKeymanLanguageNameKey];
|
|
font = [[userKeyboards objectAtIndex:0] objectForKey:kKeymanFontKey];
|
|
oskFont = [[userKeyboards objectAtIndex:0] objectForKey:kKeymanOskFontKey];
|
|
}
|
|
|
|
NSDictionary *currentKB = [userData dictionaryForKey:kKeymanUserCurrentKeyboardKey];
|
|
//if (kIsKeymanSystemKeyboard && ![self canAccessSharedContainer])
|
|
//currentKB = nil;
|
|
|
|
if (currentKB != nil) {
|
|
NSString *cKbId = [currentKB objectForKey:kKeymanKeyboardIdKey];
|
|
NSString *cLangId = [currentKB objectForKey:kKeymanLanguageIdKey];
|
|
if ([self indexForUserKeyboardWithID:cKbId languageID:cLangId] >= 0) {
|
|
kbID = [currentKB objectForKey:kKeymanKeyboardIdKey];
|
|
langID = [currentKB objectForKey:kKeymanLanguageIdKey];
|
|
kbName = [[currentKB objectForKey:kKeymanKeyboardNameKey] stringByReplacingOccurrencesOfString:@"\\'" withString:@"'"];
|
|
langName = [[currentKB objectForKey:kKeymanLanguageNameKey] stringByReplacingOccurrencesOfString:@"\\'" withString:@"'"];
|
|
font = [currentKB objectForKey:kKeymanFontKey];
|
|
oskFont = [currentKB objectForKey:kKeymanOskFontKey];
|
|
}
|
|
}
|
|
|
|
[self setKeyboardWithID:kbID languageID:langID keyboardName:kbName languageName:langName font:font oskFont:oskFont];
|
|
}
|
|
|
|
NSDictionary *kbInfo = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
self.keyboardID, kKeymanKeyboardIdKey,
|
|
self.languageID, kKeymanLanguageIdKey,
|
|
nil];
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:kKeymanKeyboardLoadedNotification
|
|
object:self
|
|
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:kbInfo, kKeymanKeyboardInfoKey, nil]];
|
|
|
|
if (shouldReloadKeyboard) {
|
|
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resetKeyboard) object:nil];
|
|
[self performSelector:@selector(resetKeyboard) withObject:nil afterDelay:0.25];
|
|
shouldReloadKeyboard = NO;
|
|
}
|
|
}
|
|
}
|
|
|
|
#pragma mark - UIWebViewDelegate
|
|
|
|
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
|
|
NSString *fragment = [[request URL] fragment];
|
|
[self.webDelegate updatedFragment:fragment];
|
|
[self.inputDelegate updatedFragment:fragment];
|
|
[self updatedFragment:fragment];
|
|
|
|
NSString *requestString = [[[request URL] absoluteString] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
|
if ([requestString hasPrefix:@"ios-log:"]) {
|
|
NSString *logString = [[requestString componentsSeparatedByString:@":#iOS#"] objectAtIndex:1];
|
|
[self KMLog:[NSString stringWithFormat:@"WebView: %@", logString] checkDebugPrinting:NO];
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (void)webViewDidFinishLoad:(UIWebView *)webView {
|
|
NSURLRequest *request = [webView request];
|
|
NSString *lastComponent = [[request URL] lastPathComponent];
|
|
NSString *fragment = [[request URL] fragment];
|
|
if ([lastComponent isEqualToString:kKeymanFullFileName] && [fragment length] == 0) {
|
|
[self KMLog:@"Loaded keyboard." checkDebugPrinting:YES];
|
|
|
|
[self resizeKeyboard];
|
|
|
|
NSString *deviceType = ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)?@"AppleMobile":@"AppleTablet";
|
|
[webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"setDeviceType('%@');", deviceType]];
|
|
if ((self.keyboardID == nil || self.languageID == nil) && !shouldReloadKeyboard) {
|
|
NSString *kbID = kKeymanDefaultKeyboardID;
|
|
NSString *langID = kKeymanDefaultLanguageID;
|
|
NSString *kbName = kKeymanDefaultKeyboardName;
|
|
NSString *langName = kKeymanDefaultLanguageName;
|
|
NSString *font = kKeymanDefaultKeyboardFont;
|
|
NSString *oskFont = nil;
|
|
|
|
NSUserDefaults *userData = nil;
|
|
if (kIsKeymanSystemKeyboard)
|
|
userData = [NSUserDefaults standardUserDefaults];
|
|
else
|
|
userData = [self activeUserDefaults];
|
|
|
|
NSArray *userKeyboards = [[self activeUserDefaults] arrayForKey:kKeymanUserKeyboardsListKey];
|
|
|
|
if (userKeyboards != nil && [userKeyboards count] > 0) {
|
|
kbID = [[userKeyboards objectAtIndex:0] objectForKey:kKeymanKeyboardIdKey];
|
|
langID = [[userKeyboards objectAtIndex:0] objectForKey:kKeymanLanguageIdKey];
|
|
kbName = [[userKeyboards objectAtIndex:0] objectForKey:kKeymanKeyboardNameKey];
|
|
langName = [[userKeyboards objectAtIndex:0] objectForKey:kKeymanLanguageNameKey];
|
|
font = [[userKeyboards objectAtIndex:0] objectForKey:kKeymanFontKey];
|
|
oskFont = [[userKeyboards objectAtIndex:0] objectForKey:kKeymanOskFontKey];
|
|
}
|
|
|
|
NSDictionary *currentKB = [userData dictionaryForKey:kKeymanUserCurrentKeyboardKey];
|
|
//if (kIsKeymanSystemKeyboard && ![self canAccessSharedContainer])
|
|
//currentKB = nil;
|
|
|
|
if (currentKB != nil) {
|
|
NSString *cKbId = [currentKB objectForKey:kKeymanKeyboardIdKey];
|
|
NSString *cLangId = [currentKB objectForKey:kKeymanLanguageIdKey];
|
|
NSInteger cIndex = [self indexForUserKeyboardWithID:cKbId languageID:cLangId];
|
|
if (cIndex >= 0) {
|
|
kbID = [[userKeyboards objectAtIndex:cIndex] objectForKey:kKeymanKeyboardIdKey];
|
|
langID = [[userKeyboards objectAtIndex:cIndex] objectForKey:kKeymanLanguageIdKey];
|
|
kbName = [[userKeyboards objectAtIndex:cIndex] objectForKey:kKeymanKeyboardNameKey];
|
|
langName = [[userKeyboards objectAtIndex:cIndex] objectForKey:kKeymanLanguageNameKey];
|
|
font = [[userKeyboards objectAtIndex:cIndex] objectForKey:kKeymanFontKey];
|
|
oskFont = [[userKeyboards objectAtIndex:cIndex] objectForKey:kKeymanOskFontKey];
|
|
/*
|
|
kbID = [currentKB objectForKey:kKeymanKeyboardIdKey];
|
|
langID = [currentKB objectForKey:kKeymanLanguageIdKey];
|
|
kbName = [[currentKB objectForKey:kKeymanKeyboardNameKey] stringByReplacingOccurrencesOfString:@"\\'" withString:@"'"];
|
|
langName = [[currentKB objectForKey:kKeymanLanguageNameKey] stringByReplacingOccurrencesOfString:@"\\'" withString:@"'"];
|
|
font = [currentKB objectForKey:kKeymanFontKey];
|
|
oskFont = [currentKB objectForKey:kKeymanOskFontKey];
|
|
*/
|
|
}
|
|
}
|
|
|
|
[self setKeyboardWithID:kbID languageID:langID keyboardName:kbName languageName:langName font:font oskFont:oskFont];
|
|
}
|
|
|
|
NSDictionary *kbInfo = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
self.keyboardID, kKeymanKeyboardIdKey,
|
|
self.languageID, kKeymanLanguageIdKey,
|
|
nil];
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:kKeymanKeyboardLoadedNotification
|
|
object:self
|
|
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:kbInfo, kKeymanKeyboardInfoKey, nil]];
|
|
|
|
if (shouldReloadKeyboard) {
|
|
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resetKeyboard) object:nil];
|
|
[self performSelector:@selector(resetKeyboard) withObject:nil afterDelay:0.25];
|
|
shouldReloadKeyboard = NO;
|
|
}
|
|
}
|
|
}
|
|
|
|
- (CGRect)getKeyFrameWithX:(CGFloat)x Y:(CGFloat)y W:(CGFloat)w H:(CGFloat)h {
|
|
BOOL isPad = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad;
|
|
CGFloat adjY = isPad?-0.5:-1.0;
|
|
|
|
CGRect frame = CGRectMake(x-w/2.0f, y-adjY, w, h);
|
|
return frame;
|
|
}
|
|
|
|
- (void)updatedFragment:(NSString *)fragment {
|
|
if (fragment == nil || [fragment length] == 0)
|
|
return;
|
|
|
|
if ([fragment rangeOfString:@"showKeyPreview"].location != NSNotFound) {
|
|
BOOL isPad = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad;
|
|
if (isPad)
|
|
return;
|
|
|
|
if (kIsKeymanSystemKeyboard && !self.isSystemKeyboardTopBarEnabled)
|
|
return;
|
|
|
|
if (subKeysView != nil)
|
|
return;
|
|
|
|
[self dismissKeyPreview];
|
|
[self clearSubKeyArrays];
|
|
NSRange range1 = [fragment rangeOfString:@"+x="];
|
|
NSRange range2 = [fragment rangeOfString:@"+y="];
|
|
NSRange range3 = [fragment rangeOfString:@"+w="];
|
|
NSRange range4 = [fragment rangeOfString:@"+h="];
|
|
NSRange range5 = [fragment rangeOfString:@"+t="];
|
|
range1.location += range1.length;
|
|
range1.length = range2.location - range1.location;
|
|
CGFloat x = [[fragment substringWithRange:range1] floatValue];
|
|
range2.location += range2.length;
|
|
range2.length = range3.location - range2.location;
|
|
CGFloat y = [[fragment substringWithRange:range2] floatValue];
|
|
range3.location += range3.length;
|
|
range3.length = range4.location - range3.location;
|
|
CGFloat w = [[fragment substringWithRange:range3] floatValue];
|
|
range4.location += range4.length;
|
|
range4.length = range5.location - range4.location;
|
|
CGFloat h = [[fragment substringWithRange:range4] floatValue];
|
|
range5.location += range5.length;
|
|
NSString *t = [fragment substringFromIndex:range5.location];
|
|
keyFrame = [self getKeyFrameWithX:x Y:y W:w H:h];
|
|
keyPreviewView = [[KMKeyPreviewView alloc] initWithFrame:keyFrame];
|
|
NSMutableString *text = nil;
|
|
NSArray *unicodes = [t componentsSeparatedByString:@","];
|
|
NSScanner *scanner = nil;
|
|
for (int i = 0; i < [unicodes count]; i++) {
|
|
scanner = [NSScanner scannerWithString:[unicodes objectAtIndex:i]];
|
|
unsigned int unicode;
|
|
[scanner scanHexInt:&unicode];
|
|
if (text == nil)
|
|
text = [NSMutableString stringWithFormat:@"%C", (unsigned short) unicode];
|
|
else
|
|
[text appendString:[NSString stringWithFormat:@"%C", (unsigned short) unicode]];
|
|
}
|
|
|
|
[keyPreviewView setLabelText:[NSString stringWithString:text]];
|
|
text = nil;
|
|
NSString *oskFontName = [self oskFontNameForKeyboardWithID:self.keyboardID languageID:self.languageID];
|
|
if (oskFontName == nil)
|
|
oskFontName = [self fontNameForKeyboardWithID:self.keyboardID languageID:self.languageID];
|
|
[keyPreviewView setLabelFont:oskFontName];
|
|
[[[self class] inputView] addSubview:keyPreviewView];
|
|
//keyPreviewView = [[UIView alloc] initWithFrame:CGRectMake(x-w/2.0f, y+6, w, h)];
|
|
//[keyPreviewView setBackgroundColor:[UIColor colorWithRed:1.0 green:0 blue:0 alpha:0.5]];
|
|
//[[[self class] inputView] addSubview:keyPreviewView];
|
|
}
|
|
else if ([fragment rangeOfString:@"dismissKeyPreview"].location != NSNotFound) {
|
|
BOOL isPad = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad;
|
|
if (isPad || keyPreviewView == nil)
|
|
return;
|
|
|
|
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(dismissKeyPreview) object:nil];
|
|
[self performSelector:@selector(dismissKeyPreview) withObject:nil afterDelay:0.1];
|
|
[self clearSubKeyArrays];
|
|
}
|
|
else if ([fragment rangeOfString:@"insertText"].location != NSNotFound) {
|
|
[self dismissHelpBubble];
|
|
self.keymanHelpOn = NO;
|
|
}
|
|
else if ([fragment rangeOfString:@"menuKeyUp"].location != NSNotFound) {
|
|
[self dismissHelpBubble];
|
|
self.keymanHelpOn = NO;
|
|
|
|
if (kIsKeymanSystemKeyboard) {
|
|
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
|
|
[userData setBool:YES forKey:kKeymanKeyboardPickerDisplayedKey];
|
|
[userData synchronize];
|
|
}
|
|
}
|
|
else if ([fragment rangeOfString:@"hideKeyboard"].location != NSNotFound) {
|
|
[self dismissHelpBubble];
|
|
[self dismissSubKeys];
|
|
[self dismissKeyboardMenu];
|
|
}
|
|
else if ([fragment rangeOfString:@"showMore"].location != NSNotFound) {
|
|
[self dismissHelpBubble];
|
|
self.keymanHelpOn = NO;
|
|
[self dismissSubKeys];
|
|
[self dismissKeyboardMenu];
|
|
|
|
NSRange range1 = [fragment rangeOfString:@"+baseFrame="];
|
|
NSRange range2 = [fragment rangeOfString:@"+keys="];
|
|
NSRange range3 = [fragment rangeOfString:@"+font="];
|
|
range1.location += range1.length;
|
|
range1.length = range2.location - range1.location;
|
|
NSArray *frame = [[fragment substringWithRange:range1] componentsSeparatedByString:@","];
|
|
CGFloat x = [[frame objectAtIndex:0] floatValue];
|
|
CGFloat y = [[frame objectAtIndex:1] floatValue];
|
|
CGFloat w = [[frame objectAtIndex:2] floatValue];
|
|
CGFloat h = [[frame objectAtIndex:3] floatValue];
|
|
keyFrame = [self getKeyFrameWithX:x Y:y W:w H:h];
|
|
|
|
range2.location += range2.length;
|
|
range2.length = [fragment length] - range2.location;
|
|
if (range3.location != NSNotFound) {
|
|
range2.length -= [fragment length] - range3.location;
|
|
range3.location += range3.length;
|
|
specialOSKFont = [NSString stringWithString:[fragment substringFromIndex:range3.location]];
|
|
}
|
|
else {
|
|
specialOSKFont = nil;
|
|
}
|
|
|
|
NSString *keys = [fragment substringWithRange:range2];
|
|
NSArray *keyArray = [keys componentsSeparatedByString: @";"];
|
|
NSUInteger len = [keyArray count];
|
|
subKeyIDs = [keyArray mutableCopy];
|
|
subKeyTexts = [keyArray mutableCopy];
|
|
|
|
for(int i=0; i < len; i++) {
|
|
NSArray *values = [[keyArray objectAtIndex:i] componentsSeparatedByString: @":"];
|
|
if([values count] == 2) {
|
|
[subKeyIDs replaceObjectAtIndex:i withObject:[values objectAtIndex:0]];
|
|
[subKeyTexts replaceObjectAtIndex:i withObject:[values objectAtIndex:1]];
|
|
}
|
|
else if ([values count] == 1){
|
|
[subKeyIDs replaceObjectAtIndex:i withObject:[values objectAtIndex:0]];
|
|
NSString *subkeyText = [values objectAtIndex:0];
|
|
NSUInteger index = [subkeyText rangeOfString:@"-"].location;
|
|
if (index != NSNotFound)
|
|
subkeyText = [subkeyText substringFromIndex:index+1];
|
|
index = [subkeyText rangeOfString:@"_"].location;
|
|
if (index != NSNotFound)
|
|
subkeyText = [subkeyText substringFromIndex:index+1];
|
|
NSString *unicode = [NSString stringWithFormat:@"0x%@", subkeyText];
|
|
[subKeyTexts replaceObjectAtIndex:i withObject:unicode];
|
|
}
|
|
else {
|
|
[subKeyIDs replaceObjectAtIndex:i withObject:@""];
|
|
[subKeyTexts replaceObjectAtIndex:i withObject:@""];
|
|
}
|
|
}
|
|
|
|
//[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(clearSubKeyArrays) object:nil];
|
|
//[self performSelector:@selector(clearSubKeyArrays) withObject:nil afterDelay:0.525];
|
|
}
|
|
}
|
|
|
|
- (void)clearSubKeyArrays {
|
|
if (subKeysView == nil) {
|
|
[subKeys removeAllObjects];
|
|
[subKeyIDs removeAllObjects];
|
|
[subKeyTexts removeAllObjects];
|
|
}
|
|
}
|
|
|
|
- (void)dismissHelpBubble {
|
|
if (helpBubbleView != nil) {
|
|
[helpBubbleView removeFromSuperview];
|
|
helpBubbleView = nil;
|
|
}
|
|
}
|
|
|
|
- (void)dismissKeyPreview {
|
|
if (keyPreviewView != nil) {
|
|
[keyPreviewView removeFromSuperview];
|
|
keyPreviewView = nil;
|
|
}
|
|
}
|
|
|
|
- (CGSize)sizeFromFrame:(CGRect)frame {
|
|
return CGSizeMake(frame.size.width - frame.origin.x, frame.size.height - frame.origin.y);
|
|
}
|
|
|
|
#pragma mark - UIGestureRecognizer
|
|
|
|
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
|
|
return YES;
|
|
}
|
|
|
|
// UILongPressGestureRecognizer implementation to show sub keys in a subview
|
|
- (void)holdAction:(UILongPressGestureRecognizer *)sender {
|
|
if (sender.state == UIGestureRecognizerStateEnded) {
|
|
// Touch Ended
|
|
if (subKeysView != nil) {
|
|
[subKeysView removeFromSuperview];
|
|
[subKeysView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
|
|
subKeysView = nil;
|
|
|
|
//[self performSelector:@selector(setPopupVisible:) withObject:[NSNumber numberWithBool:NO] afterDelay:1.0];
|
|
[self setPopupVisible:[NSNumber numberWithBool:NO]];
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:kKeymanSubKeysMenuDismissedNotification object:self
|
|
userInfo:nil];
|
|
}
|
|
|
|
UIButton *button;
|
|
BOOL buttonClicked = NO;
|
|
for (int i = 0; i < [subKeys count]; i++) {
|
|
button = [subKeys objectAtIndex:i];
|
|
if (button.isHighlighted) {
|
|
button.highlighted = NO;
|
|
button.backgroundColor = subKeyColor;
|
|
[button setEnabled:NO];
|
|
[button sendActionsForControlEvents:UIControlEventTouchUpInside];
|
|
buttonClicked = YES;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!buttonClicked) {
|
|
[self clearSubKeyArrays];
|
|
}
|
|
}
|
|
else if (sender.state == UIGestureRecognizerStateBegan) {
|
|
// Touch & Hold Began
|
|
CGPoint touchPoint = [sender locationInView:sender.view];
|
|
if ([[self class] isWKWebView:webView]) {
|
|
[(WKWebView *)webView evaluateJavaScript:@"langMenuPos();" completionHandler:^(id result, NSError *error) {
|
|
NSString *keyFrame = (NSString *)result;
|
|
[self setMenuKeyFrame:keyFrame];
|
|
if (CGRectContainsPoint(menuKeyFrame, touchPoint)) {
|
|
[self.inputDelegate updatedFragment:@"showKeyboardMenu"];
|
|
return;
|
|
}
|
|
|
|
[self touchHoldBegan];
|
|
}];
|
|
}
|
|
else {
|
|
NSString *keyFrame = [(UIWebView *)webView stringByEvaluatingJavaScriptFromString:@"langMenuPos();"];
|
|
[self setMenuKeyFrame:keyFrame];
|
|
if (CGRectContainsPoint(menuKeyFrame, touchPoint)) {
|
|
[self.inputDelegate updatedFragment:@"showKeyboardMenu"];
|
|
return;
|
|
}
|
|
|
|
[self touchHoldBegan];
|
|
}
|
|
}
|
|
else {
|
|
// Hold & Move
|
|
CGPoint touchPoint = [sender locationInView:[subKeysView containerView]];
|
|
UIButton *button;
|
|
for (int i = 0; i < [subKeys count]; i++) {
|
|
button = [subKeys objectAtIndex:i];
|
|
button.highlighted = NO;
|
|
button.backgroundColor = subKeyColor;
|
|
[button setEnabled:NO];
|
|
if (CGRectContainsPoint(button.frame, touchPoint)) {
|
|
[button setEnabled:YES];
|
|
button.highlighted = YES;
|
|
button.backgroundColor = subKeyColorHighlighted;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)touchHoldBegan {
|
|
NSInteger subKeysCount = [subKeyIDs count];
|
|
CGFloat fontSize;
|
|
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
|
|
fontSize = [UIFont buttonFontSize]*2;
|
|
else
|
|
fontSize = [UIFont buttonFontSize];
|
|
|
|
NSString *oskFontName = [self oskFontNameForKeyboardWithID:self.keyboardID languageID:self.languageID];
|
|
if (oskFontName == nil)
|
|
oskFontName = [self fontNameForKeyboardWithID:self.keyboardID languageID:self.languageID];
|
|
subKeys = [[NSMutableArray alloc] init];
|
|
if (subKeyIDs != nil && subKeysCount) {
|
|
for (int i = 0; i < subKeysCount; i++) {
|
|
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
[button setTag:i];
|
|
[button setBackgroundColor:subKeyColor];
|
|
[button setRoundedBorderWithRadius:4.0
|
|
borderWidth:1.0
|
|
color:[UIColor grayColor]];
|
|
[button setTitleColor:[UIColor blackColor] forState:UIControlStateDisabled];
|
|
[button setTitleColor:[UIColor blackColor] forState:UIControlStateHighlighted];
|
|
|
|
if (oskFontName != nil)
|
|
[[button titleLabel] setFont:[UIFont fontWithName:oskFontName size:fontSize]];
|
|
else
|
|
[[button titleLabel] setFont:[UIFont systemFontOfSize:fontSize]];
|
|
|
|
if(specialOSKFont != nil) {
|
|
[[button titleLabel] setFont:[UIFont fontWithName:@"KeymanwebOsk" size:fontSize]];
|
|
[button setTitleColor:[UIColor grayColor] forState:UIControlStateDisabled];
|
|
}
|
|
|
|
NSMutableString *buttonTitle = nil;
|
|
NSString *subKeyText = [subKeyTexts objectAtIndex:i];
|
|
if([subKeyText rangeOfString:@"0x"].location != NSNotFound){
|
|
NSArray *unicodes = [subKeyText componentsSeparatedByString: @","];
|
|
NSScanner *s = nil;
|
|
for(int i=0;i<[unicodes count];i++) {
|
|
s = [NSScanner scannerWithString:[unicodes objectAtIndex:i]];
|
|
unsigned int unicode;
|
|
[s scanHexInt:&unicode];
|
|
if(buttonTitle == nil)
|
|
buttonTitle = [NSMutableString stringWithFormat:@"%C", (unsigned short) unicode];
|
|
else
|
|
[buttonTitle appendString:[NSString stringWithFormat:@"%C", (unsigned short) unicode]];
|
|
}
|
|
}
|
|
else {
|
|
buttonTitle = [NSMutableString stringWithString:subKeyText];
|
|
}
|
|
|
|
[button addTarget:self action:@selector(buttonClick:) forControlEvents:UIControlEventTouchUpInside];
|
|
[button setTitle:[NSString stringWithString:buttonTitle] forState:UIControlStateNormal];
|
|
buttonTitle = nil;
|
|
[button setTintColor:[UIColor colorWithRed:181.0/255.0 green:181.0/255.0 blue:181.0/255.0 alpha:1.0]];
|
|
[button setEnabled:NO];
|
|
|
|
[subKeys addObject:button];
|
|
}
|
|
|
|
[self dismissKeyPreview];
|
|
subKeysView = [[KMSubKeysView alloc] initWithKeyFrame:keyFrame subKeys:subKeys];
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:kKeymanSubKeysMenuWillShowNotification object:self
|
|
userInfo:nil];
|
|
[[[self class] inputView] addSubview:subKeysView];
|
|
[self setPopupVisible:[NSNumber numberWithBool:YES]];
|
|
}
|
|
}
|
|
|
|
- (void)buttonClick:(id)sender {
|
|
NSInteger keyIndex = [sender tag];
|
|
if (keyIndex < [subKeyIDs count] && keyIndex < [subKeyTexts count]) {
|
|
NSString *subKeyID = [subKeyIDs objectAtIndex:keyIndex];
|
|
NSString *subKeyText = [subKeyTexts objectAtIndex:keyIndex];
|
|
NSString *jsString = [NSString stringWithFormat:@"executePopupKey('%@','%@');", subKeyID, subKeyText];
|
|
if ([[self class] isWKWebView:webView])
|
|
[(WKWebView *)webView evaluateJavaScript:jsString completionHandler:nil];
|
|
else
|
|
[(UIWebView *)webView stringByEvaluatingJavaScriptFromString:jsString];
|
|
}
|
|
|
|
[subKeys removeAllObjects];
|
|
[subKeyIDs removeAllObjects];
|
|
[subKeyTexts removeAllObjects];
|
|
}
|
|
|
|
- (BOOL)isSubKeysMenuVisible {
|
|
return subKeysView == nil?NO:YES;
|
|
}
|
|
|
|
- (void)dismissSubKeys {
|
|
if (subKeysView != nil) {
|
|
[subKeysView removeFromSuperview];
|
|
[subKeysView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
|
|
subKeysView = nil;
|
|
|
|
//[self performSelector:@selector(setPopupVisible:) withObject:[NSNumber numberWithBool:NO] afterDelay:1.0];
|
|
[self setPopupVisible:[NSNumber numberWithBool:NO]];
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:kKeymanSubKeysMenuDismissedNotification object:self
|
|
userInfo:nil];
|
|
}
|
|
|
|
[subKeys removeAllObjects];
|
|
[subKeyIDs removeAllObjects];
|
|
[subKeyTexts removeAllObjects];
|
|
}
|
|
|
|
- (void)setPopupVisible:(NSNumber *)value {
|
|
BOOL visible = [value boolValue];
|
|
NSString *jsString = [NSString stringWithFormat:@"popupVisible(%@);", visible?@"1":@"0"];
|
|
if ([[self class] isWKWebView:webView])
|
|
[(WKWebView *)webView evaluateJavaScript:jsString completionHandler:nil];
|
|
else
|
|
[(UIWebView *)webView stringByEvaluatingJavaScriptFromString:jsString];
|
|
}
|
|
|
|
- (void)registerCustomFonts {
|
|
NSArray *directoryContents = [[NSArray alloc] initWithArray:[[NSFileManager defaultManager] contentsOfDirectoryAtPath:[self activeFontDirectory] error:nil]];
|
|
NSMutableDictionary *fontInfo = nil;
|
|
NSString *fontFilename = nil;
|
|
|
|
if (keymanFonts_ == nil)
|
|
keymanFonts_ = [[NSMutableDictionary alloc] init];
|
|
|
|
for (fontFilename in directoryContents) {
|
|
if ([fontFilename rangeOfString:@".ttf" options:NSBackwardsSearch].location != NSNotFound || [fontFilename rangeOfString:@".otf" options:NSBackwardsSearch].location != NSNotFound) {
|
|
fontInfo = [self.keymanFonts objectForKey:fontFilename];
|
|
if (fontInfo == nil) {
|
|
fontInfo = [[self registerFontWithFilename:fontFilename] mutableCopy];
|
|
if (fontInfo != nil)
|
|
[keymanFonts_ setObject:fontInfo forKey:fontFilename];
|
|
}
|
|
else if (![[fontInfo objectForKey:kKeymanFontRegisteredKey] boolValue]) {
|
|
fontInfo = [[self registerFontWithFilename:fontFilename] mutableCopy];
|
|
if (fontInfo != nil)
|
|
[keymanFonts_ setObject:fontInfo forKey:fontFilename];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)unregisterCustomFonts {
|
|
NSArray *directoryContents = [[NSArray alloc] initWithArray:[[NSFileManager defaultManager] contentsOfDirectoryAtPath:[self activeFontDirectory] error:nil]];
|
|
NSString *fontFilename = nil;
|
|
NSMutableDictionary *fontInfo = nil;
|
|
|
|
if (keymanFonts_ == nil)
|
|
return;
|
|
|
|
for (fontFilename in directoryContents) {
|
|
if ([fontFilename rangeOfString:@".ttf" options:NSBackwardsSearch].location != NSNotFound || [fontFilename rangeOfString:@".otf" options:NSBackwardsSearch].location != NSNotFound) {
|
|
fontInfo = [self.keymanFonts objectForKey:fontFilename];
|
|
if (fontInfo != nil && [[fontInfo objectForKey:kKeymanFontRegisteredKey] boolValue]) {
|
|
if ([self unregisterFontWithFilename:fontFilename]) {
|
|
[fontInfo setObject:[NSNumber numberWithBool:NO] forKey:kKeymanFontRegisteredKey];
|
|
[keymanFonts_ setObject:fontInfo forKey:fontFilename];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (NSDictionary *)registerFontWithFilename:(NSString *)fontFilename {
|
|
NSString *fontPath = [[self activeFontDirectory] stringByAppendingPathComponent:fontFilename];
|
|
if (![[NSFileManager defaultManager] fileExistsAtPath:fontPath])
|
|
return nil;
|
|
|
|
BOOL didRegister = NO;
|
|
NSString *fontName = nil;
|
|
NSURL *fontUrl = [NSURL fileURLWithPath:fontPath];
|
|
CGDataProviderRef provider = CGDataProviderCreateWithURL((__bridge CFURLRef)fontUrl);
|
|
CGFontRef font = CGFontCreateWithDataProvider(provider);
|
|
fontName = (NSString *)CFBridgingRelease(CGFontCopyPostScriptName(font));
|
|
CGDataProviderRelease(provider);
|
|
CGFontRelease(font);
|
|
|
|
if (![self fontExists:fontName]) {
|
|
CFErrorRef error;
|
|
didRegister = CTFontManagerRegisterFontsForURL((__bridge CFURLRef)fontUrl, kCTFontManagerScopeNone, &error);
|
|
if (!didRegister) {
|
|
CFStringRef errorDescription = CFErrorCopyDescription(error);
|
|
[self KMLog:[NSString stringWithFormat:@"Failed to register font: %@ reason: %@", [fontPath lastPathComponent], errorDescription] checkDebugPrinting:NO];
|
|
CFRelease(errorDescription);
|
|
error = nil;
|
|
}
|
|
else
|
|
[self KMLog:[NSString stringWithFormat:@"Registered font: %@", fontFilename] checkDebugPrinting:YES];
|
|
}
|
|
|
|
return [NSDictionary dictionaryWithObjectsAndKeys:fontName, kKeymanFontNameKey, [NSNumber numberWithBool:didRegister], kKeymanFontRegisteredKey, nil];
|
|
}
|
|
|
|
- (BOOL)unregisterFontWithFilename:(NSString *)fontFilename {
|
|
NSString *fontPath = [[self activeFontDirectory] stringByAppendingPathComponent:fontFilename];
|
|
if (![[NSFileManager defaultManager] fileExistsAtPath:fontPath])
|
|
return NO;
|
|
|
|
CFErrorRef error;
|
|
BOOL didUnregister = CTFontManagerUnregisterFontsForURL((__bridge CFURLRef)[NSURL fileURLWithPath:fontPath], kCTFontManagerScopeNone, &error);
|
|
if (!didUnregister) {
|
|
CFStringRef errorDescription = CFErrorCopyDescription(error);
|
|
[self KMLog:[NSString stringWithFormat:@"Failed to unregister font: %@ reason: %@", [fontPath lastPathComponent], errorDescription] checkDebugPrinting:NO];
|
|
CFRelease(errorDescription);
|
|
error = nil;
|
|
}
|
|
else
|
|
[self KMLog:[NSString stringWithFormat:@"Unregistered font: %@", fontFilename] checkDebugPrinting:YES];
|
|
|
|
return didUnregister;
|
|
}
|
|
|
|
- (BOOL)fontExists:(NSString *)fontName {
|
|
BOOL exists = NO;
|
|
NSArray *fontFamilyNames = [UIFont familyNames];
|
|
for (NSString *familyName in fontFamilyNames)
|
|
{
|
|
NSArray *fontNames = [UIFont fontNamesForFamilyName:familyName];
|
|
for (NSString *name in fontNames)
|
|
{
|
|
if ([fontName isEqualToString:name]) {
|
|
exists = YES;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (exists)
|
|
break;
|
|
}
|
|
|
|
return exists;
|
|
}
|
|
|
|
- (NSString *)fontNameForKeyboardWithID:(NSString *)keyboardID languageID:(NSString *)languageID {
|
|
NSUserDefaults *userData = [self activeUserDefaults];
|
|
NSArray *userKeyboards = [userData arrayForKey:kKeymanUserKeyboardsListKey];
|
|
NSString *fontName = nil;
|
|
|
|
if (userKeyboards != nil) {
|
|
NSString *kbId = nil;
|
|
NSString *langId = nil;
|
|
|
|
for (NSDictionary *kb in userKeyboards) {
|
|
kbId = [kb objectForKey:kKeymanKeyboardIdKey];
|
|
langId = [kb objectForKey:kKeymanLanguageIdKey];
|
|
if ([kbId isEqualToString:keyboardID] && [langId isEqualToString:languageID]) {
|
|
NSString *font = [kb objectForKey:kKeymanFontKey];
|
|
NSString *fontFilename = [self fontFilenameFromJSONFont:font];
|
|
fontName = [[self.keymanFonts objectForKey:fontFilename] objectForKey:kKeymanFontNameKey];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fontName == nil && self.keyboardsDictionary != nil) {
|
|
NSString *kbKey = [NSString stringWithFormat:@"%@_%@", languageID, keyboardID];
|
|
NSString *fontFilename = [self fontFilenameFromJSONFont:[[self.keyboardsDictionary objectForKey:kbKey] objectForKey:kKeymanFontKey]];
|
|
fontName = [[self.keymanFonts objectForKey:fontFilename] objectForKey:kKeymanFontNameKey];
|
|
}
|
|
|
|
return fontName;
|
|
}
|
|
|
|
- (NSString *)oskFontNameForKeyboardWithID:(NSString *)keyboardID languageID:(NSString *)languageID {
|
|
NSUserDefaults *userData = [self activeUserDefaults];
|
|
NSArray *userKeyboards = [userData arrayForKey:kKeymanUserKeyboardsListKey];
|
|
NSString *oskFontName = nil;
|
|
|
|
if (userKeyboards != nil) {
|
|
NSString *kbId = nil;
|
|
NSString *langId = nil;
|
|
|
|
for (NSDictionary *kb in userKeyboards) {
|
|
kbId = [kb objectForKey:kKeymanKeyboardIdKey];
|
|
langId = [kb objectForKey:kKeymanLanguageIdKey];
|
|
if ([kbId isEqualToString:keyboardID] && [langId isEqualToString:languageID]) {
|
|
NSString *oskFont = [kb objectForKey:kKeymanOskFontKey];
|
|
NSString *oskFontFilename = [self fontFilenameFromJSONFont:oskFont];
|
|
oskFontName = [[self.keymanFonts objectForKey:oskFontFilename] objectForKey:kKeymanFontNameKey];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (oskFontName == nil && self.keyboardsDictionary != nil) {
|
|
NSString *kbKey = [NSString stringWithFormat:@"%@_%@", languageID, keyboardID];
|
|
NSString *oskFontFilename = [self fontFilenameFromJSONFont:[[self.keyboardsDictionary objectForKey:kbKey] objectForKey:kKeymanOskFontKey]];
|
|
oskFontName = [[self.keymanFonts objectForKey:oskFontFilename] objectForKey:kKeymanFontNameKey];
|
|
}
|
|
|
|
return oskFontName;
|
|
}
|
|
|
|
- (BOOL)isRTLKeyboardWithID:(NSString *)keyboardID languageID:(NSString *)languageID {
|
|
NSUserDefaults *userData = [self activeUserDefaults];
|
|
NSArray *userKeyboards = [userData arrayForKey:kKeymanUserKeyboardsListKey];
|
|
NSString *isRTL = nil;
|
|
|
|
if (userKeyboards != nil) {
|
|
NSString *kbId = nil;
|
|
NSString *langId = nil;
|
|
|
|
for (NSDictionary *kb in userKeyboards) {
|
|
kbId = [kb objectForKey:kKeymanKeyboardIdKey];
|
|
langId = [kb objectForKey:kKeymanLanguageIdKey];
|
|
if([kbId isEqualToString:keyboardID] && [langId isEqualToString:languageID]) {
|
|
isRTL = [kb objectForKey:kKeymanKeyboardRTLKey];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isRTL == nil && self.keyboardsDictionary != nil) {
|
|
NSString *kbKey = [NSString stringWithFormat:@"%@_%@", languageID, keyboardID];
|
|
isRTL = [[self.keyboardsDictionary objectForKey:kbKey] objectForKey:kKeymanKeyboardRTLKey];
|
|
}
|
|
|
|
if (isRTL != nil && [isRTL isEqualToString:@"Y"])
|
|
return YES;
|
|
else
|
|
return NO;
|
|
}
|
|
|
|
- (NSString *)fontFilenameFromJSONFont:(NSString *)jsonFont {
|
|
if (jsonFont == nil)
|
|
return nil;
|
|
|
|
NSString *fontFilename = nil;
|
|
NSError *e;
|
|
|
|
if ([jsonFont rangeOfString:@".ttf"].location != NSNotFound || [jsonFont rangeOfString:@".otf"].location != NSNotFound) {
|
|
if ([[jsonFont substringFromIndex:jsonFont.length-4] isEqualToString:@".ttf"] || [[jsonFont substringFromIndex:jsonFont.length-4] isEqualToString:@".otf"])
|
|
return jsonFont;
|
|
}
|
|
|
|
NSDictionary *fontDict = [NSJSONSerialization JSONObjectWithData:[jsonFont dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:&e];
|
|
if (fontDict != nil) {
|
|
id fontFiles = [fontDict objectForKey:kKeymanFontFilesKey];
|
|
if ([fontFiles isKindOfClass:[NSArray class]]) {
|
|
for (NSString *file in fontFiles) {
|
|
if ([file rangeOfString:@".ttf" options:NSBackwardsSearch].location != NSNotFound || [file rangeOfString:@".otf" options:NSBackwardsSearch].location != NSNotFound) {
|
|
fontFilename = [NSString stringWithString:file];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if ([fontFiles isKindOfClass:[NSString class]]) {
|
|
NSString *file = (NSString *)fontFiles;
|
|
if ([file rangeOfString:@".ttf" options:NSBackwardsSearch].location != NSNotFound || [file rangeOfString:@".otf" options:NSBackwardsSearch].location != NSNotFound)
|
|
fontFilename = [NSString stringWithString:file];
|
|
}
|
|
}
|
|
|
|
return fontFilename;
|
|
}
|
|
|
|
- (NSString *)jsFontForKeyboardID:(NSString *)kbID languageID:(NSString *)langID {
|
|
NSString *kbKey = [NSString stringWithFormat:@"%@_%@", langID, kbID];
|
|
NSDictionary *kbDict = [self.keyboardsDictionary objectForKey:kbKey];
|
|
NSString *jsFont = [kbDict objectForKey:kKeymanFontKey];
|
|
return jsFont?jsFont:@"''";
|
|
}
|
|
|
|
- (NSString *)jsOskFontForKeyboardID:(NSString *)kbID languageID:(NSString *)langID {
|
|
NSString *kbKey = [NSString stringWithFormat:@"%@_%@", langID, kbID];
|
|
NSDictionary *kbDict = [self.keyboardsDictionary objectForKey:kbKey];
|
|
NSString *jsOskFont = [kbDict objectForKey:kKeymanOskFontKey];
|
|
return jsOskFont?jsOskFont:@"''";
|
|
}
|
|
|
|
- (NSString *)jsFontFromFontDictionary:(NSDictionary *)fontDict {
|
|
NSMutableString *str = nil;
|
|
if (fontDict != nil && fontDict.count) {
|
|
NSError *e;
|
|
NSData *data = [NSJSONSerialization dataWithJSONObject:fontDict options:0 error:&e];
|
|
str = [[NSMutableString alloc] initWithData:data encoding:NSASCIIStringEncoding];
|
|
[str replaceOccurrencesOfString:kKeymanFontFilenameKey withString:kKeymanFontFilesKey options:0 range:NSMakeRange(0, str.length)]; // Font filename is deprecated
|
|
[str replaceOccurrencesOfString:kKeymanFontSourceKey withString:kKeymanFontFilesKey options:0 range:NSMakeRange(0, str.length)];
|
|
}
|
|
else
|
|
return @"''";
|
|
|
|
return [NSString stringWithString:str];
|
|
}
|
|
|
|
+ (NSString *)md5FromString:(NSString *)str {
|
|
const char *cStr = [str UTF8String];
|
|
unsigned char digest[16];
|
|
CC_MD5(cStr, (CC_LONG)strlen(cStr), digest);
|
|
|
|
NSMutableString *md5String = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
|
|
|
|
for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
|
|
[md5String appendFormat:@"%02x", digest[i]];
|
|
|
|
return [NSString stringWithString:md5String];
|
|
}
|
|
|
|
+ (NSString *)sha1FromString:(NSString *)str {
|
|
const char *cStr = [str cStringUsingEncoding:NSUTF8StringEncoding];
|
|
NSData *data = [NSData dataWithBytes:cStr length:str.length];
|
|
|
|
uint8_t digest[CC_SHA1_DIGEST_LENGTH];
|
|
|
|
CC_SHA1(data.bytes, (CC_LONG)data.length, digest);
|
|
|
|
NSMutableString *sha1String = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
|
|
|
|
for(int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
|
|
[sha1String appendFormat:@"%02x", digest[i]];
|
|
|
|
return [NSString stringWithString:sha1String];
|
|
}
|
|
|
|
- (void)KMLog:(NSString *)logStr checkDebugPrinting:(BOOL)checkDebugPrinting {
|
|
if (!checkDebugPrinting || (checkDebugPrinting && self.debugPrintingOn)) {
|
|
NSLog(@"%@", logStr);
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:kKeymanDebugLogNotification object:self
|
|
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:logStr, @"log", nil]];
|
|
//[self.debugLog appendString:[NSString stringWithFormat:@"[%@] %@\n", [NSDate date], logStr]];
|
|
}
|
|
}
|
|
/*
|
|
- (NSMutableString *)debugLog {
|
|
if (_debugLog == nil)
|
|
_debugLog = [NSMutableString stringWithString:@""];
|
|
|
|
return _debugLog;
|
|
}*/
|
|
|
|
#pragma mark - InputViewController methods
|
|
|
|
- (void)updateViewConstraints {
|
|
[self dismissSubKeys];
|
|
[self dismissKeyPreview];
|
|
[self dismissKeyboardMenu];
|
|
[self resizeKeyboardIfNeeded];
|
|
}
|
|
|
|
- (void)inputViewDidLoad {
|
|
[self dismissSubKeys];
|
|
[self dismissKeyPreview];
|
|
[self dismissKeyboardMenu];
|
|
[self resizeKeyboard];
|
|
|
|
NSUserDefaults *aUserDef = [self activeUserDefaults];
|
|
NSUserDefaults *sUserDef = [NSUserDefaults standardUserDefaults];
|
|
NSDate *xDate = [[sUserDef objectForKey:kKeymanSynchronizeSWKeyboardKey] objectAtIndex:0];
|
|
NSDate *nDate = [[aUserDef objectForKey:kKeymanSynchronizeSWKeyboardKey] objectAtIndex:0];
|
|
BOOL shouldSynchronize = YES;
|
|
if (xDate != nil && nDate != nil)
|
|
shouldSynchronize = ([nDate compare:xDate] != NSOrderedSame)?YES:NO;
|
|
else if (nDate == nil)
|
|
shouldSynchronize = NO;
|
|
|
|
if ((!didSynchronize || shouldSynchronize) && [self canAccessSharedContainer]) {
|
|
[self synchronizeSWKeyboard];
|
|
if (keyboardID_ != nil && languageID_ != nil) {
|
|
shouldReloadKeyboard = YES;
|
|
[self reloadKeyboard];
|
|
}
|
|
didSynchronize = YES;
|
|
[sUserDef setObject:[aUserDef objectForKey:kKeymanSynchronizeSWKeyboardKey] forKey:kKeymanSynchronizeSWKeyboardKey];
|
|
[sUserDef synchronize];
|
|
}
|
|
}
|
|
|
|
- (void)inputViewWillRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
|
|
[self dismissSubKeys];
|
|
[self dismissKeyPreview];
|
|
[self dismissKeyboardMenu];
|
|
[self resizeKeyboardWithOrientation:toInterfaceOrientation];
|
|
if (self.keymanHelpOn) {
|
|
[helpBubbleView removeFromSuperview];
|
|
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(showHelpBubble) object:nil];
|
|
[self performSelector:@selector(showHelpBubble) withObject:nil afterDelay:1.5];
|
|
}
|
|
|
|
didResizeToOrientation = YES;
|
|
}
|
|
|
|
- (void)synchronizeSWKeyboard {
|
|
[self copyUserDefaultsFromSharedContainer];
|
|
[self copyKeymanFilesFromSharedContainer];
|
|
}
|
|
|
|
- (void)setMenuKeyFrame:(NSString *)frameStr {
|
|
CGRect frame = CGRectZero;
|
|
if ([frameStr length]) {
|
|
NSArray *values = [frameStr componentsSeparatedByString: @","];
|
|
CGFloat x = [[values objectAtIndex:0] floatValue];
|
|
CGFloat y = [[values objectAtIndex:1] floatValue];
|
|
CGFloat w = [[values objectAtIndex:2] floatValue];
|
|
CGFloat h = [[values objectAtIndex:3] floatValue];
|
|
|
|
BOOL isPad = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad;
|
|
CGFloat adjX = 0.0;
|
|
CGFloat adjY = isPad?-0.5:-1.0;
|
|
|
|
frame = CGRectMake(x+adjX -w/2.0f, y -adjY, w, h);
|
|
}
|
|
|
|
menuKeyFrame = frame;
|
|
}
|
|
|
|
- (void)showKeyboardMenu:(KMInputViewController *)ic closeButtonTitle:(NSString *)closeButtonTitle {
|
|
UIView *parentView = ic.view;
|
|
if (parentView == nil)
|
|
parentView = webView;
|
|
|
|
if ([[self class] isWKWebView:webView]) {
|
|
[(WKWebView *)webView evaluateJavaScript:@"langMenuPos();" completionHandler:^(id result, NSError *error) {
|
|
NSString *keyFrame = (NSString *)result;
|
|
[self setMenuKeyFrame:keyFrame];
|
|
if (!CGRectEqualToRect(menuKeyFrame, CGRectZero)) {
|
|
[keyboardMenuView removeFromSuperview];
|
|
keyboardMenuView = [[KMKeyboardMenuView alloc] initWithKeyFrame:menuKeyFrame inputViewController:ic closeButtonTitle:closeButtonTitle];
|
|
[parentView addSubview:keyboardMenuView];
|
|
}
|
|
}];
|
|
}
|
|
else {
|
|
NSString *keyFrame = [(UIWebView *)webView stringByEvaluatingJavaScriptFromString:@"langMenuPos();"];
|
|
[self setMenuKeyFrame:keyFrame];
|
|
if (!CGRectEqualToRect(menuKeyFrame, CGRectZero)) {
|
|
[keyboardMenuView removeFromSuperview];
|
|
keyboardMenuView = [[KMKeyboardMenuView alloc] initWithKeyFrame:menuKeyFrame inputViewController:ic closeButtonTitle:closeButtonTitle];
|
|
[parentView addSubview:keyboardMenuView];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)dismissKeyboardMenu {
|
|
if (keyboardMenuView != nil) {
|
|
[keyboardMenuView removeFromSuperview];
|
|
keyboardMenuView = nil;
|
|
}
|
|
}
|
|
|
|
- (BOOL)isKeyboardMenuVisible {
|
|
return keyboardMenuView == nil?NO:YES;
|
|
}
|
|
|
|
- (BOOL)isSystemKeyboardTopBarEnabled {
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)copyKeymanFilesToTemp {
|
|
if (NSTemporaryDirectory() == nil)
|
|
return NO;
|
|
|
|
NSFileManager *fileMan = [NSFileManager defaultManager];
|
|
NSString *activeKeymanDir = [NSString stringWithString:[self activeKeymanDirectory]];
|
|
NSString *tempKeymanDir = [NSString stringWithString:[NSTemporaryDirectory() stringByAppendingPathComponent:@"keyman"]];
|
|
[[NSFileManager defaultManager] createDirectoryAtPath:tempKeymanDir withIntermediateDirectories:YES attributes:nil error:nil];
|
|
NSString *activeLangDir = [NSString stringWithString:[self activeLanguageDirectory]];
|
|
NSString *tempLangDir = [NSString stringWithString:[tempKeymanDir stringByAppendingPathComponent:@"languages"]];
|
|
[[NSFileManager defaultManager] createDirectoryAtPath:tempLangDir withIntermediateDirectories:YES attributes:nil error:nil];
|
|
NSString *activeFontDir = [NSString stringWithString:[self activeFontDirectory]];
|
|
NSString *tempFontDir = [NSString stringWithString:[tempKeymanDir stringByAppendingPathComponent:@"fonts"]];
|
|
[[NSFileManager defaultManager] createDirectoryAtPath:tempFontDir withIntermediateDirectories:YES attributes:nil error:nil];
|
|
|
|
NSArray *dirContents = [fileMan contentsOfDirectoryAtPath:activeKeymanDir error:nil];
|
|
for (NSString *file in dirContents){
|
|
NSString *pathToCopy = [activeKeymanDir stringByAppendingPathComponent:file];
|
|
NSString *destinationPath = [tempKeymanDir stringByAppendingPathComponent:file];
|
|
[self copyAndExcludeFromBackup:pathToCopy destinationPath:destinationPath];
|
|
/* BOOL fileExists = [fileMan fileExistsAtPath:pathToCopy isDirectory:&isDirectory];
|
|
if (fileExists && !isDirectory) {
|
|
// copy if destination does not exist or replace if source is newer
|
|
if (![fileMan fileExistsAtPath:destinationPath])
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:nil];
|
|
else if ([self compareFileModDates:pathToCopy destinationPath:destinationPath] > 0) {
|
|
[fileMan removeItemAtPath:destinationPath error:&error];
|
|
if (error)
|
|
[self KMLog:[NSString stringWithFormat:@"%@", error.description] checkDebugPrinting:NO];
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:&error];
|
|
if (error)
|
|
[self KMLog:[NSString stringWithFormat:@"%@", error.description] checkDebugPrinting:NO];
|
|
}
|
|
}*/
|
|
}
|
|
|
|
dirContents = [fileMan contentsOfDirectoryAtPath:activeLangDir error:nil];
|
|
for (NSString *file in dirContents){
|
|
NSString *pathToCopy = [activeLangDir stringByAppendingPathComponent:file];
|
|
NSString *destinationPath = [tempLangDir stringByAppendingPathComponent:file];
|
|
[self copyAndExcludeFromBackup:pathToCopy destinationPath:destinationPath];
|
|
/* BOOL fileExists = [fileMan fileExistsAtPath:pathToCopy isDirectory:&isDirectory];
|
|
if (fileExists && !isDirectory) {
|
|
// copy if destination does not exist or replace if source is newer
|
|
if (![fileMan fileExistsAtPath:destinationPath])
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:nil];
|
|
else if ([self compareFileModDates:pathToCopy destinationPath:destinationPath] > 0) {
|
|
[fileMan removeItemAtPath:destinationPath error:&error];
|
|
if (error)
|
|
[self KMLog:[NSString stringWithFormat:@"%@", error.description] checkDebugPrinting:NO];
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:&error];
|
|
if (error)
|
|
[self KMLog:[NSString stringWithFormat:@"%@", error.description] checkDebugPrinting:NO];
|
|
}
|
|
}*/
|
|
}
|
|
|
|
dirContents = [fileMan contentsOfDirectoryAtPath:activeFontDir error:nil];
|
|
for (NSString *file in dirContents){
|
|
NSString *pathToCopy = [activeFontDir stringByAppendingPathComponent:file];
|
|
NSString *destinationPath = [tempFontDir stringByAppendingPathComponent:file];
|
|
[self copyAndExcludeFromBackup:pathToCopy destinationPath:destinationPath];
|
|
/* BOOL fileExists = [fileMan fileExistsAtPath:pathToCopy isDirectory:&isDirectory];
|
|
if (fileExists && !isDirectory) {
|
|
// copy if destination does not exist or replace if source is newer
|
|
if (![fileMan fileExistsAtPath:destinationPath])
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:nil];
|
|
else if ([self compareFileModDates:pathToCopy destinationPath:destinationPath] > 0) {
|
|
[fileMan removeItemAtPath:destinationPath error:&error];
|
|
if (error)
|
|
[self KMLog:[NSString stringWithFormat:@"%@", error.description] checkDebugPrinting:NO];
|
|
[fileMan copyItemAtPath:pathToCopy toPath:destinationPath error:&error];
|
|
if (error)
|
|
[self KMLog:[NSString stringWithFormat:@"%@", error.description] checkDebugPrinting:NO];
|
|
}
|
|
}*/
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
|
|
@end
|