diff --git a/ios/.swiftlint.yml b/ios/.swiftlint.yml index 99405ad7cc..e88ca41947 100644 --- a/ios/.swiftlint.yml +++ b/ios/.swiftlint.yml @@ -1,7 +1,10 @@ disabled_rules: + - file_length + - force_cast - function_body_length - function_parameter_count - todo + - type_body_length identifier_name: min_length: diff --git a/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj b/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj index 127982483c..9504633cd3 100644 --- a/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj +++ b/ios/keyman/Keyman/Keyman.xcodeproj/project.pbxproj @@ -18,7 +18,6 @@ 37BF5A3E1DE8404E0014AF19 /* KMTextFieldDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 98D418FC1769486C008D2FF3 /* KMTextFieldDelegateProxy.h */; }; 37BF5A3F1DE8404E0014AF19 /* KMTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = 98D418FD1769486C008D2FF3 /* KMTextView.h */; }; 37BF5A401DE8404E0014AF19 /* KMTextViewDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 98D418FE1769486C008D2FF3 /* KMTextViewDelegateProxy.h */; }; - 981AFA7F19EF44DE006706BF /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 98ABADCC176935E500B62590 /* MainViewController.m */; }; 981AFA8519EF44DE006706BF /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 98DE283417F50DBF00D6A496 /* AudioToolbox.framework */; }; 981AFA8619EF44DE006706BF /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 98D41903176952D0008D2FF3 /* CFNetwork.framework */; }; 981AFA8719EF44DE006706BF /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 98D41905176952E0008D2FF3 /* MobileCoreServices.framework */; }; @@ -188,6 +187,7 @@ C0A1A6371F5E8C9100EAF822 /* WebBrowserViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0A1A6361F5E8C9100EAF822 /* WebBrowserViewController.swift */; }; C0E943F81F61234C00E7D98C /* UIImage+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0E943F71F61234C00E7D98C /* UIImage+Helpers.swift */; }; C0E943FA1F6124E100E7D98C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0E943F91F6124E100E7D98C /* AppDelegate.swift */; }; + C0E943FE1F61377900E7D98C /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0E943FD1F61377900E7D98C /* MainViewController.swift */; }; C0FF769B1F5D368C00BD23C3 /* KeyboardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0FF769A1F5D368C00BD23C3 /* KeyboardViewController.swift */; }; C0FF769E1F5D4ECB00BD23C3 /* ActivityItemProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0FF769D1F5D4ECB00BD23C3 /* ActivityItemProvider.swift */; }; CE06AA681F171CA0006D91C3 /* Keyman.h in Headers */ = {isa = PBXBuildFile; fileRef = 98D418F71769486C008D2FF3 /* Keyman.h */; }; @@ -352,8 +352,6 @@ 98ABADBB176935E400B62590 /* Keyman-Free-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Keyman-Free-Info.plist"; sourceTree = ""; }; 98ABADBD176935E400B62590 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 98ABADC1176935E400B62590 /* Keyman-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Keyman-Prefix.pch"; sourceTree = ""; }; - 98ABADCB176935E500B62590 /* MainViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = MainViewController.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; - 98ABADCC176935E500B62590 /* MainViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = MainViewController.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 98ABADCF176935E500B62590 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainViewController_iPhone.xib; sourceTree = ""; }; 98ABADD2176935E500B62590 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainViewController_iPad.xib; sourceTree = ""; }; 98AD861219B56AAE00D25C26 /* 887-notepad-selected.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "887-notepad-selected.png"; path = "Keyman Icons/887-notepad-selected.png"; sourceTree = ""; }; @@ -397,6 +395,7 @@ C0A1A6361F5E8C9100EAF822 /* WebBrowserViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = WebBrowserViewController.swift; sourceTree = ""; tabWidth = 2; }; C0E943F71F61234C00E7D98C /* UIImage+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = "UIImage+Helpers.swift"; sourceTree = ""; tabWidth = 2; }; C0E943F91F6124E100E7D98C /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; tabWidth = 2; }; + C0E943FD1F61377900E7D98C /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; tabWidth = 2; }; C0FF76991F5D368B00BD23C3 /* SWKeyboard-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SWKeyboard-Bridging-Header.h"; sourceTree = ""; }; C0FF769A1F5D368C00BD23C3 /* KeyboardViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = KeyboardViewController.swift; sourceTree = ""; tabWidth = 2; }; C0FF769C1F5D4ECA00BD23C3 /* Keyman-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Keyman-Bridging-Header.h"; sourceTree = ""; }; @@ -662,14 +661,13 @@ 9822CB671A3A77D200D73CAA /* MainViewController_.xib */, 98ABADD1176935E500B62590 /* MainViewController_iPad.xib */, 98ABADCE176935E500B62590 /* MainViewController_iPhone.xib */, - 98ABADCB176935E500B62590 /* MainViewController.h */, - 98ABADCC176935E500B62590 /* MainViewController.m */, 9845A7C71A439A9200544E2E /* SetUpViewController */, 98ABADBA176935E400B62590 /* Supporting Files */, C0FF769D1F5D4ECB00BD23C3 /* ActivityItemProvider.swift */, C0FF769C1F5D4ECA00BD23C3 /* Keyman-Bridging-Header.h */, C0E943F71F61234C00E7D98C /* UIImage+Helpers.swift */, C0E943F91F6124E100E7D98C /* AppDelegate.swift */, + C0E943FD1F61377900E7D98C /* MainViewController.swift */, ); path = Keyman; sourceTree = ""; @@ -1109,9 +1107,9 @@ C055F0B11F60E8D400140735 /* GetStartedViewController.swift in Sources */, C055F0B51F610FB200140735 /* DropDownListView.swift in Sources */, C0E943F81F61234C00E7D98C /* UIImage+Helpers.swift in Sources */, + C0E943FE1F61377900E7D98C /* MainViewController.swift in Sources */, C055F0B31F60F7A400140735 /* DropDownView.swift in Sources */, C0A1A6371F5E8C9100EAF822 /* WebBrowserViewController.swift in Sources */, - 981AFA7F19EF44DE006706BF /* MainViewController.m in Sources */, C0E943FA1F6124E100E7D98C /* AppDelegate.swift in Sources */, C0A1A6351F5E786A00EAF822 /* BookmarksViewController.swift in Sources */, C0A1A6331F5E749000EAF822 /* SetUpViewController.swift in Sources */, diff --git a/ios/keyman/Keyman/Keyman/AppDelegate.swift b/ios/keyman/Keyman/Keyman/AppDelegate.swift index ba39006e65..88289d80a9 100644 --- a/ios/keyman/Keyman/Keyman/AppDelegate.swift +++ b/ios/keyman/Keyman/Keyman/AppDelegate.swift @@ -41,8 +41,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool { - NotificationCenter.default.post(name: NSNotification.Name.keymanLaunchedFromUrl, object: self, - userInfo: [kKeymanUrlKey: url] + NotificationCenter.default.post(name: launchedFromUrlNotification, object: self, + userInfo: [urlKey: url] ) return true } @@ -51,8 +51,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { _overlayWindow = nil KMManager.sharedInstance().unregisterCustomFonts() let userData = AppDelegate.activeUserDefaults() - userData.set(viewController?.textView?.text, forKey: kKeymanUserTextKey) - userData.set(viewController?.textSize.description, forKey: kKeymanUserTextSizeKey) + userData.set(viewController?.textView?.text, forKey: userTextKey) + userData.set(viewController?.textSize.description, forKey: userTextSizeKey) userData.synchronize() } diff --git a/ios/keyman/Keyman/Keyman/GetStartedViewController/GetStartedViewController.swift b/ios/keyman/Keyman/Keyman/GetStartedViewController/GetStartedViewController.swift index bb1a723d89..6bfa9c862d 100644 --- a/ios/keyman/Keyman/Keyman/GetStartedViewController/GetStartedViewController.swift +++ b/ios/keyman/Keyman/Keyman/GetStartedViewController/GetStartedViewController.swift @@ -9,7 +9,7 @@ import UIKit class GetStartedViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { - var mainViewController: UIViewController? + var mainViewController: MainViewController? @IBOutlet var navItem: UINavigationItem! @IBOutlet var tableView: UITableView! @@ -138,16 +138,15 @@ class GetStartedViewController: UIViewController, UITableViewDelegate, UITableVi func performAction(for indexPath: IndexPath) { switch indexPath.section { case 0: - (mainViewController as? MainViewController)?.dismissGetStartedView(nil) - KMManager.sharedInstance().showKeyboardPicker( - in: mainViewController, shouldAddKeyboard: true) + mainViewController?.dismissGetStartedView(nil) + KMManager.sharedInstance().showKeyboardPicker(in: mainViewController, shouldAddKeyboard: true) case 1: - (mainViewController as? MainViewController)?.dismissGetStartedView(nil) + mainViewController?.dismissGetStartedView(nil) let setUpVC = SetUpViewController() - (mainViewController as? MainViewController)?.present(setUpVC, animated: true, completion: nil) + mainViewController?.present(setUpVC, animated: true, completion: nil) case 2: - (mainViewController as? MainViewController)?.dismissGetStartedView(nil) - (mainViewController as? MainViewController)?.infoButtonClick(nil) + mainViewController?.dismissGetStartedView(nil) + mainViewController?.infoButtonClick(nil) default: break } @@ -184,13 +183,13 @@ class GetStartedViewController: UIViewController, UITableViewDelegate, UITableVi func switchValueChanged(_ sender: Any) { let userData = AppDelegate.activeUserDefaults() if let toggle = sender as? UISwitch { - userData.set(toggle.isOn, forKey: kKeymanDontShowGetStartedKey) + userData.set(toggle.isOn, forKey: dontShowGetStartedKey) userData.synchronize() } } func dontShowGetStarted() -> Bool { let userData = AppDelegate.activeUserDefaults() - return userData.bool(forKey: kKeymanDontShowGetStartedKey) + return userData.bool(forKey: dontShowGetStartedKey) } } diff --git a/ios/keyman/Keyman/Keyman/Keyman-Bridging-Header.h b/ios/keyman/Keyman/Keyman/Keyman-Bridging-Header.h index 1384aee4b8..7d70d01f0c 100644 --- a/ios/keyman/Keyman/Keyman/Keyman-Bridging-Header.h +++ b/ios/keyman/Keyman/Keyman/Keyman-Bridging-Header.h @@ -3,5 +3,4 @@ // #import "Keyman.h" -#import "MainViewController.h" #import "Reachability.h" diff --git a/ios/keyman/Keyman/Keyman/MainViewController.h b/ios/keyman/Keyman/Keyman/MainViewController.h deleted file mode 100644 index f76a6ec4a9..0000000000 --- a/ios/keyman/Keyman/Keyman/MainViewController.h +++ /dev/null @@ -1,39 +0,0 @@ -// -// MainViewController.h -// Keyman for iPhone and iPad -// -// Created by Serkan Kurt on 13/06/13. -// Copyright (c) 2017 SIL International. All rights reserved. -// - -#import -#import "Keyman.h" - -extern NSString *const kKeymanUserTextKey; -extern NSString *const kKeymanUserTextSizeKey; -extern NSString *const kKeymanDontShowGetStartedKey; -extern NSString *const kKeymanLaunchedFromUrlNotification; -extern NSString *const kKeymanUrlKey; - -@interface MainViewController : UIViewController { - NSUInteger iosVersion; - CGFloat textSize; - CGFloat screenWidth; - CGFloat screenHeight; - CGFloat portLeftMargin; - CGFloat portRightMargin; - CGFloat lscpeLeftMargin; - CGFloat lscpeRightMargin; - NSTimer *loadTimer; - NSInteger updateStatus; - BOOL didDownload; -} - -//@property (strong, nonatomic) UIWindow *overlayWindow; -@property (strong, nonatomic) KMTextView *textView; -@property CGFloat textSize; - -- (void)infoButtonClick:(id)sender; -- (void)dismissGetStartedView:(id)sender; - -@end diff --git a/ios/keyman/Keyman/Keyman/MainViewController.m b/ios/keyman/Keyman/Keyman/MainViewController.m deleted file mode 100644 index dd23b5906b..0000000000 --- a/ios/keyman/Keyman/Keyman/MainViewController.m +++ /dev/null @@ -1,1571 +0,0 @@ -// -// MainViewController.m -// Keyman for iPhone and iPad -// -// Created by Serkan Kurt on 13/06/13. -// Copyright (c) 2017 SIL International. All rights reserved. -// - -#import "MainViewController.h" -#import "CoreText/CTFontManager.h" -#import -#import - -// Internal strings -NSString *const baseUri = @"http://r.keymanweb.com/20/fonts/get_mobileconfig.php?id="; -NSString *const kKeymanProfileKey = @"profile"; -NSString *const kKeymanCheckedProfilesKey = @"CheckedProfiles"; - -// External strings -NSString *const kKeymanUserTextKey = @"UserText"; -NSString *const kKeymanUserTextSizeKey = @"UserTextSize"; -#ifdef FREE -NSString *const kKeymanDontShowGetStartedKey = @"DontShowGetStarted"; -#else -NSString *const kKeymanDontShowGetStartedKey = @"DontShowGetStarted-Pro"; -#endif -NSString *const kKeymanLaunchedFromUrlNotification = @"kKeymanLaunchedFromUrlNotification"; -NSString *const kKeymanUrlKey = @"url"; - -CGFloat const minTextSize = 9.0; -CGFloat const maxTextSize = 72.0; - -NSInteger const getStartedViewTag = 7183; -NSInteger const activityIndicatorViewTag = 6573; -NSInteger const dropDownListTag = 686876; - -NSUInteger const minIOSVersion4FontInstall = 7; - -@interface MainViewController () - -@property (strong, nonatomic) GetStartedViewController *getStartedVC; -@property (strong, nonatomic) InfoViewController *infoView; -@property (strong, nonatomic) UIPopoverController *popover; -@property (strong, nonatomic) UIActionSheet *actionSheet; -@property (strong, nonatomic) UISlider *textSizeController; - -@property (strong, nonatomic) UIBarButtonItem *moreButton; -@property (strong, nonatomic) UIBarButtonItem *infoButton; -@property (strong, nonatomic) UIBarButtonItem *getStartedButton; -@property (strong, nonatomic) UIBarButtonItem *trashButton; -@property (strong, nonatomic) UIBarButtonItem *textSizeButton; -@property (strong, nonatomic) UIBarButtonItem *browserButton; -@property (strong, nonatomic) UIBarButtonItem *actionButton; - -//@property (strong, nonatomic) NSMutableDictionary *profileNamesByKeyboardID; -@property (strong, nonatomic) NSMutableArray *profiles2Install; -@property (strong, nonatomic) NSMutableArray *checkedProfiles; -@property (strong, nonatomic) NSString *profileName; -@property (strong, nonatomic) NSURL *launchUrl; -@property (strong, nonatomic) NSDictionary *keyboard2Download; -@property (strong, nonatomic) NSDictionary *customKeyboard2Download; -@property (assign, nonatomic) BOOL wasKeyboardVisible; - -@end - -@implementation MainViewController -@synthesize textView, textSize, infoView, popover, actionSheet, textSizeController; -@synthesize moreButton, getStartedButton, infoButton, trashButton; -@synthesize textSizeButton, actionButton, browserButton; -@synthesize profiles2Install, checkedProfiles, profileName, launchUrl; -@synthesize keyboard2Download, customKeyboard2Download, wasKeyboardVisible; - -- (AppDelegate *)appDelegate { - return (AppDelegate *)[[UIApplication sharedApplication] delegate]; -} - -- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { - // Setup Notifications - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardWillShowNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardLoaded:) name:kKeymanKeyboardLoadedNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardChanged:) name:kKeymanKeyboardChangedNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(languagesUpdated:) name:kKeymanLanguagesUpdatedNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(languagesDownloadFailed:) name:kKeymanLanguagesDownloadFailedNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardPickerDismissed:) name:kKeymanKeyboardPickerDismissedNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDownloadStarted:) name:kKeymanKeyboardDownloadStartedNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDownloadFinished:) name:kKeymanKeyboardDownloadCompletedNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDownloadFailed:) name:kKeymanKeyboardDownloadFailedNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardRemoved:) name:kKeymanKeyboardRemovedNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(launchedFromUrl:) name:kKeymanLaunchedFromUrlNotification object:nil]; - //[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(registeredFontsChanged:) name:(__bridge NSString *)kCTFontManagerRegisteredFontsChangedNotification object:nil]; - - } - return self; -} - -- (void)viewDidLoad { - [super viewDidLoad]; - - if ([self respondsToSelector:@selector(setExtendedLayoutIncludesOpaqueBars:)]) - [self setExtendedLayoutIncludesOpaqueBars:YES]; - if ([self respondsToSelector:@selector(setAutomaticallyAdjustsScrollViewInsets:)]) - [self setAutomaticallyAdjustsScrollViewInsets:NO]; - - iosVersion = [[[UIDevice currentDevice] systemVersion] intValue]; - NSArray *systemFonts = [NSArray arrayWithArray:[UIFont familyNames]]; - - CGRect screenRect = [[UIScreen mainScreen] bounds]; - UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; - if (iosVersion >= 8) { - screenWidth = screenRect.size.width; - screenHeight = screenRect.size.height; - } - else { - if (UIInterfaceOrientationIsPortrait(orientation)) { - screenWidth = screenRect.size.width; - screenHeight = screenRect.size.height; - } - else { - screenWidth = screenRect.size.height; - screenHeight = screenRect.size.width; - } - } - - // Setup Keyman Manager & fetch keyboards list - updateStatus = 0; - [KMManager setApplicationGroupIdentifier:@"group.KM4I"]; - - #ifdef DEBUG - [[KMManager sharedInstance] setDebugPrintingOn:YES]; - #endif - - #ifdef FREE - [[KMManager sharedInstance] setCanRemoveDefaultKeyboard:NO]; - #else - [[KMManager sharedInstance] setCanRemoveDefaultKeyboard:YES]; - #endif - [[KMManager sharedInstance] fetchKeyboardsList]; - - UIColor *bgColor = [UIColor colorWithRed:1.0 green:1.0 blue:207.0/255.0 alpha:1.0]; - [self.view setBackgroundColor:bgColor]; - - // Check for configuration profiles/fonts to install for iOS 7+ - if (iosVersion >= minIOSVersion4FontInstall) { - NSMutableDictionary *kmFonts = [NSMutableDictionary dictionaryWithDictionary:[[KMManager sharedInstance] keymanFonts]]; - [kmFonts removeObjectForKey:@"keymanweb-osk.ttf"]; - NSArray *allKeys = [NSArray arrayWithArray:[kmFonts allKeys]]; - NSUInteger length = [allKeys count]; - NSMutableDictionary *profilesByFontName = [NSMutableDictionary dictionaryWithCapacity:length]; - for (int i = 0; i < length; i++) { - NSString *key = [allKeys objectAtIndex:i]; - NSString *value = [[kmFonts objectForKey:key] objectForKey:kKeymanFontNameKey]; - NSUInteger index = [key rangeOfString:@"." options:NSBackwardsSearch].location; - NSString *type = [key substringFromIndex:index]; - [profilesByFontName setObject:[key stringByReplacingOccurrencesOfString:type withString:@".mobileconfig"] forKey:value]; - } - - NSMutableArray *customFonts = [NSMutableArray arrayWithArray:[UIFont familyNames]]; - [customFonts removeObjectsInArray:systemFonts]; - [customFonts removeObjectsInArray:@[@"KeymanwebOsk"]]; - - NSUserDefaults *userData = [AppDelegate activeUserDefaults]; - checkedProfiles = [[userData objectForKey:kKeymanCheckedProfilesKey] mutableCopy]; - if (checkedProfiles == nil) - checkedProfiles = [NSMutableArray arrayWithCapacity:0]; - - profiles2Install = [NSMutableArray arrayWithCapacity:[customFonts count]]; - for (NSString *name in customFonts) { - [profiles2Install addObject:[profilesByFontName objectForKey:[[UIFont fontNamesForFamilyName:name] objectAtIndex:0]]]; - } - } - - // Setup NavigationBar - if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { - CGFloat size = screenRect.size.height > screenRect.size.width?screenRect.size.height:screenRect.size.width; - if (size > 568.0) { - // Navbar for iPhone 6 & 6 Plus - UIImage *bgImg = nil; - if (UIInterfaceOrientationIsPortrait(orientation)) - bgImg = [[UIImage imageNamed:@"navbar-Portrait.png"] - resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0) - resizingMode:UIImageResizingModeStretch]; - else - bgImg = [[UIImage imageNamed:@"navbar-Landscape-568h.png"] - resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0) - resizingMode:UIImageResizingModeStretch]; - - [self.navigationController.navigationBar setBackgroundImage:bgImg - forBarMetrics:UIBarMetricsDefault]; - //[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"navbar-Landscape-568h.png"] forBarMetrics:UIBarMetricsCompact]; - } - else if (size == 568) { - // Navbar for iPhone and iPod Touch with 4" Display - [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"navbar-Portrait.png"] forBarMetrics:UIBarMetricsDefault]; - [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"navbar-Landscape-568h.png"] forBarMetrics:UIBarMetricsCompact]; - } - else if (size < 568.0) { - // Navbar for iPhone and iPod Touch with 3.5" Display - [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"navbar-Portrait.png"] forBarMetrics:UIBarMetricsDefault]; - [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"navbar-Landscape.png"] forBarMetrics:UIBarMetricsCompact]; - } - } - else { - // Navbar for iPad - if (UIInterfaceOrientationIsPortrait(orientation)) - [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"navbar-Portrait.png"] forBarMetrics:UIBarMetricsDefault]; - else - [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"navbar-Landscape.png"] forBarMetrics:UIBarMetricsDefault]; - } - - // Setup Keyman TextView - textSize = 16.0; - textView = [[KMTextView alloc] initWithFrame:screenRect]; - [textView setKeymanDelegate:self]; - [textView setViewController:self]; - [textView setBackgroundColor:bgColor]; - [textView setScrollEnabled:YES]; - [textView setUserInteractionEnabled:YES]; - textView.font = [textView.font fontWithSize:textSize]; - [self.view addSubview:textView]; - [textView setEditable:NO]; - - // Setup Info View - if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { - infoView = [[InfoViewController alloc] initWithNibName:@"InfoViewController_iPhone" bundle:nil]; - } - else { - infoView = [[InfoViewController alloc] initWithNibName:@"InfoViewController_iPad" bundle:nil]; - textSize *= 2; - textView.font = [textView.font fontWithSize:textSize]; - } - - infoView.view.hidden = YES; - [infoView.view setBackgroundColor:bgColor]; - [self.view addSubview:infoView.view]; - - [self resizeViewsWithKeyboardVisible:[textView isFirstResponder]]; - - // Setup Text Size Controller - textSizeController = [[UISlider alloc] init]; - [textSizeController setFrame:CGRectZero]; - [textSizeController setMinimumValue:0.0]; - [textSizeController setMaximumValue:(maxTextSize - minTextSize)]; - [textSizeController setValue:(textSize - minTextSize)]; - UIImage *textSizeUp = [[UIImage imageNamed:@"textsize_up.png"] resizeTo:CGSizeMake(20, 20)]; - [textSizeController setMaximumValueImage:textSizeUp]; - UIImage *textSizeDown = [[UIImage imageNamed:@"textsize_up.png"] resizeTo:CGSizeMake(15, 15)]; - [textSizeController setMinimumValueImage:textSizeDown]; - [textSizeController addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged]; - - [self setNavBarButtons]; -} - -- (void)viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:animated]; - wasKeyboardVisible = [textView isFirstResponder]; -} - -- (void)setNavBarButtons { - UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; - UIBarButtonItem *fixedSpace = [[UIBarButtonItem alloc] - initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace - target:nil - action:nil]; - - CGFloat imageScaleF = 0.9; - moreButton = [self createNavBarButtonWithImage:[UIImage imageNamed:@"more.png"] - highlightedImage:[UIImage imageNamed:@"more-selected.png"] - imageScale:imageScaleF - action:@selector(showDropDownMenu:) - orientation:orientation]; - moreButton.title = @"More"; - - infoButton = [self createNavBarButtonWithImage:[UIImage imageNamed:@"724-info.png"] - highlightedImage:[UIImage imageNamed:@"724-info-selected.png"] - imageScale:imageScaleF - action:@selector(infoButtonClick:) - orientation:orientation]; - infoButton.title = @"Info"; - - getStartedButton = [self createNavBarButtonWithImage:[UIImage imageNamed:@"887-notepad.png"] - highlightedImage:[UIImage imageNamed:@"887-notepad-selected.png"] - imageScale:imageScaleF - action:@selector(showGetStartedView:) - orientation:orientation]; - getStartedButton.title = @"Get Started"; - - trashButton = [self createNavBarButtonWithImage:[UIImage imageNamed:@"711-trash.png"] - highlightedImage:[UIImage imageNamed:@"711-trash-selected.png"] - imageScale:imageScaleF - action:@selector(trashButtonClick:) - orientation:orientation]; - trashButton.title = @"Clear Text"; - - textSizeButton = [self createNavBarButtonWithImage:[UIImage imageNamed:@"textsize.png"] - highlightedImage:[UIImage imageNamed:@"textsize_selected.png"] - imageScale:imageScaleF - action:@selector(textSizeButtonClick:) - orientation:orientation]; - textSizeButton.title = @"Text Size"; - - #ifndef FREE - browserButton = [self createNavBarButtonWithImage:[UIImage imageNamed:@"786-browser.png"] - highlightedImage:[UIImage imageNamed:@"786-browser-selected.png"] - imageScale:1.0 - action:@selector(showKMWebBrowserView:) - orientation:orientation]; - browserButton.title = @"Browser"; - #endif - - actionButton = [self createNavBarButtonWithImage:[UIImage imageNamed:@"702-share.png"] - highlightedImage:[UIImage imageNamed:@"702-share-selected.png"] - imageScale:imageScaleF - action:@selector(actionButtonClick:) - orientation:orientation]; - actionButton.title = @"Share"; - - CGFloat scaleF = ([UIScreen mainScreen].scale == 2.0)?2.0:1.5; - scaleF *= ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)?1.0:2.0; - fixedSpace.width = 10*scaleF; - - if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { - #ifdef FREE - [[self navigationItem] setRightBarButtonItems:@[moreButton, fixedSpace, textSizeButton, fixedSpace, actionButton]]; - #else - [[self navigationItem] setRightBarButtonItems:@[moreButton, fixedSpace, browserButton, fixedSpace, actionButton]]; - #endif - } - else { - #ifdef FREE - [[self navigationItem] setRightBarButtonItems:@[infoButton, fixedSpace, getStartedButton, fixedSpace, trashButton, fixedSpace, textSizeButton, fixedSpace, actionButton]]; - #else - [[self navigationItem] setRightBarButtonItems:@[infoButton, fixedSpace, getStartedButton, fixedSpace, trashButton, fixedSpace, textSizeButton, fixedSpace, browserButton, fixedSpace, actionButton]]; - #endif - } -} - -- (UIBarButtonItem *)createNavBarButtonWithImage:(UIImage *)image highlightedImage:(UIImage *)imageHighlighted imageScale:(CGFloat)scaleF action:(SEL)selector orientation:(UIInterfaceOrientation)orientation { - UIImage *icon = [image resizeTo:CGSizeMake(image.size.width*scaleF, image.size.height*scaleF)]; - UIImage *iconHighlighted = [imageHighlighted resizeTo:CGSizeMake(imageHighlighted.size.width*scaleF, imageHighlighted.size.height*scaleF)]; - - if ([UIImage instancesRespondToSelector:@selector(imageWithRenderingMode:)]) { // This fixes icon display issues for iOS 7 SDK - icon = [icon imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; - iconHighlighted = [iconHighlighted imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; - } - - CGFloat vAdjPort = -2.6; - CGFloat vAdjLscpe = -1.6; - if ([UIScreen mainScreen].scale == 2.0) - vAdjPort = -3.6; - - CGFloat vAdj = UIInterfaceOrientationIsPortrait(orientation)?vAdjPort:vAdjLscpe; - CGFloat navBarHeight = [self navBarHeight]; - CGFloat y = (navBarHeight - icon.size.height)/2.0 + vAdj; - - UIButton *customButton = [UIButton buttonWithType:UIButtonTypeCustom]; - [customButton setImage:icon forState:UIControlStateNormal]; - [customButton setImage:iconHighlighted forState:UIControlStateHighlighted]; - [customButton setFrame:CGRectMake(0.0, y, icon.size.width, icon.size.height)]; - [customButton addTarget:self action:selector - forControlEvents:UIControlEventTouchUpInside]; - UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, icon.size.width, navBarHeight)]; - [containerView setBackgroundColor:[UIColor clearColor]]; - [containerView addSubview:customButton]; - UIBarButtonItem *navbarButton = [[UIBarButtonItem alloc] initWithCustomView:containerView]; - return navbarButton; -} - -- (void)viewDidAppear:(BOOL)animated { - [super viewDidAppear:animated]; - - if (wasKeyboardVisible) - [textView becomeFirstResponder]; - - if (![textView isEditable]) - [self showActivityIndicator]; - else if ([self shouldShowGetStarted]) - [self performSelector:@selector(showGetStartedView:) withObject:nil afterDelay:1.0]; -} - -- (void)viewDidUnload { - [super viewDidUnload]; - // Release any retained subviews of the main view. - self.textView = nil; - self.infoView = nil; - self.popover = nil; - self.actionSheet = nil; - self.textSizeController = nil; -} - -- (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -- (void)didReceiveMemoryWarning { - [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. -} - -- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { - UIInterfaceOrientation orientation = toInterfaceOrientation; - for (UIView *view in [self.overlayWindow subviews]) { - [UIView animateWithDuration:duration - delay:0.0 - options:(UIViewAnimationOptionTransitionNone) - animations:^{[view setTransform:[self transformForOrientation:orientation]];} - completion:nil]; - } - - if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { - CGRect screenRect = [[UIScreen mainScreen] bounds]; - CGFloat size = screenRect.size.height > screenRect.size.width?screenRect.size.height:screenRect.size.width; - if (size > 568.0) { - // Navbar for iPhone 6 & 6 Plus - UIImage *bgImg = nil; - if (UIInterfaceOrientationIsPortrait(orientation)) - bgImg = [[UIImage imageNamed:@"navbar-Portrait.png"] - resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0) - resizingMode:UIImageResizingModeStretch]; - else - bgImg = [[UIImage imageNamed:@"navbar-Landscape-568h.png"] - resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0) - resizingMode:UIImageResizingModeStretch]; - - [self.navigationController.navigationBar setBackgroundImage:bgImg - forBarMetrics:UIBarMetricsDefault]; - } - } - else { - if (UIInterfaceOrientationIsPortrait(orientation)) - [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"navbar-Portrait.png"] forBarMetrics:UIBarMetricsDefault]; - else - [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"navbar-Landscape.png"] forBarMetrics:UIBarMetricsDefault]; - } - - [popover dismissPopoverAnimated:NO]; - [self dismissDropDownMenu]; - [actionSheet dismissWithClickedButtonIndex:actionSheet.cancelButtonIndex animated:NO]; -} - -- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { - if ([infoView.view isHidden]) - [self setNavBarButtons]; - [self resizeViewsWithKeyboardVisible:[self.textView isFirstResponder]]; -} - -- (void)resizeViewsWithKeyboardVisible:(BOOL)keyboardVisible { - // Resize textView and infoView - CGRect mainScreen = [[UIScreen mainScreen] bounds]; - CGFloat margin = 2.0; - CGFloat width, height; - CGFloat barHeights = [AppDelegate statusBarHeight] + [self navBarHeight]; - CGFloat kbHeight = keyboardVisible?CGRectGetHeight(textView.inputView.frame):0; - - if (iosVersion >= 8) { - width = mainScreen.size.width; - height = mainScreen.size.height - barHeights - kbHeight; - } - else { - UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; - if (UIInterfaceOrientationIsPortrait(orientation)) { - width = CGRectGetWidth(mainScreen); - height = CGRectGetHeight(mainScreen) - barHeights - kbHeight; - } - else { - width = CGRectGetHeight(mainScreen); - height = CGRectGetWidth(mainScreen) - barHeights - kbHeight; - } - } - - [self.textView setFrame:CGRectMake(margin, ((iosVersion >= 7)?barHeights:0) + margin, width - 2*margin, height - 2*margin)]; - [self.infoView.view setFrame:CGRectMake(0, (iosVersion >= 7)?barHeights:0, width, height + kbHeight)]; -} - -- (CGFloat)navBarWidth { - CGRect navBarFrame = self.navigationController.navigationBar.frame; - return CGRectGetWidth(navBarFrame); -} - -- (CGFloat)navBarHeight { - CGRect navBarFrame = self.navigationController.navigationBar.frame; - return CGRectGetHeight(navBarFrame); -} - -#pragma mark - Keyboard notifications - -- (void)keyboardWillShow:(NSNotification *)notification { - [self dismissDropDownMenu]; - [self resizeViewsWithKeyboardVisible:YES]; -} - -- (void)keyboardDidShow:(NSNotification *)notification { - // Workaround to display overlay window above keyboard - if (iosVersion >= 9) { - NSArray *windows = [[UIApplication sharedApplication] windows]; - UIWindow *lastWindow = (UIWindow *)[windows lastObject]; - [self.overlayWindow setWindowLevel:lastWindow.windowLevel+1]; - } -} - -- (void)keyboardWillHide:(NSNotification *)notification { - [self resizeViewsWithKeyboardVisible:NO]; -} - -#pragma mark - Keyman notifications - -- (void)keyboardLoaded:(NSNotification *)notification { - [self startTimer]; -} - -- (void)keyboardChanged:(NSNotification *)notification { - BOOL listCheck = YES; - if (didDownload) { - listCheck = NO; - didDownload = NO; - } - - if (iosVersion >= minIOSVersion4FontInstall) { - NSDictionary *kbInfo = [[notification userInfo] objectForKey:kKeymanKeyboardInfoKey]; - NSString *kbID = [kbInfo objectForKey:kKeymanKeyboardIdKey]; - NSString *langID = [kbInfo objectForKey:kKeymanLanguageIdKey]; - [self checkProfileForKeyboardID:kbID languageID:langID doListCheck:listCheck]; - } -} - -- (void)keyboardDownloadStarted:(NSNotification *)notification { - if (self.launchUrl != nil) { - [self showActivityIndicator]; - } -} - -- (void)keyboardDownloadFinished:(NSNotification *)notification { - didDownload = YES; - if (self.launchUrl != nil) { - NSUserDefaults *userData = [AppDelegate activeUserDefaults]; - NSArray *userKeyboards = [userData arrayForKey:kKeymanUserKeyboardsListKey]; - if (userKeyboards == nil || ![userKeyboards count]) { - BOOL isRTL = [kKeymanDefaultKeyboardRTL isEqualToString:@"Y"]?YES:NO; - [[KMManager sharedInstance] addKeyboardWithID:kKeymanDefaultKeyboardID languageID:kKeymanDefaultLanguageID keyboardName:kKeymanDefaultKeyboardName languageName:kKeymanDefaultLanguageName isRTL:isRTL isCustom:NO font:kKeymanDefaultKeyboardFont oskFont:nil]; - } - - [self performSelector:@selector(dismissActivityIndicator) withObject:nil afterDelay:1.0]; - NSDictionary *kbInfo = [[notification userInfo] objectForKey:kKeymanKeyboardInfoKey]; - NSString *kbID = nil; - NSString *langID = nil; - if ([kbInfo isKindOfClass:[NSDictionary class]]) { - kbID = [(NSDictionary *)kbInfo objectForKey:kKeymanKeyboardIdKey]; - langID = [(NSDictionary *)kbInfo objectForKey:kKeymanLanguageIdKey]; - if ([self.launchUrl.query rangeOfString:@"url="].location == NSNotFound) { - NSString *dictKey = [NSString stringWithFormat:@"%@_%@", langID, kbID]; - NSDictionary *kbDict = [[[KMManager sharedInstance] keyboardsDictionary] objectForKey:dictKey]; - if (kbDict != nil) { - NSString *kbName = [kbDict objectForKey:kKeymanKeyboardNameKey]; - NSString *langName = [kbDict objectForKey:kKeymanLanguageNameKey]; - BOOL isRTL = [[kbDict objectForKey:kKeymanKeyboardRTLKey] isEqualToString:@"Y"]?YES:NO; - NSString *font = [kbDict objectForKey:kKeymanFontKey]; - NSString *oskFont = [kbDict objectForKey:kKeymanOskFontKey]; - [[KMManager sharedInstance] addKeyboardWithID:kbID languageID:langID keyboardName:kbName languageName:langName isRTL:isRTL isCustom:NO font:font oskFont:oskFont]; - [[KMManager sharedInstance] setKeyboardWithID:kbID languageID:langID keyboardName:kbName languageName:langName font:font oskFont:oskFont]; - } - } - else { - NSString *kbName = [(NSDictionary *)kbInfo objectForKey:kKeymanKeyboardNameKey]; - NSString *langName = [(NSDictionary *)kbInfo objectForKey:kKeymanLanguageNameKey]; - BOOL isRTL = [[(NSDictionary *)kbInfo objectForKey:kKeymanKeyboardRTLKey] isEqualToString:@"Y"]?YES:NO; - BOOL isCustom = [[(NSDictionary *)kbInfo objectForKey:kKeymanCustomKeyboardKey] isEqualToString:@"Y"]?YES:NO; - NSString *font = [(NSDictionary *)kbInfo objectForKey:kKeymanFontKey]; - NSString *oskFont = [(NSDictionary *)kbInfo objectForKey:kKeymanOskFontKey]; - [[KMManager sharedInstance] addKeyboardWithID:kbID languageID:langID keyboardName:kbName languageName:langName isRTL:isRTL isCustom:isCustom font:font oskFont:oskFont]; - [[KMManager sharedInstance] setKeyboardWithID:kbID languageID:langID keyboardName:kbName languageName:langName font:font oskFont:oskFont]; - } - } - else { - for (NSDictionary *info in (NSArray *)kbInfo) { - kbID = [info objectForKey:kKeymanKeyboardIdKey]; - langID = [info objectForKey:kKeymanLanguageIdKey]; - NSString *kbName = [info objectForKey:kKeymanKeyboardNameKey]; - NSString *langName = [info objectForKey:kKeymanLanguageNameKey]; - BOOL isRTL = [[info objectForKey:kKeymanKeyboardRTLKey] isEqualToString:@"Y"]?YES:NO; - BOOL isCustom = [[info objectForKey:kKeymanCustomKeyboardKey] isEqualToString:@"Y"]?YES:NO; - NSString *font = [info objectForKey:kKeymanFontKey]; - NSString *oskFont = [info objectForKey:kKeymanOskFontKey]; - [[KMManager sharedInstance] addKeyboardWithID:kbID languageID:langID keyboardName:kbName languageName:langName isRTL:isRTL isCustom:isCustom font:font oskFont:oskFont]; - [[KMManager sharedInstance] setKeyboardWithID:kbID languageID:langID keyboardName:kbName languageName:langName font:font oskFont:oskFont]; - } - } - - self.launchUrl = nil; - } -} - -- (void)keyboardDownloadFailed:(NSNotification *)notification { - if (self.launchUrl != nil) { - [self performSelector:@selector(dismissActivityIndicator) withObject:nil afterDelay:1.0]; - NSError *error = [[notification userInfo] objectForKey:NSUnderlyingErrorKey]; - [self showAlertWithTitle:@"Keyboard Download Error" - message:error.localizedDescription - cancelButtonTitle:@"OK" - otherButtonTitles:nil - tag:-1]; - - self.launchUrl = nil; - } -} - -- (void)languagesUpdated:(NSNotification *)notification { - updateStatus = 1; -} - -- (void)languagesDownloadFailed:(NSNotification *)notification { - updateStatus = -1; -} - -- (void)keyboardPickerDismissed:(NSNotification *)notification { - [textView becomeFirstResponder]; - if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { - [self performSelector:@selector(showGetStartedView) withObject:nil afterDelay:1.0]; - } -} - -- (void)showGetStartedView { - if ([self shouldShowGetStarted]) - [self showGetStartedView:nil]; -} - -- (void)launchedFromUrl:(NSNotification *)notification { - NSURL *url = [[notification userInfo] objectForKey:kKeymanUrlKey]; - if ([url query] != nil) { - self.launchUrl = url; - if (updateStatus > 0) { - [self performActionFromUrl:self.launchUrl]; - } - } - else { - self.launchUrl = nil; - } -} - -- (void)keyboardRemoved:(NSNotification *)notification { - if (iosVersion >= minIOSVersion4FontInstall) { - NSDictionary *kbInfo = [[notification userInfo] objectForKey:kKeymanKeyboardInfoKey]; - NSString *profile = [self profileNameWithJSONFont:[kbInfo objectForKey:kKeymanFontKey]]; - if (profile != nil) { - [self.profiles2Install removeObjectsInArray:@[profile]]; - [self.profiles2Install addObject:profile]; - - [self.checkedProfiles removeObjectsInArray:@[profile]]; - NSUserDefaults *userData = [AppDelegate activeUserDefaults]; - [userData setObject:self.checkedProfiles forKey:kKeymanCheckedProfilesKey]; - [userData synchronize]; - } - } -} - -/* -- (void)registeredFontsChanged:(NSNotification *)notification { - NSLog(@"registeredFontsChanged: %@", notification); - NSLog(@"%@", [UIFont familyNames]); -} -*/ - -- (void)infoButtonClick:(id)sender { - [UIView setAnimationDelegate:self]; - SEL animSelector = NSSelectorFromString(@"animationDidStop:finished:context:"); - [UIView setAnimationDidStopSelector:animSelector]; - [UIView beginAnimations:nil context:nil]; - [UIView setAnimationDuration:0.75]; - [UIView setAnimationTransition:((textView.hidden == NO)?UIViewAnimationTransitionFlipFromRight:UIViewAnimationTransitionFlipFromLeft) forView:self.view cache:YES]; - - if (textView.hidden == NO) { - textView.hidden = YES; - infoView.view.hidden = NO; - [infoView reloadKeymanHelp]; - } - else { - textView.hidden = NO; - infoView.view.hidden = YES; - } - - [UIView commitAnimations]; - - if (textView.hidden == NO) { - [[self navigationItem] setTitleView:nil]; - [[self navigationItem] setRightBarButtonItem:nil]; - [self setNavBarButtons]; - if (wasKeyboardVisible) - [self performSelector:@selector(showKeyboard) withObject:nil afterDelay:0.75]; - if ([self shouldShowGetStarted]) - [self performSelector:@selector(showGetStartedView:) withObject:nil afterDelay:0.75]; - } - else { - [self dismissDropDownMenu]; - [popover dismissPopoverAnimated:NO]; - [actionSheet dismissWithClickedButtonIndex:actionSheet.cancelButtonIndex animated:NO]; - wasKeyboardVisible = [textView isFirstResponder]; - [textView dismissKeyboard]; - - [[self navigationItem] setRightBarButtonItems:nil]; - CGFloat vAdjPort = -2.6; - CGFloat vAdjLscpe = -1.6; - if ([UIScreen mainScreen].scale == 2.0) - vAdjPort = -3.6; - - UIBarButtonItem *infoDoneButton = [[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStylePlain target:self action:@selector(infoButtonClick:)]; - [infoDoneButton setBackgroundVerticalPositionAdjustment:vAdjPort forBarMetrics:UIBarMetricsDefault]; - [infoDoneButton setBackgroundVerticalPositionAdjustment:vAdjLscpe forBarMetrics:UIBarMetricsCompact]; - [[self navigationItem] setRightBarButtonItem:infoDoneButton]; - UILabel *versionLabel = [[UILabel alloc] init]; - NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]; - NSString *build = [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleVersionKey]; - [versionLabel setText:[NSString stringWithFormat:@"Version: %@ (build %@)", version, build]]; - [versionLabel setFont:[UIFont systemFontOfSize:9.0]]; - [versionLabel setTextAlignment:NSTextAlignmentRight]; - [versionLabel sizeToFit]; - CGRect frame = versionLabel.frame; - [versionLabel setFrame:CGRectInset(frame, -8, 0)]; - [versionLabel setBackgroundColor:[UIColor clearColor]]; - [[self navigationItem] setTitleView:versionLabel]; - } -} - -- (void)actionButtonClick:(id)sender { - [self dismissDropDownMenu]; - [popover dismissPopoverAnimated:NO]; - [actionSheet dismissWithClickedButtonIndex:actionSheet.cancelButtonIndex animated:NO]; - - if (NSClassFromString(@"UIActivityViewController")) { - NSString *lrm = @"\u200e"; - NSString *rlm = @"\u200f"; - NSMutableString *mText = [textView.text mutableCopy]; - if ([mText rangeOfString:lrm].location == 0 || [mText rangeOfString:rlm].location == 0) { - [mText deleteCharactersInRange:NSMakeRange(0, 1)]; - } - - ActivityItemProvider *activityProvider = [[ActivityItemProvider alloc] - initWithText: mText font: textView.font]; - - UISimpleTextPrintFormatter *printText = [[UISimpleTextPrintFormatter alloc] initWithText:textView.text]; - printText.font = textView.font; - - UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:@[activityProvider, printText] applicationActivities:nil]; - activityVC.excludedActivityTypes = [[NSArray alloc] initWithObjects:UIActivityTypeAssignToContact, UIActivityTypePostToWeibo, UIActivityTypeSaveToCameraRoll, nil]; - #ifdef FREE - [activityVC setCompletionHandler:^(NSString *activityType, BOOL completed) { - [textView setUserInteractionEnabled:YES]; - }]; - #else - [activityVC setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) { - [textView setUserInteractionEnabled:YES]; - }]; - #endif - - [textView setUserInteractionEnabled:NO]; - if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { - [self presentViewController:activityVC animated:YES completion:nil]; - } - else { - popover = [[UIPopoverController alloc] initWithContentViewController:activityVC]; - [popover presentPopoverFromBarButtonItem:[[self.navigationItem rightBarButtonItems] objectAtIndex:8] permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; - } - } -} - -- (void)textSizeButtonClick:(id)sender { - [self dismissDropDownMenu]; - [popover dismissPopoverAnimated:NO]; - [actionSheet dismissWithClickedButtonIndex:actionSheet.cancelButtonIndex animated:NO]; - - if (iosVersion >= 8) { - UIViewController *textSizeVC = [[UIViewController alloc] init]; - textSizeVC.modalPresentationStyle = UIModalPresentationOverCurrentContext; - [textSizeVC.view setBackgroundColor:[UIColor clearColor]]; - - CGRect mframe = textSizeVC.view.frame; - CGFloat w = 304; - CGFloat h = 142; - CGRect sframe = CGRectMake(mframe.size.width/2.0 - w/2.0, mframe.size.height, w, h); - CGRect eframe = sframe; - eframe.origin.y -= h + 8; - UIView *containerView = [[UIView alloc] initWithFrame:sframe]; - containerView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin; - [containerView setTag:1]; - [containerView setBackgroundColor:[UIColor whiteColor]]; - [containerView.layer setCornerRadius:4.0]; - - UIButton *doneButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; - [doneButton addTarget:self action:@selector(dismissTextSizeVC:) - - forControlEvents:UIControlEventTouchUpInside]; - [doneButton setBackgroundColor:[UIColor whiteColor]]; - [doneButton setTitle:@"Done" forState:UIControlStateNormal]; - [doneButton.titleLabel setFont:[doneButton.titleLabel.font fontWithSize:21.0]]; - [doneButton setTitleColor:[UIColor colorWithRed:0.0 green:0.5 blue:1.0 alpha:1.0] forState:UIControlStateNormal]; - [doneButton.layer setCornerRadius:4.0]; - CGRect bframe = CGRectMake(0, h - 42, sframe.size.width, 42); - [doneButton setFrame:bframe]; - [containerView addSubview:doneButton]; - - CGRect lframe = CGRectMake(0, bframe.origin.y - 1, sframe.size.width, 1); - UIView *borderLine = [[UIView alloc] initWithFrame:lframe]; - CGFloat c = 230.0/255.0; - [borderLine setBackgroundColor:[UIColor colorWithRed:c green:c blue:c alpha:1.0]]; - [containerView addSubview:borderLine]; - - CGRect tframe = CGRectMake(0, 0, bframe.size.width, 40); - UILabel *textSizeTitle = [[UILabel alloc] initWithFrame:tframe]; - [textSizeTitle setTag:2]; - [textSizeTitle setBackgroundColor:[UIColor clearColor]]; - [textSizeTitle setText:[NSString stringWithFormat:@"Text size: %d", (int)textSize]]; - [textSizeTitle setTextAlignment:NSTextAlignmentCenter]; - [textSizeTitle setTextColor:[UIColor grayColor]]; - [textSizeTitle setFont:[textSizeTitle.font fontWithSize:14.0]]; - [containerView addSubview:textSizeTitle]; - - CGRect sliderFrame = CGRectMake(12, 45, bframe.size.width - 24, 50); - [textSizeController setFrame:sliderFrame]; - [containerView addSubview:textSizeController]; - - [textSizeVC.view addSubview:containerView]; - if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { - wasKeyboardVisible = [textView isFirstResponder]; - [textView dismissKeyboard]; - [self presentViewController:textSizeVC animated:NO completion:^{ - [UIView animateWithDuration:0.25 - delay:0.0 - options:UIViewAnimationOptionCurveEaseIn - animations:^{ - [containerView setFrame:eframe]; - [textSizeVC.view setBackgroundColor:[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.4]]; - } - completion:nil - ]; - }]; - } - else { - UIViewController *vc = [[UIViewController alloc] init]; - [vc setModalPresentationStyle:UIModalPresentationPopover]; - [vc.popoverPresentationController setBarButtonItem:[[self.navigationItem rightBarButtonItems] objectAtIndex:6]]; - [vc setView:containerView]; - [vc setPreferredContentSize:containerView.frame.size]; - [self presentViewController:vc animated:YES completion:nil]; - } - } - else { - wasKeyboardVisible = [textView isFirstResponder]; - [textView dismissKeyboard]; - actionSheet = [[UIActionSheet alloc] initWithTitle:@"" delegate:self - cancelButtonTitle:@"Done" - destructiveButtonTitle:nil - otherButtonTitles:@"", nil]; - [actionSheet setTitle:[NSString stringWithFormat:@"Text size: %d", (int)textSize]]; - [actionSheet addSubview:textSizeController]; - [actionSheet setTag:0]; - [actionSheet showFromBarButtonItem:[[self.navigationItem rightBarButtonItems] objectAtIndex:6] animated:YES]; - } -} - -- (void)dismissTextSizeVC:(id)sender { - UIViewController *presentedVC = [self presentedViewController]; - if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { - UIView *containerView = [presentedVC.view viewWithTag:1]; - CGRect sframe = containerView.frame; - CGRect eframe = sframe; - eframe.origin.y += sframe.size.height + 8; - [UIView animateWithDuration:0.25 - delay:0.0 - options:UIViewAnimationOptionCurveEaseIn - animations:^{ - [containerView setFrame:eframe]; - [presentedVC.view setBackgroundColor:[UIColor clearColor]]; - } - completion:^(BOOL finished){ - [presentedVC dismissViewControllerAnimated:NO completion:^{ - if (wasKeyboardVisible) - [textView becomeFirstResponder]; - }]; - } - ]; - } - else - [presentedVC dismissViewControllerAnimated:YES completion:nil]; -} - -- (void)trashButtonClick:(id)sender { - [self dismissDropDownMenu]; - if ([UIAlertController class]) { - UIAlertController *alert = [UIAlertController - alertControllerWithTitle:nil - message:nil - preferredStyle:UIAlertControllerStyleActionSheet]; - - UIAlertAction *clear = [UIAlertAction - actionWithTitle:@"Clear Text" - style:UIAlertActionStyleDestructive - handler:^(UIAlertAction *action) { - [textView setText:@""]; - }]; - UIAlertAction *cancel = [UIAlertAction - actionWithTitle:@"Cancel" - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - }]; - [alert addAction:clear]; - [alert addAction:cancel]; - [alert.popoverPresentationController setBarButtonItem:[[self.navigationItem rightBarButtonItems] objectAtIndex:4]]; - [self presentViewController:alert animated:YES completion:nil]; - } - else { - [popover dismissPopoverAnimated:NO]; - [actionSheet dismissWithClickedButtonIndex:actionSheet.cancelButtonIndex animated:NO]; - wasKeyboardVisible = [textView isFirstResponder]; - [textView dismissKeyboard]; - actionSheet = [[UIActionSheet alloc] initWithTitle:nil - delegate:self - cancelButtonTitle:@"Cancel" - destructiveButtonTitle:@"Clear Text" - otherButtonTitles:nil]; - [actionSheet setTag:1]; - [actionSheet showFromBarButtonItem:[[self.navigationItem rightBarButtonItems] objectAtIndex:4] animated:YES]; - } -} - -- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { - if (wasKeyboardVisible) - [textView becomeFirstResponder]; - - if (self.actionSheet.tag == 0) { - - } - else if (self.actionSheet.tag == 1) { - if (buttonIndex == 0) { - [textView setText:@""]; - } - } -} - -- (void)willPresentActionSheet:(UIActionSheet *)actionSheet { - if (self.actionSheet.tag == 0) { - UIButton *button = nil; - CGRect sliderFrame = CGRectZero; - for (UIView *view in self.actionSheet.subviews) - { - if ([view isKindOfClass:[UIButton class]]) - { - button = (UIButton *)view; - button.enabled = NO; - button.hidden = YES; - sliderFrame = button.frame; - break; - } - } - - sliderFrame.origin.x += 12; - sliderFrame.size.width -= 24; - [textSizeController setFrame:sliderFrame]; - } -} - -- (UIWindow *)overlayWindow { - return [self appDelegate].overlayWindow; -} - -- (void)showActivityIndicator { - if (![self.overlayWindow isHidden] && [self.overlayWindow viewWithTag:activityIndicatorViewTag] != nil) - return; - - [self dismissGetStartedView:nil]; - [self.overlayWindow setHidden:NO]; - UIActivityIndicatorView *activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; - UIView *containerView = [[UIView alloc] initWithFrame:CGRectInset(activityView.bounds, -10.0f, -10.0f)]; - containerView.backgroundColor = [UIColor colorWithWhite:0.5f alpha:0.8f]; - containerView.layer.cornerRadius = 6.0f; - containerView.center = self.overlayWindow.center; - containerView.tag = activityIndicatorViewTag; - containerView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; - - activityView.center = CGPointMake(containerView.bounds.size.width*0.5f, containerView.bounds.size.height*0.5f); - [activityView startAnimating]; - - [containerView addSubview:activityView]; - [self.overlayWindow addSubview:containerView]; -} - -- (void)dismissActivityIndicator { - [[self.overlayWindow viewWithTag:activityIndicatorViewTag] removeFromSuperview]; - [self.overlayWindow setHidden:YES]; -} - -- (void)sliderValueChanged:(UISlider *)sender { - textSize = roundf(sender.value + minTextSize); - textView.font = [textView.font fontWithSize:textSize]; - if (iosVersion >= 8) { - UILabel *textSizeTitle = (UILabel *)[[self presentedViewController].view viewWithTag:2]; - [textSizeTitle setText:[NSString stringWithFormat:@"Text size: %d", (int)textSize]]; - } - else - [actionSheet setTitle:[NSString stringWithFormat:@"Text size: %d", (int)textSize]]; -} -/* -- (UIImage *)resizeImage:(UIImage *)image scaledToSize:(CGSize)newSize { - UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0); - [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)]; - UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - return newImage; -}*/ - -- (CGRect)rectForBarButtonItem:(UIBarButtonItem *)barButtonItem { - UIView *view = [barButtonItem valueForKey:@"view"]; - return view?view.frame:CGRectZero; -} - -- (void)startTimer { - if (loadTimer == nil) { - loadTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f - target:self - selector:@selector(timerAction) - userInfo:nil - repeats:YES]; - } -} - -- (void)stopTimer { - if (loadTimer != nil) { - [loadTimer invalidate]; - loadTimer = nil; - } -} - -- (void)timerAction { - if (updateStatus == 1) { - [self stopTimer]; - - /* - if (iosVersion >= minIOSVersion4FontInstall) { - [self createProfileNamesByKeyboardID]; - }*/ - - [self dismissActivityIndicator]; - - [textView setEditable:YES]; - [textView becomeFirstResponder]; - - if (self.launchUrl != nil) { - [self performActionFromUrl:self.launchUrl]; - } - else { - [self loadSavedUserText]; - if ([self shouldShowGetStarted]) - [self performSelector:@selector(showGetStartedView:) withObject:nil afterDelay:1.0]; - } - } - else if (updateStatus == -1) { - [self stopTimer]; - [self dismissActivityIndicator]; - [self loadSavedUserText]; - [textView setEditable:YES]; - [textView becomeFirstResponder]; - if ([self shouldShowGetStarted]) - [self performSelector:@selector(showGetStartedView:) withObject:nil afterDelay:1.0]; - } -} - -- (void)resetTextViewCursor { - [textView setSelectedRange:NSMakeRange(0,0)]; - CGFloat size = textView.font.pointSize; - [textView setFont:[[textView font] fontWithSize:(size+5)]]; - [textView setFont:[[textView font] fontWithSize:(size-5)]]; -} - -- (void)loadSavedUserText { - NSUserDefaults *userData = [AppDelegate activeUserDefaults]; - NSString *userText = [userData objectForKey:kKeymanUserTextKey]; - NSString *userTextSize = [userData objectForKey:kKeymanUserTextSizeKey]; - - if (userText != nil && [userText length] > 0) - [textView setText:userText]; - - if (userTextSize != nil && [userTextSize length] > 0) { - textSize = [userTextSize floatValue]; - textView.font = [textView.font fontWithSize:textSize]; - [textSizeController setValue:(textSize - minTextSize)]; - } -} - -- (void)performActionFromUrl:(NSURL *)url { - if ([url query] == nil) { - self.launchUrl = nil; - return; - } - - NSString *langID = nil; - NSString *kbID = nil; - NSURL *jsonUrl = nil; - NSString *direct = nil; - if ([[url lastPathComponent] isEqualToString:@"open"]) { - NSString *query = [url query]; - if ([query rangeOfString:@"url="].location != NSNotFound) { - // Download and set custom keyboard - NSArray *params = [[url query] componentsSeparatedByString:@"&"]; - if ([params count] >= 1) { - for (NSString *value in params) { - NSUInteger index = -1; - if ((index = [value rangeOfString:@"url="].location) != NSNotFound) { - jsonUrl = [NSURL URLWithString:[NSString stringWithString:[value substringFromIndex:index+4]]]; - } - else if ((index = [value rangeOfString:@"direct="].location) != NSNotFound) { - direct = [NSString stringWithString:[value substringFromIndex:index+7]]; - } - else if ((index = [value rangeOfString:@"keyboard="].location) != NSNotFound) { - kbID = [NSString stringWithString:[value substringFromIndex:index+9]]; - } - else if ((index = [value rangeOfString:@"language="].location) != NSNotFound) { - langID = [NSString stringWithString:[value substringFromIndex:index+9]]; - } - } - } - - /* Disabled to allow re-download of custom keyboards - if (kbID != nil && langID != nil) { - NSInteger index = [[KMManager sharedInstance] indexForUserKeyboardWithID:kbID languageID:langID]; - if (index >= 0) { - NSUserDefaults *userData = [AppDelegate activeUserDefaults]; - NSArray *userKeyboards = [userData arrayForKey:kKeymanUserKeyboardsListKey]; - NSDictionary *kbInfo = [userKeyboards objectAtIndex:index]; - NSString *kbName = [kbInfo objectForKey:kKeymanKeyboardNameKey]; - NSString *langName = [kbInfo objectForKey:kKeymanLanguageNameKey]; - NSString *font = [kbInfo objectForKey:kKeymanFontKey]; - [[KMManager sharedInstance] setKeyboardWithID:kbID languageID:langID keyboardName:kbName languageName:langName font:font]; - self.launchUrl = nil; - return; - } - }*/ - - if (jsonUrl != nil && [[jsonUrl absoluteString] length]) { - [[KMManager sharedInstance] dismissKeyboardPicker:self]; - if (![self.infoView.view isHidden]) - [self performSelector:@selector(infoButtonClick:) withObject:nil]; - - BOOL isDirect = NO; - if (direct != nil && [direct isEqualToString:@"true"]) - isDirect = YES; - - self.customKeyboard2Download = [NSDictionary dictionaryWithObjectsAndKeys: - jsonUrl, @"url", - [NSNumber numberWithBool:isDirect], @"direct", - nil]; - NSString *title = [NSString stringWithFormat:@"Custom Keyboard: %@", [jsonUrl lastPathComponent]]; - [self showAlertWithTitle:title - message:@"Would you like to install this keyboard?" - cancelButtonTitle:@"Cancel" - otherButtonTitles:@"Install" - tag:2]; - } - else { - [self showAlertWithTitle:@"Custom Keyboard" - message:@"The keyboard could not be installed: Invalid Url" - cancelButtonTitle:@"OK" - otherButtonTitles:nil - tag:-1]; - - self.launchUrl = nil; - } - } - else { - // Query should include keyboard and language IDs to set the keyboard (first download if not available) - NSArray *params = [[url query] componentsSeparatedByString:@"&"]; - if ([params count] >= 2) { - for (NSString *value in params) { - NSUInteger index = -1; - if ((index = [value rangeOfString:@"keyboard="].location) != NSNotFound) { - kbID = [NSString stringWithString:[value substringFromIndex:index+9]]; - } - else if ((index = [value rangeOfString:@"language="].location) != NSNotFound) { - langID = [NSString stringWithString:[value substringFromIndex:index+9]]; - } - } - } - - if (kbID != nil && langID != nil) { - NSString *dictKey = [NSString stringWithFormat:@"%@_%@", langID, kbID]; - NSDictionary *kbDict = [[[KMManager sharedInstance] keyboardsDictionary] objectForKey:dictKey]; - NSString *kbName = [kbDict objectForKey:kKeymanKeyboardNameKey]; - NSString *langName = [kbDict objectForKey:kKeymanLanguageNameKey]; - BOOL isRTL = [[kbDict objectForKey:kKeymanKeyboardRTLKey] isEqualToString:@"Y"]?YES:NO; - NSString *font = [kbDict objectForKey:kKeymanFontKey]; - NSString *oskFont = [kbDict objectForKey:kKeymanOskFontKey]; - - if ([[KMManager sharedInstance] stateForKeyboardWithID:kbID] == kKMKeyboardStateNeedsDownload) { - self.keyboard2Download = [NSDictionary dictionaryWithObjectsAndKeys: - langID, kKeymanLanguageIdKey, - kbID, kKeymanKeyboardIdKey, - nil]; - [self showAlertWithTitle:[NSString stringWithFormat:@"%@: %@", langName, kbName] - message:@"Would you like to install this keyboard?" - cancelButtonTitle:@"Cancel" - otherButtonTitles:@"Install" - tag:0]; - } - else { - [[KMManager sharedInstance] addKeyboardWithID:kbID languageID:langID keyboardName:kbName languageName:langName isRTL:isRTL isCustom:NO font:font oskFont:oskFont]; - [[KMManager sharedInstance] setKeyboardWithID:kbID languageID:langID keyboardName:kbName languageName:langName font:font oskFont:oskFont]; - } - } - else { - self.launchUrl = nil; - } - } - } -} - -/* -- (void)createProfileNamesByKeyboardID { - NSUserDefaults *userData = [AppDelegate activeUserDefaults]; - NSArray *userKeyboards = [userData arrayForKey:kKeymanUserKeyboardsListKey]; - NSLog(@"userKeyboards = %@", userKeyboards); - - NSArray *languages = [[KMManager sharedInstance] languages]; - self.profileNamesByKeyboardID = [NSMutableDictionary dictionaryWithCapacity:0]; - - for (NSDictionary *langDict in languages) { - NSArray *keyboards = [langDict objectForKey:kKeymanLanguageKeyboardsKey]; - for (NSDictionary *kbDict in keyboards) { - NSString *kbID = [kbDict objectForKey:kKeymanIdKey]; - NSDictionary *fontDict = [kbDict objectForKey:kKeymanFontKey]; - if (fontDict != nil && [self.profileNamesByKeyboardID objectForKey:kbID] == nil) { - id fontSource = [fontDict objectForKey:kKeymanFontSourceKey]; - if ([fontSource isKindOfClass:[NSArray class]]) { - for (NSString *value in fontSource) { - if ([value rangeOfString:@".mobileconfig"].location != NSNotFound) { - [self.profileNamesByKeyboardID setObject:[NSDictionary dictionaryWithObjectsAndKeys:value, kKeymanProfileKey, nil] forKey:kbID]; - break; - } - } - } - } - } - } -}*/ - -- (NSDictionary *)dictionaryWithKeyboardID:(NSString *)kbID languageID:(NSString *)langID { - NSInteger index = [[KMManager sharedInstance] indexForUserKeyboardWithID:kbID - languageID:langID]; - if (index < 0) - return nil; - - NSArray *userKeyboards = [[AppDelegate activeUserDefaults] arrayForKey:kKeymanUserKeyboardsListKey]; - return [userKeyboards objectAtIndex:index]; -} - -- (NSString *)profileNameWithKeyboardID:(NSString *)kbID languageID:(NSString *)langID { - NSDictionary *kbDict = [self dictionaryWithKeyboardID:kbID languageID:langID]; - if (kbDict == nil) - return nil; - - return [self profileNameWithJSONFont:[kbDict objectForKey:kKeymanFontKey]]; -} - -- (NSString *)profileNameWithJSONFont:(NSString *)jsonFont { - NSDictionary *fontDict = [NSJSONSerialization JSONObjectWithData:[jsonFont dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:nil]; - if (fontDict == nil) - return nil; - - id fontSource = [fontDict objectForKey:kKeymanFontFilesKey]; - NSString *profile = nil; - if ([fontSource isKindOfClass:[NSArray class]]) { - for (NSString *value in fontSource) { - if ([value rangeOfString:@".mobileconfig"].location != NSNotFound) { - profile = [NSString stringWithString:value]; - } - } - } - - return profile; -} - -- (void)checkProfileForKeyboardID:(NSString *)kbID languageID:(NSString *)langID doListCheck:(BOOL)doListCheck { - if ([kbID isEqualToString:kKeymanDefaultKeyboardID] && [langID isEqualToString:kKeymanDefaultLanguageID]) - return; - - NSString *profile = [self profileNameWithKeyboardID:kbID languageID:langID]; - if (profile != nil) { - BOOL doInstall = NO; - if (doListCheck) { - NSMutableArray *checkList = [NSMutableArray arrayWithArray:self.profiles2Install]; - [checkList removeObjectsInArray:self.checkedProfiles]; - for (NSString *value in checkList) { - if ([profile isEqualToString:value]) { - doInstall = YES; - break; - } - } - } - else { - doInstall = YES; - } - - if (doInstall) { - profileName = profile; - NSDictionary *kbDict = [self dictionaryWithKeyboardID:kbID languageID:langID]; - NSString *languageName = [kbDict objectForKey:kKeymanLanguageNameKey]; - NSString *title = [NSString stringWithFormat:@"%@ Font", languageName]; - NSString *msg = [NSString stringWithFormat:@"Touch Install to make %@ display correctly in all your apps", languageName]; - [self showAlertWithTitle:title - message:msg - cancelButtonTitle:@"Cancel" - otherButtonTitles:@"Install" - tag:1]; - } - else { - profileName = nil; - } - } -} - -- (void)showKeyboard { - [textView becomeFirstResponder]; -} - -- (void)showAlertWithTitle:(NSString *)title message:(NSString *)msg cancelButtonTitle:(NSString *)cbTitle otherButtonTitles:(NSString *)obTitles tag:(NSInteger)tag { - /* - if ([UIAlertController class]) { - UIAlertController *alert = [UIAlertController alertControllerWithTitle:title - message:msg - preferredStyle:UIAlertControllerStyleAlert]; - - //UIAlertController* __weak weakAlert = alert; - UIAlertAction *cancel = [UIAlertAction actionWithTitle:cbTitle - style:UIAlertActionStyleCancel - handler:^(UIAlertAction *action) { - if (profileName != nil && tag == 1) { - [checkedProfiles addObject:profileName]; - NSUserDefaults *userData = [AppDelegate activeUserDefaults]; - [userData setObject:self.checkedProfiles forKey:kKeymanCheckedProfilesKey]; - [userData synchronize]; - profileName = nil; - } - }]; - [alert addAction:cancel]; - - if (obTitles != nil) { - UIAlertAction *other = [UIAlertAction actionWithTitle:obTitles - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - [self performAlertButtonClickWithTag:tag]; - }]; - [alert addAction:other]; - } - - UIViewController *vc = [self.navigationController presentedViewController]; - [vc presentViewController:alert animated:YES completion:nil]; - } - else { - UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title - message:msg - delegate:self - cancelButtonTitle:cbTitle - otherButtonTitles:obTitles, nil]; - alertView.tag = tag; - [alertView show]; - } - */ - - [self dismissGetStartedView:nil]; - UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title - message:msg - delegate:self - cancelButtonTitle:cbTitle - otherButtonTitles:obTitles, nil]; - alertView.tag = tag; - [alertView show]; -} - -- (void)performAlertButtonClickWithTag:(NSInteger)tag { - if (tag == 0) { - NSString *langID = [self.keyboard2Download objectForKey:kKeymanLanguageIdKey]; - NSString *kbID = [self.keyboard2Download objectForKey:kKeymanKeyboardIdKey]; - [[KMManager sharedInstance] downloadKeyboardWithID:kbID languageID:langID isUpdate:NO]; - } - else if (tag == 1) { - if (profileName != nil) { - [checkedProfiles addObject:profileName]; - NSUserDefaults *userData = [AppDelegate activeUserDefaults]; - [userData setObject:self.checkedProfiles forKey:kKeymanCheckedProfilesKey]; - [userData synchronize]; - - [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@%@", baseUri, profileName]]]; - - profileName = nil; - } - } - else if (tag == 2) { - NSURL *jsonUrl = [self.customKeyboard2Download objectForKey:@"url"]; - BOOL isDirect = [[self.customKeyboard2Download objectForKey:@"direct"] boolValue]; - [[KMManager sharedInstance] downloadKeyboardFromUrl:jsonUrl isDirect:isDirect]; - } -} - -#pragma mark - Alert view delegate - -- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { - if (buttonIndex == alertView.cancelButtonIndex) { - if (profileName != nil && alertView.tag == 1) { - [checkedProfiles addObject:profileName]; - NSUserDefaults *userData = [AppDelegate activeUserDefaults]; - [userData setObject:self.checkedProfiles forKey:kKeymanCheckedProfilesKey]; - [userData synchronize]; - profileName = nil; - } - - if (alertView.tag == 2) { - if ([self shouldShowGetStarted]) - [self showGetStartedView:nil]; - } - - return; - } - - [self performAlertButtonClickWithTag:alertView.tag]; -} - -#pragma mark - DropDown menu methods - -- (void)showDropDownMenu:(id)sender { - if ([self dismissDropDownMenu]) - return; - - wasKeyboardVisible = [textView isFirstResponder]; - [textView dismissKeyboard]; - - CGFloat w = 160; - CGFloat h = 44; - CGFloat x = [self navBarWidth] - w; - CGFloat y = [AppDelegate statusBarHeight] + [self navBarHeight]; - #ifdef FREE - NSArray *listItems = [NSArray arrayWithObjects:trashButton, getStartedButton, infoButton, nil]; - #else - NSArray *listItems = [NSArray arrayWithObjects:textSizeButton, trashButton, getStartedButton, infoButton, nil]; - #endif - DropDownListView *dropDownList = [[DropDownListView alloc] initWithListItems:listItems - itemSize:CGSizeMake(w, h) - position:CGPointMake(x, y)]; - dropDownList.tag = dropDownListTag; - [self.navigationController.view addSubview:dropDownList]; - //[self.view addSubview:dropDownList]; -} - -- (BOOL)dismissDropDownMenu { - UIView *view = [self.navigationController.view viewWithTag:dropDownListTag]; - if (view != nil && wasKeyboardVisible) - [textView becomeFirstResponder]; - - [view removeFromSuperview]; - return (view == nil)?NO:YES; -} - -#pragma mark - Get started view methods - -- (void)showGetStartedView:(id)sender { - if (![self.overlayWindow isHidden] && [self.overlayWindow viewWithTag:getStartedViewTag] != nil) - return; - - [popover dismissPopoverAnimated:NO]; - [self dismissDropDownMenu]; - [self.overlayWindow setHidden:NO]; - self.getStartedVC = [[GetStartedViewController alloc] init]; - [self.getStartedVC setMainViewController:self]; - UIView *containerView = self.getStartedVC.view; - UINavigationBar *navBar = (UINavigationBar *)[containerView viewWithTag:786586]; - UIBarButtonItem *doneButton = navBar.topItem.rightBarButtonItem; - [doneButton setTarget:self]; - [doneButton setAction:@selector(dismissGetStartedView:)]; - - containerView.frame = CGRectInset(containerView.frame, 15, 140); - containerView.backgroundColor = [UIColor whiteColor]; - containerView.layer.cornerRadius = 6.0f; - containerView.center = self.overlayWindow.center; - containerView.tag = getStartedViewTag; - containerView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; - UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; - [containerView setTransform:[self transformForOrientation:orientation]]; - [self.overlayWindow addSubview:containerView]; -} - -- (void)dismissGetStartedView:(id)sender { - [[self.overlayWindow viewWithTag:getStartedViewTag] removeFromSuperview]; - [self.overlayWindow setHidden:YES]; -} - -- (BOOL)shouldShowGetStarted { - // Do not display "Get started" when MainView is not visible - if (self.navigationController.visibleViewController != self) - return NO; - - BOOL shouldShowGetStarted = NO; - NSUserDefaults *userData = [AppDelegate activeUserDefaults]; - BOOL dontShowGetStarted = [userData boolForKey:kKeymanDontShowGetStartedKey]; - if (!dontShowGetStarted) { - NSArray *userKbs = [userData objectForKey:kKeymanUserKeyboardsListKey]; - if ([userKbs count] < 2) { - NSDictionary *firstKB = [userKbs objectAtIndex:0]; - if (firstKB != nil) { - NSString *kbID = [firstKB objectForKey:kKeymanKeyboardIdKey]; - NSString *langID = [firstKB objectForKey:kKeymanLanguageIdKey]; - if ([kbID isEqualToString:kKeymanDefaultKeyboardID] && [langID isEqualToString:langID]) - shouldShowGetStarted = YES; - else - shouldShowGetStarted = NO; - } - else - shouldShowGetStarted = YES; - } - - if (iosVersion >= 8) { - if (![AppDelegate isKeymanEnabledSystemWide]) - shouldShowGetStarted = YES; - } - } - - return shouldShowGetStarted; -} - - -#ifndef FREE -#pragma mark - KMWebBrowser view methods - -- (void)showKMWebBrowserView:(id)sender { - [popover dismissPopoverAnimated:NO]; - [self dismissDropDownMenu]; - WebBrowserViewController *webBrowserVC = [[WebBrowserViewController alloc] init]; - [webBrowserVC setFontFamily:[[textView font] fontName]]; - [self presentViewController:webBrowserVC animated:YES completion:nil]; -} -#endif - -#define DegreesToRadians(degrees) (degrees * M_PI / 180) - -- (CGAffineTransform)transformForOrientation:(UIInterfaceOrientation)orientation { - if (iosVersion >= 8) - return CGAffineTransformIdentity; - - switch (orientation) { - case UIInterfaceOrientationLandscapeLeft: - return CGAffineTransformMakeRotation(-DegreesToRadians(90)); - case UIInterfaceOrientationLandscapeRight: - return CGAffineTransformMakeRotation(DegreesToRadians(90)); - case UIInterfaceOrientationPortraitUpsideDown: - return CGAffineTransformMakeRotation(DegreesToRadians(180)); - case UIInterfaceOrientationPortrait: - default: - return CGAffineTransformMakeRotation(DegreesToRadians(0)); - } -} - -/* -- (UIImage *)navBarBgImage { - CGRect rect = CGRectMake(0, 0, 320, 44); - UIGraphicsBeginImageContext(rect.size); - CGContextRef context = UIGraphicsGetCurrentContext(); - UIGraphicsPushContext(context); - CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor); - CGContextFillRect(context, rect); - UIImage *bgImg = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsPopContext(); - UIGraphicsEndImageContext(); - return bgImg; -}*/ - -@end diff --git a/ios/keyman/Keyman/Keyman/MainViewController.swift b/ios/keyman/Keyman/Keyman/MainViewController.swift new file mode 100644 index 0000000000..ec4644b545 --- /dev/null +++ b/ios/keyman/Keyman/Keyman/MainViewController.swift @@ -0,0 +1,1200 @@ +// +// MainViewController.swift +// Keyman +// +// Created by Gabriel Wong on 2017-09-07. +// Copyright © 2017 SIL International. All rights reserved. +// + +import UIKit +import QuartzCore + +// Internal strings +private let baseUri = "http://r.keymanweb.com/20/fonts/get_mobileconfig.php?id=" +private let profileKey = "profile" +private let checkedProfilesKey = "CheckedProfiles" + +// External strings +let userTextKey = "UserText" +let userTextSizeKey = "UserTextSize" +let dontShowGetStartedKey = "DontShowGetStarted" +let launchedFromUrlNotification = NSNotification.Name("LaunchedFromUrlNotification") +let urlKey = "url" + +class MainViewController: UIViewController, KMTextViewDelegate, UIActionSheetDelegate, UIAlertViewDelegate { + private let minTextSize: CGFloat = 9.0 + private let maxTextSize: CGFloat = 72.0 + private let getStartedViewTag = 7183 + private let activityIndicatorViewTag = 6573 + private let dropDownListTag = 686876 + + var textView: KMTextView! + var textSize: CGFloat = 0.0 + + var getStartedVC: GetStartedViewController! + var infoView: InfoViewController! + var popover: UIPopoverController? + var textSizeController: UISlider! + var dropdownItems: [UIBarButtonItem]! + + var profiles2Install = [String]() + var checkedProfiles = [String]() + var profileName: String? + var launchUrl: URL? + var keyboard2Download = [String: Any]() + var customKeyboard2Download = [String: Any]() + var wasKeyboardVisible: Bool = false + + var iosVersion: Int = 0 + var screenWidth: CGFloat = 0.0 + var screenHeight: CGFloat = 0.0 + var portLeftMargin: CGFloat = 0.0 + var portRightMargin: CGFloat = 0.0 + var lscpeLeftMargin: CGFloat = 0.0 + var lscpeRightMargin: CGFloat = 0.0 + var loadTimer: Timer? + var updateStatus: Int = 0 // TODO: Make into enum + var didDownload: Bool = false + + var appDelegate: AppDelegate! { + return UIApplication.shared.delegate as? AppDelegate + } + + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + + // Setup Notifications + NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), + name: NSNotification.Name.UIKeyboardWillShow, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardDidShow), + name: NSNotification.Name.UIKeyboardWillShow, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), + name: NSNotification.Name.UIKeyboardWillHide, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardLoaded), + name: NSNotification.Name.keymanKeyboardLoaded, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardChanged), + name: NSNotification.Name.keymanKeyboardChanged, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.languagesUpdated), + name: NSNotification.Name.keymanLanguagesUpdated, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.languagesDownloadFailed), + name: NSNotification.Name.keymanLanguagesDownloadFailed, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardPickerDismissed), + name: NSNotification.Name.keymanKeyboardPickerDismissed, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardDownloadStarted), + name: NSNotification.Name.keymanKeyboardDownloadStarted, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardDownloadFinished), + name: NSNotification.Name.keymanKeyboardDownloadCompleted, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardDownloadFailed), + name: NSNotification.Name.keymanKeyboardDownloadFailed, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardRemoved), + name: NSNotification.Name.keymanKeyboardRemoved, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.launched), + name: launchedFromUrlNotification, object: nil) + + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + extendedLayoutIncludesOpaqueBars = true + automaticallyAdjustsScrollViewInsets = false + + iosVersion = (UIDevice.current.systemVersion as NSString).integerValue + let systemFonts = Set(UIFont.familyNames) + let screenRect = UIScreen.main.bounds + let orientation = UIApplication.shared.statusBarOrientation + screenWidth = screenRect.size.width + screenHeight = screenRect.size.height + + // Setup Keyman Manager & fetch keyboards list + updateStatus = 0 + KMManager.setApplicationGroupIdentifier("group.KM4I") + + #if DEBUG + KMManager.sharedInstance().debugPrintingOn = true + #endif + + KMManager.sharedInstance().canRemoveDefaultKeyboard = true + KMManager.sharedInstance().fetchKeyboardsList() + + let bgColor = UIColor(red: 1.0, green: 1.0, blue: 207.0 / 255.0, alpha: 1.0) + view?.backgroundColor = bgColor + + // Check for configuration profiles/fonts to install + let kmFonts = KMManager.sharedInstance().keymanFonts as! [String: [String: Any]] + var profilesByFontName = [String: String](minimumCapacity: kmFonts.count - 1) + for (key, value) in kmFonts where key != "keymanweb-osk.ttf" { + let fontName = value[kKeymanFontNameKey] as! String + let type = key.substring(from: key.range(of: ".", options: .backwards)!.lowerBound) + profilesByFontName[fontName] = key.replacingOccurrences(of: type, with: ".mobileconfig") + } + + let customFonts = UIFont.familyNames.filter { !systemFonts.contains($0) && !($0 == "KeymanwebOsk") } + + let userData = AppDelegate.activeUserDefaults() + checkedProfiles = userData.object(forKey: checkedProfilesKey) as? [String] ?? [String]() + + profiles2Install = [String]() + for name in customFonts { + if let profile = profilesByFontName[UIFont.fontNames(forFamilyName: name)[0]] { + profiles2Install.append(profile) + } + } + + // Setup NavigationBar + // TODO: refactor + if UIDevice.current.userInterfaceIdiom == .phone { + let size = CGFloat.maximum(screenRect.width, screenRect.height) + if size > 568.0 { + // Navbar for iPhone 6 & 6 Plus + let image: UIImage + if UIInterfaceOrientationIsPortrait(orientation) { + image = #imageLiteral(resourceName: "navbar-Portrait.png") + } else { + image = #imageLiteral(resourceName: "navbar-Landscape-568h.png") + } + let bgImg = image.resizableImage(withCapInsets: + UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0), resizingMode: .stretch) + navigationController?.navigationBar.setBackgroundImage(bgImg, for: UIBarMetrics.default) + } else if size == 568 { + // Navbar for iPhone and iPod Touch with 4" Display + navigationController?.navigationBar.setBackgroundImage(#imageLiteral(resourceName: "navbar-Portrait.png"), + for: UIBarMetrics.default) + navigationController?.navigationBar.setBackgroundImage(#imageLiteral(resourceName: "navbar-Landscape-568h.png"), + for: UIBarMetrics.compact) + } else if size < 568.0 { + // Navbar for iPhone and iPod Touch with 3.5" Display + navigationController?.navigationBar.setBackgroundImage(#imageLiteral(resourceName: "navbar-Portrait.png"), + for: UIBarMetrics.default) + navigationController?.navigationBar.setBackgroundImage(#imageLiteral(resourceName: "navbar-Landscape.png"), + for: UIBarMetrics.compact) + } + } else { + // Navbar for iPad + if UIInterfaceOrientationIsPortrait(orientation) { + navigationController?.navigationBar.setBackgroundImage( + UIImage(named: "navbar-Portrait.png"), for: UIBarMetrics.default) + } else { + navigationController?.navigationBar.setBackgroundImage( + UIImage(named: "navbar-Landscape.png"), for: UIBarMetrics.default) + } + } + + // Setup Keyman TextView + textSize = 16.0 + textView = KMTextView(frame: screenRect) + textView.setKeymanDelegate(self) + textView.viewController = self + textView.backgroundColor = bgColor + textView.isScrollEnabled = true + textView.isUserInteractionEnabled = true + textView.font = textView.font?.withSize(textSize) + view?.addSubview(textView!) + textView.isEditable = false + + // Setup Info View + if UIDevice.current.userInterfaceIdiom == .phone { + infoView = InfoViewController(nibName: "InfoViewController_iPhone", bundle: nil) + } else { + infoView = InfoViewController(nibName: "InfoViewController_iPad", bundle: nil) + textSize *= 2 + textView.font = textView?.font?.withSize(textSize) + } + + infoView.view?.isHidden = true + infoView.view?.backgroundColor = bgColor + view?.addSubview(infoView!.view) + + resizeViews(withKeyboardVisible: textView.isFirstResponder) + + // Setup Text Size Controller + textSizeController = UISlider() + textSizeController.frame = CGRect.zero + textSizeController.minimumValue = 0.0 + textSizeController.maximumValue = Float(maxTextSize - minTextSize) + textSizeController.value = Float(textSize - minTextSize) + let textSizeUp = #imageLiteral(resourceName: "textsize_up.png").resize(to: CGSize(width: 20, height: 20)) + textSizeController.maximumValueImage = textSizeUp + let textSizeDown = #imageLiteral(resourceName: "textsize_up.png").resize(to: CGSize(width: 15, height: 15)) + textSizeController.minimumValueImage = textSizeDown + textSizeController.addTarget(self, action: #selector(self.sliderValueChanged), for: .valueChanged) + + setNavBarButtons() + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + wasKeyboardVisible = textView.isFirstResponder + } + + func setNavBarButtons() { + let orientation: UIInterfaceOrientation = UIApplication.shared.statusBarOrientation + let fixedSpace = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil) + + let imageScaleF: CGFloat = 0.9 + let moreButton = createNavBarButton(with: #imageLiteral(resourceName: "more.png"), + highlightedImage: #imageLiteral(resourceName: "more-selected.png"), + imageScale: imageScaleF, + action: #selector(self.showDropDownMenu), + orientation: orientation) + moreButton.title = "More" + + let infoButton = createNavBarButton(with: #imageLiteral(resourceName: "724-info.png"), + highlightedImage: #imageLiteral(resourceName: "724-info-selected.png"), + imageScale: imageScaleF, + action: #selector(self.infoButtonClick), + orientation: orientation) + infoButton.title = "Info" + + let getStartedButton = createNavBarButton(with: #imageLiteral(resourceName: "887-notepad.png"), + highlightedImage: #imageLiteral(resourceName: "887-notepad-selected.png"), + imageScale: imageScaleF, + action: #selector(self.showGetStartedView), + orientation: orientation) + getStartedButton.title = "Get Started" + + let trashButton = createNavBarButton(with: #imageLiteral(resourceName: "711-trash.png"), + highlightedImage: #imageLiteral(resourceName: "711-trash-selected.png"), + imageScale: imageScaleF, + action: #selector(self.trashButtonClick), + orientation: orientation) + trashButton.title = "Clear Text" + + let textSizeButton = createNavBarButton(with: #imageLiteral(resourceName: "textsize.png"), + highlightedImage: #imageLiteral(resourceName: "textsize_selected.png"), + imageScale: imageScaleF, + action: #selector(self.textSizeButtonClick), + orientation: orientation) + textSizeButton.title = "Text Size" + + let browserButton = createNavBarButton(with: #imageLiteral(resourceName: "786-browser.png"), + highlightedImage: #imageLiteral(resourceName: "786-browser-selected.png"), + imageScale: 1.0, + action: #selector(self.showKMWebBrowserView), + orientation: orientation) + browserButton.title = "Browser" + + let actionButton = createNavBarButton(with: #imageLiteral(resourceName: "702-share.png"), + highlightedImage: #imageLiteral(resourceName: "702-share-selected.png"), + imageScale: imageScaleF, + action: #selector(self.actionButtonClick), + orientation: orientation) + actionButton.title = "Share" + + dropdownItems = [textSizeButton, trashButton, getStartedButton, infoButton] + + var scaleF: CGFloat = (UIScreen.main.scale == 2.0) ? 2.0 : 1.5 + scaleF *= (UIDevice.current.userInterfaceIdiom == .phone) ? 1.0 : 2.0 + fixedSpace.width = 10 * scaleF + if UIDevice.current.userInterfaceIdiom == .phone { + navigationItem.rightBarButtonItems = [moreButton, fixedSpace, browserButton, fixedSpace, actionButton] + } else { + navigationItem.rightBarButtonItems = [infoButton, fixedSpace, getStartedButton, fixedSpace, trashButton, + fixedSpace, textSizeButton, fixedSpace, browserButton, fixedSpace, actionButton] + } + } + + func createNavBarButton(with image: UIImage, + highlightedImage imageHighlighted: UIImage, + imageScale scaleF: CGFloat, + action selector: Selector, + orientation: UIInterfaceOrientation) -> UIBarButtonItem { + let icon = image.resize(to: CGSize(width: image.size.width * scaleF, height: image.size.height * scaleF)) + .withRenderingMode(.alwaysOriginal) + let iconHighlighted = imageHighlighted.resize(to: + CGSize(width: imageHighlighted.size.width * scaleF, height: imageHighlighted.size.height * scaleF)) + .withRenderingMode(.alwaysOriginal) + + let vAdjPort: CGFloat = UIScreen.main.scale == 2.0 ? -3.6 : -2.6 + let vAdjLscpe: CGFloat = -1.6 + + let vAdj = UIInterfaceOrientationIsPortrait(orientation) ? vAdjPort : vAdjLscpe + let navBarHeight = self.navBarHeight() + let y = (navBarHeight - icon.size.height) / 2.0 + vAdj + + let customButton = UIButton(type: .custom) + customButton.setImage(icon, for: .normal) + customButton.setImage(iconHighlighted, for: .highlighted) + customButton.frame = CGRect(x: 0.0, y: y, width: icon.size.width, height: icon.size.height) + customButton.addTarget(self, action: selector, for: .touchUpInside) + + let containerView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: icon.size.width, height: navBarHeight)) + containerView.backgroundColor = UIColor.clear + containerView.addSubview(customButton) + return UIBarButtonItem(customView: containerView) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + if wasKeyboardVisible { + textView.becomeFirstResponder() + } + + if !textView.isEditable { + showActivityIndicator() + } else if shouldShowGetStarted() { + perform(#selector(self.showGetStartedView), with: nil, afterDelay: 1.0) + } + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + override func willRotate(to toInterfaceOrientation: UIInterfaceOrientation, duration: TimeInterval) { + let orientation: UIInterfaceOrientation = toInterfaceOrientation + for view in overlayWindow.subviews { + UIView.animate(withDuration: duration, delay: 0.0, options: ([]), animations: {() -> Void in + view.transform = self.transform(for: orientation) + }, completion: nil) + } + + // TODO: Refactor + if UIDevice.current.userInterfaceIdiom == .phone { + let screenRect = UIScreen.main.bounds + let size = CGFloat.maximum(screenRect.width, screenRect.height) + if size > 568.0 { + // Navbar for iPhone 6 & 6 Plus + let image: UIImage + if UIInterfaceOrientationIsPortrait(orientation) { + image = #imageLiteral(resourceName: "navbar-Portrait.png") + } else { + image = #imageLiteral(resourceName: "navbar-Landscape-568h.png") + } + let bgImg = image.resizableImage(withCapInsets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0), + resizingMode: .stretch) + navigationController?.navigationBar.setBackgroundImage(bgImg, for: UIBarMetrics.default) + } + } else { + if UIInterfaceOrientationIsPortrait(orientation) { + navigationController?.navigationBar.setBackgroundImage(#imageLiteral(resourceName: "navbar-Portrait.png"), for: UIBarMetrics.default) + } else { + navigationController?.navigationBar.setBackgroundImage(#imageLiteral(resourceName: "navbar-Landscape.png"), for: UIBarMetrics.default) + } + } + popover?.dismiss(animated: false) + _ = dismissDropDownMenu() + } + + override func didRotate(from fromInterfaceOrientation: UIInterfaceOrientation) { + if infoView?.view?.isHidden ?? true { + setNavBarButtons() + } + resizeViews(withKeyboardVisible: textView.isFirstResponder) + } + + func resizeViews(withKeyboardVisible keyboardVisible: Bool) { + // Resize textView and infoView + let mainScreen: CGRect = UIScreen.main.bounds + let margin: CGFloat = 2.0 + let barHeights: CGFloat = AppDelegate.statusBarHeight() + navBarHeight() + let kbHeight: CGFloat = keyboardVisible ? textView.inputView?.frame.height ?? 0 : 0 + let width: CGFloat = mainScreen.size.width + let height: CGFloat = mainScreen.size.height - barHeights - kbHeight + + textView.frame = CGRect(x: margin, y: barHeights + margin, + width: width - 2 * margin, height: height - 2 * margin) + infoView?.view?.frame = CGRect(x: 0, y: barHeights, width: width, height: height + kbHeight) + } + + func navBarWidth() -> CGFloat { + return navigationController!.navigationBar.frame.width + } + + func navBarHeight() -> CGFloat { + return navigationController!.navigationBar.frame.height + } + + // MARK: - Keyboard notifications + func keyboardWillShow(_ notification: Notification) { + _ = dismissDropDownMenu() + resizeViews(withKeyboardVisible: true) + } + + func keyboardDidShow(_ notification: Notification) { + // Workaround to display overlay window above keyboard + if iosVersion >= 9 { + let windows = UIApplication.shared.windows + let lastWindow = windows.last + overlayWindow.windowLevel = lastWindow!.windowLevel + 1 + } + } + + func keyboardWillHide(_ notification: Notification) { + resizeViews(withKeyboardVisible: false) + } + + // MARK: - Keyman notifications + func keyboardLoaded(_ notification: Notification) { + startTimer() + } + + func keyboardChanged(_ notification: Notification) { + var listCheck: Bool = true + if didDownload { + listCheck = false + didDownload = false + } + + let kbInfo = notification.userInfo?[kKeymanKeyboardInfoKey] as? [AnyHashable: Any] ?? [AnyHashable: Any]() + let kbID: String = kbInfo[kKeymanKeyboardIdKey] as? String ?? "" + let langID: String = kbInfo[kKeymanLanguageIdKey] as? String ?? "" + checkProfile(forKeyboardID: kbID, languageID: langID, doListCheck: listCheck) + } + + func keyboardDownloadStarted(_ notification: Notification) { + if launchUrl != nil { + showActivityIndicator() + } + } + + func keyboardDownloadFinished(_ notification: Notification) { + didDownload = true + guard let url = launchUrl else { + return + } + let userData = AppDelegate.activeUserDefaults() + let userKeyboards: [Any]? = userData.array(forKey: kKeymanUserKeyboardsListKey) + if userKeyboards == nil || userKeyboards!.isEmpty { + let isRTL = (kKeymanDefaultKeyboardRTL == "Y") ? true : false + KMManager.sharedInstance().addKeyboard(withID: kKeymanDefaultKeyboardID, + languageID: kKeymanDefaultLanguageID, + keyboardName: kKeymanDefaultKeyboardName, + languageName: kKeymanDefaultLanguageName, + isRTL: isRTL, + isCustom: false, + font: kKeymanDefaultKeyboardFont, + oskFont: nil) + } + + perform(#selector(self.dismissActivityIndicator), with: nil, afterDelay: 1.0) + let kbInfo = notification.userInfo?[kKeymanKeyboardInfoKey] + + if let info = kbInfo as? [String: Any] { + let kbID = info[kKeymanKeyboardIdKey] as! String + let langID = info[kKeymanLanguageIdKey] as! String + if url.query == nil || url.query!.range(of: "url=") == nil { + let dictKey = "\(langID)_\(kbID)" + if let kbDict = KMManager.sharedInstance().keyboardsDictionary[dictKey] as? [String: String] { + let kbName = kbDict[kKeymanKeyboardNameKey]! + let langName = kbDict[kKeymanLanguageNameKey]! + let isRTL = (kbDict[kKeymanKeyboardRTLKey]! == "Y") ? true : false + let font = kbDict[kKeymanFontKey]! + let oskFont = kbDict[kKeymanOskFontKey]! + KMManager.sharedInstance().addKeyboard(withID: kbID, languageID: langID, keyboardName: kbName, + languageName: langName, isRTL: isRTL, isCustom: false, font: font, oskFont: oskFont) + KMManager.sharedInstance().setKeyboardWithID(kbID, languageID: langID, keyboardName: kbName, + languageName: langName, font: font, oskFont: oskFont) + } + } else { + let kbName = info[kKeymanKeyboardNameKey] as! String + let langName = info[kKeymanLanguageNameKey] as! String + let isRTL = ((info[kKeymanKeyboardRTLKey] as! String) == "Y") ? true : false + let font = info[kKeymanFontKey] as! String + let oskFont = info[kKeymanOskFontKey] as! String + KMManager.sharedInstance().addKeyboard(withID: nil, languageID: nil, keyboardName: kbName, + languageName: langName, isRTL: isRTL, isCustom: false, font: font, oskFont: oskFont) + KMManager.sharedInstance().setKeyboardWithID(nil, languageID: nil, keyboardName: kbName, + languageName: langName, font: font, oskFont: oskFont) + } + } else if let infoArray = kbInfo as? [[String: Any]] { + for info in infoArray { + let kbID = info[kKeymanKeyboardIdKey] as! String + let langID = info[kKeymanLanguageIdKey] as! String + let kbName = info[kKeymanKeyboardNameKey] as! String + let langName = info[kKeymanLanguageNameKey] as! String + let isRTL = (info[kKeymanKeyboardRTLKey] as! String) == "Y" + let font = info[kKeymanFontKey] as! String + let oskFont = info[kKeymanOskFontKey] as! String + KMManager.sharedInstance().addKeyboard(withID: kbID, languageID: langID, keyboardName: kbName, + languageName: langName, isRTL: isRTL, isCustom: false, font: font, oskFont: oskFont) + KMManager.sharedInstance().setKeyboardWithID(kbID, languageID: langID, keyboardName: kbName, + languageName: langName, font: font, oskFont: oskFont) + } + } + + launchUrl = nil + } + + func keyboardDownloadFailed(_ notification: Notification) { + if launchUrl != nil { + perform(#selector(self.dismissActivityIndicator), with: nil, afterDelay: 1.0) + let error = notification.userInfo?[NSUnderlyingErrorKey] as! Error + showAlert(withTitle: "Keyboard Download Error", message: error.localizedDescription, + cancelButtonTitle: "OK", otherButtonTitles: nil, tag: -1) + launchUrl = nil + } + } + + func languagesUpdated(_ notification: Notification) { + updateStatus = 1 + } + + func languagesDownloadFailed(_ notification: Notification) { + updateStatus = -1 + } + + func keyboardPickerDismissed(_ notification: Notification) { + textView.becomeFirstResponder() + if UIDevice.current.userInterfaceIdiom == .pad { + perform(#selector(self.showGetStartedView), with: nil, afterDelay: 1.0) + } + } + + func launched(fromUrl notification: Notification) { + if let url = notification.userInfo?[urlKey] as? URL, url.query != nil { + launchUrl = url + if updateStatus > 0 { + performAction(from: url) + } + } else { + launchUrl = nil + } + } + + func keyboardRemoved(_ notification: Notification) { + guard let kbInfo = notification.userInfo?[kKeymanKeyboardInfoKey] as? [String: Any] else { + return + } + guard let font = kbInfo[kKeymanFontKey] as? String else { + return + } + guard let profile = profileNameWithJSONFont(font) else { + return + } + profiles2Install = profiles2Install.filter { $0 != profile } + profiles2Install.append(profile) + checkedProfiles = checkedProfiles.filter { $0 != profile } + let userData = AppDelegate.activeUserDefaults() + userData.set(checkedProfiles, forKey: checkedProfilesKey) + userData.synchronize() + } + + func infoButtonClick(_ sender: Any?) { + UIView.setAnimationDelegate(self) + let animSelector = NSSelectorFromString("animationDidStop:finished:context:") + UIView.setAnimationDidStop(animSelector) + UIView.beginAnimations(nil, context: nil) + UIView.setAnimationDuration(0.75) + UIView.setAnimationTransition((!textView.isHidden ? .flipFromRight : .flipFromLeft), for: view, cache: true) + + if !textView.isHidden { + textView.isHidden = true + infoView.view.isHidden = false + infoView.reloadKeymanHelp() + } else { + textView.isHidden = false + infoView.view.isHidden = true + } + + UIView.commitAnimations() + + if !textView.isHidden { + navigationItem.titleView = nil + navigationItem.rightBarButtonItem = nil + setNavBarButtons() + if wasKeyboardVisible { + perform(#selector(self.showKeyboard), with: nil, afterDelay: 0.75) + } + if shouldShowGetStarted() { + perform(#selector(self.showGetStartedView), with: nil, afterDelay: 0.75) + } + } else { + _ = dismissDropDownMenu() + popover?.dismiss(animated: false) + wasKeyboardVisible = textView.isFirstResponder + textView.dismissKeyboard() + + navigationItem.rightBarButtonItems = nil + let vAdjPort: CGFloat = UIScreen.main.scale == 2.0 ? -3.6 : -2.6 + let vAdjLscpe: CGFloat = -1.6 + + let infoDoneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, + action: #selector(self.infoButtonClick)) + infoDoneButton.setBackgroundVerticalPositionAdjustment(vAdjPort, for: UIBarMetrics.default) + infoDoneButton.setBackgroundVerticalPositionAdjustment(vAdjLscpe, for: UIBarMetrics.compact) + navigationItem.rightBarButtonItem = infoDoneButton + + let versionLabel = UILabel() + let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "" + let build = Bundle.main.infoDictionary?[kCFBundleVersionKey as String] as? String ?? "" + versionLabel.text = "Version: \(version) (build \(build))" + versionLabel.font = UIFont.systemFont(ofSize: 9.0) + versionLabel.textAlignment = .right + versionLabel.sizeToFit() + let frame: CGRect = versionLabel.frame + versionLabel.frame = frame.insetBy(dx: -8, dy: 0) + versionLabel.backgroundColor = UIColor.clear + navigationItem.titleView = versionLabel + } + } + + func actionButtonClick(_ sender: Any) { + _ = dismissDropDownMenu() + popover?.dismiss(animated: false) + + let lrm = "\u{200e}" + let rlm = "\u{200f}" + var text: String! = textView.text + if text.hasPrefix(lrm) || text.hasPrefix(rlm) { + text = text.substring(from: text.index(after: text.startIndex)) + } + + let activityProvider = ActivityItemProvider(text: text, font: textView.font) + + let printText = UISimpleTextPrintFormatter(text: textView.text) + printText.font = textView.font + + let activityVC = UIActivityViewController(activityItems: [activityProvider, printText], applicationActivities: nil) + activityVC.excludedActivityTypes = [UIActivityType.assignToContact, UIActivityType.postToWeibo, + UIActivityType.saveToCameraRoll] + activityVC.completionWithItemsHandler = {(_ activityType: UIActivityType?, _ completed: Bool, + _ returnedItems: [Any]?, _ activityError: Error?) -> Void in + self.textView.isUserInteractionEnabled = true + } + + textView.isUserInteractionEnabled = false + if UIDevice.current.userInterfaceIdiom == .phone { + present(activityVC, animated: true, completion: nil) + } else { + popover = UIPopoverController(contentViewController: activityVC) + popover!.present(from: (navigationItem.rightBarButtonItems?[8])!, permittedArrowDirections: .any, + animated: true) + } + } + + func textSizeButtonClick(_ sender: Any) { + _ = dismissDropDownMenu() + popover?.dismiss(animated: false) + + let textSizeVC = UIViewController() + textSizeVC.modalPresentationStyle = .overCurrentContext + textSizeVC.view.backgroundColor = UIColor.clear + + let mframe: CGRect = textSizeVC.view.frame + let w: CGFloat = 304 + let h: CGFloat = 142 + let sframe = CGRect(x: mframe.size.width / 2.0 - w / 2.0, y: mframe.size.height, width: w, height: h) + let eframe = CGRect(x: sframe.origin.x, y: sframe.origin.y - h - 8, width: w, height: h) + let containerView = UIView(frame: sframe) + containerView.autoresizingMask = [.flexibleTopMargin, .flexibleLeftMargin, .flexibleRightMargin] + containerView.tag = 1 + containerView.backgroundColor = UIColor.white + containerView.layer.cornerRadius = 4.0 + + let doneButton = UIButton(type: .roundedRect) + doneButton.addTarget(self, action: #selector(self.dismissTextSizeVC), for: .touchUpInside) + + doneButton.backgroundColor = UIColor.white + doneButton.setTitle("Done", for: .normal) + doneButton.titleLabel?.font = doneButton.titleLabel?.font?.withSize(21.0) + doneButton.setTitleColor(UIColor(red: 0.0, green: 0.5, blue: 1.0, alpha: 1.0), for: .normal) + doneButton.layer.cornerRadius = 4.0 + let bframe = CGRect(x: 0, y: h - 42, width: sframe.size.width, height: 42) + doneButton.frame = bframe + containerView.addSubview(doneButton) + + let lframe = CGRect(x: 0, y: bframe.origin.y - 1, width: sframe.size.width, height: 1) + let borderLine = UIView(frame: lframe) + let c: CGFloat = 230.0 / 255.0 + borderLine.backgroundColor = UIColor(red: c, green: c, blue: c, alpha: 1.0) + containerView.addSubview(borderLine) + + let tframe = CGRect(x: 0, y: 0, width: bframe.size.width, height: 40) + let textSizeTitle = UILabel(frame: tframe) + textSizeTitle.tag = 2 + textSizeTitle.backgroundColor = UIColor.clear + textSizeTitle.text = "Text size: \(Int(textSize))" + textSizeTitle.textAlignment = .center + textSizeTitle.textColor = UIColor.gray + textSizeTitle.font = textSizeTitle.font.withSize(14.0) + containerView.addSubview(textSizeTitle) + + let sliderFrame = CGRect(x: 12, y: 45, width: bframe.size.width - 24, height: 50) + textSizeController.frame = sliderFrame + containerView.addSubview(textSizeController) + + textSizeVC.view.addSubview(containerView) + if UIDevice.current.userInterfaceIdiom == .phone { + wasKeyboardVisible = textView.isFirstResponder + textView.dismissKeyboard() + present(textSizeVC, animated: false, completion: {() -> Void in + UIView.animate(withDuration: 0.25, delay: 0.0, options: .curveEaseIn, animations: {() -> Void in + containerView.frame = eframe + textSizeVC.view.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.4) + }, completion: nil) + }) + } else { + let vc = UIViewController() + vc.modalPresentationStyle = .popover + vc.popoverPresentationController?.barButtonItem = navigationItem.rightBarButtonItems?[6] + vc.view = containerView + vc.preferredContentSize = containerView.frame.size + present(vc, animated: true, completion: nil) + } + } + + func dismissTextSizeVC(_ sender: Any) { + let presentedVC = presentedViewController + if UIDevice.current.userInterfaceIdiom == .phone { + let containerView: UIView! = presentedVC?.view?.viewWithTag(1) + let sframe = containerView.frame + let eframe = CGRect(x: sframe.origin.x, y: sframe.origin.y + sframe.height + 8, + width: sframe.width, height: sframe.height) + UIView.animate(withDuration: 0.25, delay: 0.0, options: .curveEaseIn, animations: {() -> Void in + containerView.frame = eframe + presentedVC?.view?.backgroundColor = UIColor.clear + }, completion: {(_ finished: Bool) -> Void in + presentedVC?.dismiss(animated: false, completion: {() -> Void in + if self.wasKeyboardVisible { + self.textView.becomeFirstResponder() + } + }) + }) + } else { + presentedVC?.dismiss(animated: true) { _ in } + } + } + + func trashButtonClick(_ sender: Any) { + _ = dismissDropDownMenu() + let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) + let clear = UIAlertAction(title: "Clear Text", style: .destructive, handler: {(_ action: UIAlertAction) -> Void in + self.textView.text = "" + }) + let cancel = UIAlertAction(title: "Cancel", style: .default, handler: nil) + alert.addAction(clear) + alert.addAction(cancel) + alert.popoverPresentationController?.barButtonItem = navigationItem.rightBarButtonItems?[4] + present(alert, animated: true, completion: nil) + } + + var overlayWindow: UIWindow! { + return appDelegate?.overlayWindow + } + + func showActivityIndicator() { + if !overlayWindow.isHidden && overlayWindow.viewWithTag(activityIndicatorViewTag) != nil { + return + } + + dismissGetStartedView(nil) + overlayWindow.isHidden = false + let activityView = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge) + let containerView = UIView(frame: activityView.bounds.insetBy(dx: -10.0, dy: -10.0)) + containerView.backgroundColor = UIColor(white: 0.5, alpha: 0.8) + containerView.layer.cornerRadius = 6.0 + containerView.center = overlayWindow.center + containerView.tag = activityIndicatorViewTag + containerView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleTopMargin, + .flexibleBottomMargin] + activityView.center = CGPoint(x: containerView.bounds.size.width * 0.5, y: containerView.bounds.size.height * 0.5) + activityView.startAnimating() + containerView.addSubview(activityView) + overlayWindow.addSubview(containerView) + } + + func dismissActivityIndicator() { + overlayWindow.viewWithTag(activityIndicatorViewTag)?.removeFromSuperview() + overlayWindow.isHidden = true + } + + func sliderValueChanged(_ sender: UISlider) { + textSize = CGFloat(sender.value + Float(minTextSize)).rounded() + textView.font = textView.font?.withSize(textSize) + let textSizeTitle = presentedViewController?.view.viewWithTag(2) as? UILabel + textSizeTitle?.text = "Text size: \(Int(textSize))" + } + + func rectForBarButtonItem(_ barButtonItem: UIBarButtonItem) -> CGRect { + let view = barButtonItem.value(forKey: "view") as? UIView + return view?.frame ?? CGRect.zero + } + + func startTimer() { + if loadTimer == nil { + loadTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, + selector: #selector(self.timerAction), userInfo: nil, repeats: true) + } + } + + func stopTimer() { + if let timer = loadTimer { + timer.invalidate() + loadTimer = nil + } + } + + func timerAction() { + if updateStatus == 1 { + stopTimer() + + dismissActivityIndicator() + + textView.isEditable = true + textView.becomeFirstResponder() + + if let launchUrl = launchUrl { + performAction(from: launchUrl) + } else { + loadSavedUserText() + if shouldShowGetStarted() { + perform(#selector(self.showGetStartedView), with: nil, afterDelay: 1.0) + } + } + } else if updateStatus == -1 { + stopTimer() + dismissActivityIndicator() + loadSavedUserText() + textView.isEditable = true + textView.becomeFirstResponder() + if shouldShowGetStarted() { + perform(#selector(self.showGetStartedView), with: nil, afterDelay: 1.0) + } + } + } + + func resetTextViewCursor() { + textView.selectedRange = NSRange(location: 0, length: 0) + + // TODO: Figure out what this does + if let size = textView.font?.pointSize { + textView.font = textView.font?.withSize(size + 5) + textView.font = textView.font?.withSize(size - 5) + } + } + + func loadSavedUserText() { + let userData = AppDelegate.activeUserDefaults() + let userText = userData.object(forKey: userTextKey) as? String + let userTextSize = userData.object(forKey: userTextSizeKey) as? String + + if let text = userText, !text.isEmpty { + textView.text = text + } + + if let sizeString = userTextSize, let size = Float(sizeString) { + textSize = CGFloat(size) + textView.font = textView.font?.withSize(textSize) + textSizeController.value = (Float(textSize - minTextSize)) + } + } + + func params(of query: String) -> [String: String] { + let components = query.components(separatedBy: "&") + return components.reduce([String: String]()) { prevParams, s in + var params = prevParams + let components = s.components(separatedBy: "=") + if components.count == 2 { + params[components[0]] = components[1] + } + return params + } + } + + // TODO: Refactor control flow + func performAction(from url: URL) { + guard let query = url.query else { + launchUrl = nil + return + } + + if url.lastPathComponent != "open" { + return + } + + let params = self.params(of: query) + if params["url"] == nil { + // Download and set custom keyboard + + let jsonUrl = params["url"].flatMap { URL(string: $0) } + let direct = params["direct"] + + if let jsonUrl = jsonUrl { + KMManager.sharedInstance().dismissKeyboardPicker(self) + if !infoView.view.isHidden { + perform(#selector(self.infoButtonClick), with: nil) + } + + let isDirect = direct == "true" + + customKeyboard2Download = [ + "url": jsonUrl, + "direct": isDirect + ] + let title = "Custom Keyboard: \(jsonUrl.lastPathComponent)" + showAlert(withTitle: title, message: "Would you like to install this keyboard?", + cancelButtonTitle: "Cancel", otherButtonTitles: "Install", tag: 2) + } else { + showAlert(withTitle: "Custom Keyboard", message: "The keyboard could not be installed: Invalid Url", + cancelButtonTitle: "OK", otherButtonTitles: nil, tag: -1) + launchUrl = nil + } + } else { + // Query should include keyboard and language IDs to set the keyboard (first download if not available) + if let kbID = params["keyboard"], let langID = params["language"] { + let dictKey = "\(langID)_\(kbID)" + let kbDict = KMManager.sharedInstance().keyboardsDictionary[dictKey] as? [String: String] ?? [String: String]() + let kbName = kbDict[kKeymanKeyboardNameKey] ?? "" + let langName = kbDict[kKeymanLanguageNameKey] ?? "" + let isRTL = kbDict[kKeymanKeyboardRTLKey] == "Y" + let font = kbDict[kKeymanFontKey] + let oskFont = kbDict[kKeymanOskFontKey] + + if KMManager.sharedInstance().stateForKeyboard(withID: kbID) == kKMKeyboardStateNeedsDownload { + keyboard2Download = [ + kKeymanLanguageIdKey: langID, + kKeymanKeyboardIdKey: kbID + ] + showAlert(withTitle: "\(langName): \(kbName)", message: "Would you like to install this keyboard?", + cancelButtonTitle: "Cancel", otherButtonTitles: "Install", tag: 0) + } else { + KMManager.sharedInstance().addKeyboard(withID: kbID, languageID: langID, keyboardName: kbName, + languageName: langName, isRTL: isRTL, isCustom: false, + font: font, oskFont: oskFont) + KMManager.sharedInstance().setKeyboardWithID(kbID, languageID: langID, keyboardName: kbName, + languageName: langName, font: font, oskFont: oskFont) + } + } else { + launchUrl = nil + } + } + } + + func dictionaryWithKeyboardID(_ kbID: String, languageID langID: String) -> [String: String]? { + let index = KMManager.sharedInstance().indexForUserKeyboard(withID: kbID, languageID: langID) + + if index < 0 { + return nil + } + + let userKeyboards = AppDelegate.activeUserDefaults().array(forKey: kKeymanUserKeyboardsListKey) + return userKeyboards?[index] as? [String: String] + } + + func profileNameWithKeyboardID(_ kbID: String, languageID langID: String) -> String? { + guard let kbDict = dictionaryWithKeyboardID(kbID, languageID: langID) else { + return nil + } + guard let jsonFont = kbDict[kKeymanFontKey] else { + return nil + } + + return profileNameWithJSONFont(jsonFont) + } + + func profileNameWithJSONFont(_ jsonFont: String) -> String? { + guard let fontData = jsonFont.data(using: String.Encoding.utf8) else { + return nil + } + guard let fontDict = (try? JSONSerialization.jsonObject(with: fontData, options: .mutableContainers)) + as? [String: Any] else { + return nil + } + + let fontSource = fontDict[kKeymanFontFilesKey] + if let fontSource = fontSource as? [String] { + return fontSource.first { !($0.contains(".mobileconfig")) } + } + return nil + } + + func checkProfile(forKeyboardID kbID: String, languageID langID: String, doListCheck: Bool) { + if (kbID == kKeymanDefaultKeyboardID) && (langID == kKeymanDefaultLanguageID) { + return + } + + guard let profile = profileNameWithKeyboardID(kbID, languageID: langID) else { + return + } + + var doInstall: Bool = false + if doListCheck { + for value in profiles2Install where !checkedProfiles.contains(value) && profile == value { + doInstall = true + break + } + } else { + doInstall = true + } + + if doInstall { + profileName = profile + let kbDict = dictionaryWithKeyboardID(kbID, languageID: langID) + let languageName = kbDict?[kKeymanLanguageNameKey] ?? "" + let title = "\(languageName) Font" + let msg = "Touch Install to make \(languageName) display correctly in all your apps" + showAlert(withTitle: title, message: msg, cancelButtonTitle: "Cancel", otherButtonTitles: "Install", tag: 1) + } else { + profileName = nil + } + } + + func showKeyboard() { + textView.becomeFirstResponder() + } + + func showAlert(withTitle title: String, message msg: String, cancelButtonTitle cbTitle: String?, + otherButtonTitles obTitles: String?, tag: Int) { + dismissGetStartedView(nil) + let alertView = UIAlertView(title: title, message: msg, delegate: self, cancelButtonTitle: cbTitle, + otherButtonTitles: obTitles ?? "") + alertView.tag = tag + alertView.show() + } + + func performAlertButtonClick(withTag tag: Int) { + switch tag { + case 0: + let langID = keyboard2Download[kKeymanLanguageIdKey] as? String + let kbID = keyboard2Download[kKeymanKeyboardIdKey] as? String + KMManager.sharedInstance().downloadKeyboard(withID: kbID, languageID: langID, isUpdate: false) + case 1: + if let profileName = profileName { + checkedProfiles.append(profileName) + let userData = AppDelegate.activeUserDefaults() + userData.set(checkedProfiles, forKey: checkedProfilesKey) + userData.synchronize() + + UIApplication.shared.openURL(URL(string: "\(baseUri)\(profileName)")!) + self.profileName = nil + } + case 2: + if let jsonUrl = customKeyboard2Download["url"] as? URL, + let isDirect = customKeyboard2Download["direct"] as? Bool { + KMManager.sharedInstance().downloadKeyboard(from: jsonUrl, isDirect: isDirect) + } + default: + break + } + } + + func alertView(_ alertView: UIAlertView, clickedButtonAt buttonIndex: Int) { + if buttonIndex != alertView.cancelButtonIndex { + performAlertButtonClick(withTag: alertView.tag) + } + + if let profileName = profileName, alertView.tag == 1 { + checkedProfiles.append(profileName) + let userData = AppDelegate.activeUserDefaults() + userData.set(checkedProfiles, forKey: checkedProfilesKey) + userData.synchronize() + self.profileName = nil + } else if alertView.tag == 2 { + if shouldShowGetStarted() { + showGetStartedView(nil) + } + } + } + + func showDropDownMenu(_ sender: Any) { + if dismissDropDownMenu() { + return + } + + wasKeyboardVisible = textView.isFirstResponder + textView.dismissKeyboard() + + let w: CGFloat = 160 + let h: CGFloat = 44 + let x: CGFloat = navBarWidth() - w + let y: CGFloat = AppDelegate.statusBarHeight() + navBarHeight() + + let dropDownList = DropDownListView(listItems: dropdownItems, itemSize: CGSize(width: w, height: h), + position: CGPoint(x: x, y: y)) + dropDownList.tag = dropDownListTag + + navigationController?.view?.addSubview(dropDownList) + } + + func dismissDropDownMenu() -> Bool { + let view = navigationController?.view?.viewWithTag(dropDownListTag) + if view != nil && wasKeyboardVisible { + textView.becomeFirstResponder() + } + view?.removeFromSuperview() + return view != nil + } + + func showGetStartedView(_ sender: Any?) { + if !overlayWindow.isHidden && overlayWindow.viewWithTag(getStartedViewTag) != nil { + return + } + + popover?.dismiss(animated: false) + _ = dismissDropDownMenu() + overlayWindow.isHidden = false + getStartedVC = GetStartedViewController() + getStartedVC.mainViewController = self + let containerView: UIView! = getStartedVC.view + let navBar = containerView?.viewWithTag(786586) as? UINavigationBar + let doneButton = navBar?.topItem?.rightBarButtonItem + doneButton?.target = self + doneButton?.action = #selector(self.dismissGetStartedView) + + containerView.frame = containerView.frame.insetBy(dx: 15, dy: 140) + containerView.backgroundColor = UIColor.white + containerView.layer.cornerRadius = 6.0 + containerView.center = overlayWindow.center + containerView.tag = getStartedViewTag + containerView.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleTopMargin, + .flexibleBottomMargin] + let orientation = UIApplication.shared.statusBarOrientation + containerView.transform = transform(for: orientation) + overlayWindow.addSubview(containerView) + } + + func dismissGetStartedView(_ sender: Any?) { + overlayWindow.viewWithTag(getStartedViewTag)?.removeFromSuperview() + overlayWindow.isHidden = true + } + + func shouldShowGetStarted() -> Bool { + // Do not display "Get started" when MainView is not visible + if navigationController?.visibleViewController != self { + return false + } + + let userData = AppDelegate.activeUserDefaults() + if userData.bool(forKey: dontShowGetStartedKey) { + return false + } + + if !AppDelegate.isKeymanEnabledSystemWide() { + return true + } + + let userKbs: [[String: Any]]! = userData.object(forKey: kKeymanUserKeyboardsListKey) as? [[String: Any]] + if userKbs.count >= 2 { + return false + } + if userKbs.isEmpty { + return true + } + + let firstKB = userKbs[0] + let kbID = firstKB[kKeymanKeyboardIdKey] as? String + let langID = firstKB[kKeymanLanguageIdKey] as? String + if (kbID == kKeymanDefaultKeyboardID) && (langID == langID) { + return true + } + return false + } + + func showKMWebBrowserView(_ sender: Any) { + popover?.dismiss(animated: false) + _ = dismissDropDownMenu() + let webBrowserVC = WebBrowserViewController() + webBrowserVC.fontFamily = (textView.font?.fontName)! + present(webBrowserVC, animated: true, completion: nil) + } + + func transform(for orientation: UIInterfaceOrientation) -> CGAffineTransform { + return CGAffineTransform.identity + } +}