chatsecureios / ChatSecure / Classes / OTRAppDelegate.m @ fa6e5004
History | View | Annotate | Download (22.7 KB)
1 |
// |
---|---|
2 |
// OTRAppDelegate.m |
3 |
// Off the Record |
4 |
// |
5 |
// Created by Chris Ballinger on 8/11/11. |
6 |
// Copyright (c) 2011 Chris Ballinger. All rights reserved. |
7 |
// |
8 |
// This file is part of ChatSecure. |
9 |
// |
10 |
// ChatSecure is free software: you can redistribute it and/or modify |
11 |
// it under the terms of the GNU General Public License as published by |
12 |
// the Free Software Foundation, either version 3 of the License, or |
13 |
// (at your option) any later version. |
14 |
// |
15 |
// ChatSecure is distributed in the hope that it will be useful, |
16 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 |
// GNU General Public License for more details. |
19 |
// |
20 |
// You should have received a copy of the GNU General Public License |
21 |
// along with ChatSecure. If not, see <http://www.gnu.org/licenses/>. |
22 |
|
23 |
#import "OTRAppDelegate.h" |
24 |
|
25 |
#import "OTRConversationViewController.h" |
26 |
|
27 |
#import "OTRMessagesHoldTalkViewController.h" |
28 |
#import "OTRSettingsViewController.h" |
29 |
#import "OTRSettingsManager.h" |
30 |
|
31 |
@import Appirater; |
32 |
#import "OTRConstants.h" |
33 |
|
34 |
#import "OTRUtilities.h" |
35 |
#import "OTRAccountsManager.h" |
36 |
#import "OTRSettingsManager.h" |
37 |
@import OTRAssets; |
38 |
#import "OTRDatabaseManager.h" |
39 |
@import SAMKeychain; |
40 |
|
41 |
#import "OTRLog.h" |
42 |
@import CocoaLumberjack; |
43 |
#import "OTRAccount.h" |
44 |
#import "OTRXMPPAccount.h" |
45 |
#import "OTRBuddy.h" |
46 |
@import YapDatabase; |
47 |
|
48 |
#import "OTRCertificatePinning.h" |
49 |
#import "NSURL+ChatSecure.h" |
50 |
#import "OTRDatabaseUnlockViewController.h" |
51 |
#import "OTRIncomingMessage.h" |
52 |
#import "OTROutgoingMessage.h" |
53 |
#import "OTRPasswordGenerator.h" |
54 |
#import "UIViewController+ChatSecure.h" |
55 |
#import "OTRNotificationController.h" |
56 |
@import XMPPFramework; |
57 |
#import "OTRProtocolManager.h" |
58 |
#import "OTRInviteViewController.h" |
59 |
#import "OTRTheme.h" |
60 |
#import <ChatSecureCore/ChatSecureCore-Swift.h> |
61 |
#import "OTRMessagesViewController.h" |
62 |
#import "OTRXMPPTorAccount.h" |
63 |
@import OTRAssets; |
64 |
@import OTRKit; |
65 |
#import "OTRPushTLVHandlerProtocols.h" |
66 |
#import <KSCrash/KSCrash.h> |
67 |
#import <KSCrash/KSCrashInstallationQuincyHockey.h> |
68 |
#import <KSCrash/KSCrashInstallation+Alert.h> |
69 |
@import UserNotifications; |
70 |
|
71 |
#import "OTRChatDemo.h" |
72 |
|
73 |
@interface OTRAppDelegate () <UNUserNotificationCenterDelegate> |
74 |
|
75 |
@property (nonatomic, strong) OTRSplitViewCoordinator *splitViewCoordinator; |
76 |
@property (nonatomic, strong) OTRSplitViewControllerDelegateObject *splitViewControllerDelegate; |
77 |
|
78 |
@property (nonatomic, strong) NSTimer *fetchTimer; |
79 |
@property (nonatomic, strong) NSTimer *backgroundTimer; |
80 |
@property (nonatomic) UIBackgroundTaskIdentifier backgroundTask; |
81 |
|
82 |
@end |
83 |
|
84 |
@implementation OTRAppDelegate |
85 |
@synthesize window = _window; |
86 |
|
87 |
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions |
88 |
{ |
89 |
#if DEBUG |
90 |
[DDLog addLogger:[DDTTYLogger sharedInstance]]; |
91 |
|
92 |
DDFileLogger *fileLogger = [[DDFileLogger alloc] init]; |
93 |
fileLogger.rollingFrequency = 0; |
94 |
fileLogger.maximumFileSize = 0; |
95 |
[DDLog addLogger:fileLogger withLevel:DDLogLevelAll]; |
96 |
#endif |
97 |
|
98 |
[self setupCrashReporting]; |
99 |
|
100 |
_theme = [[[self themeClass] alloc] init]; |
101 |
[self.theme setupGlobalTheme]; |
102 |
|
103 |
[SAMKeychain setAccessibilityType:kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly]; |
104 |
|
105 |
UIViewController *rootViewController = nil; |
106 |
|
107 |
// Create 3 primary view controllers, settings, conversation list and messages |
108 |
_conversationViewController = [self.theme conversationViewController]; |
109 |
_messagesViewController = [self.theme messagesViewController]; |
110 |
|
111 |
|
112 |
if ([OTRDatabaseManager existsYapDatabase] && ![[OTRDatabaseManager sharedInstance] hasPassphrase]) { |
113 |
// user needs to enter password for current database |
114 |
rootViewController = [[OTRDatabaseUnlockViewController alloc] init]; |
115 |
} else { |
116 |
////// Normal launch to conversationViewController ////// |
117 |
if (![OTRDatabaseManager existsYapDatabase]) { |
118 |
/** |
119 |
First Launch |
120 |
Create password and save to keychain |
121 |
**/ |
122 |
NSString *newPassword = [OTRPasswordGenerator passwordWithLength:OTRDefaultPasswordLength]; |
123 |
NSError *error = nil; |
124 |
[[OTRDatabaseManager sharedInstance] setDatabasePassphrase:newPassword remember:YES error:&error]; |
125 |
if (error) { |
126 |
DDLogError(@"Password Error: %@",error); |
127 |
} |
128 |
} |
129 |
|
130 |
[[OTRDatabaseManager sharedInstance] setupDatabaseWithName:OTRYapDatabaseName]; |
131 |
rootViewController = [self setupDefaultSplitViewControllerWithLeadingViewController:[[UINavigationController alloc] initWithRootViewController:self.conversationViewController]]; |
132 |
if ([[[NSProcessInfo processInfo] environment][@"OTRLaunchMode"] isEqualToString:@"ChatSecureUITestsDemoData"]) { |
133 |
[OTRChatDemo loadDemoChatInDatabase]; |
134 |
} else if ([[[NSProcessInfo processInfo] environment][@"OTRLaunchMode"] isEqualToString:@"ChatSecureUITests"]) { |
135 |
[[OTRDatabaseManager sharedInstance].readWriteDatabaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction * _Nonnull transaction) { |
136 |
[transaction removeAllObjectsInAllCollections]; |
137 |
}]; |
138 |
} |
139 |
} |
140 |
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; |
141 |
self.window.rootViewController = rootViewController; |
142 |
|
143 |
/////////// testing VCs |
144 |
// OTRXMPPAccount *account = [[OTRXMPPAccount alloc] init]; |
145 |
// account.username = @"test@example.com"; |
146 |
// OTRInviteViewController *vc = [[OTRInviteViewController alloc] initWithAccount:account]; |
147 |
// UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc]; |
148 |
// self.window.rootViewController = nav; |
149 |
//////////// |
150 |
|
151 |
[self.window makeKeyAndVisible]; |
152 |
[TransactionObserver.shared startObserving]; |
153 |
|
154 |
OTRNotificationController *notificationController = [OTRNotificationController sharedInstance]; |
155 |
[notificationController start]; |
156 |
|
157 |
if ([PushController getPushPreference] == PushPreferenceEnabled) { |
158 |
[PushController registerForPushNotifications]; |
159 |
} |
160 |
|
161 |
[Appirater setAppId:@"464200063"]; |
162 |
[Appirater setOpenInAppStore:NO]; |
163 |
[Appirater appLaunched:YES]; |
164 |
|
165 |
[self autoLoginFromBackground:NO]; |
166 |
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum]; |
167 |
|
168 |
// For disabling screen dimming while plugged in |
169 |
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(batteryStateDidChange:) name:UIDeviceBatteryStateDidChangeNotification object:nil]; |
170 |
[UIDevice currentDevice].batteryMonitoringEnabled = YES; |
171 |
[self batteryStateDidChange:nil]; |
172 |
|
173 |
// Setup iOS 10+ in-app notifications |
174 |
NSOperatingSystemVersion ios10version = {.majorVersion = 10, .minorVersion = 0, .patchVersion = 0}; |
175 |
if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:ios10version]) { |
176 |
[UNUserNotificationCenter currentNotificationCenter].delegate = self; |
177 |
} |
178 |
|
179 |
|
180 |
|
181 |
return YES; |
182 |
} |
183 |
|
184 |
- (void) setupCrashReporting { |
185 |
KSCrash *crash = [KSCrash sharedInstance]; |
186 |
crash.monitoring = KSCrashMonitorTypeProductionSafeMinimal; |
187 |
|
188 |
//#warning Change this to KSCrashMonitorTypeProductionSafeMinimal before App Store release! |
189 |
//#warning Otherwise it may crash for pauses longer than the deadlockWatchdogInterval! |
190 |
|
191 |
// People are reporting deadlocks again... |
192 |
// Let's turn this back on for a little while. |
193 |
#if DEBUG |
194 |
crash.monitoring = KSCrashMonitorTypeDebuggerSafe; |
195 |
#else |
196 |
//crash.monitoring = KSCrashMonitorTypeAll; |
197 |
//crash.deadlockWatchdogInterval = 20; |
198 |
#endif |
199 |
|
200 |
// Setup Crash Reporting |
201 |
KSCrashInstallationHockey* installation = [KSCrashInstallationHockey sharedInstance]; |
202 |
[installation addConditionalAlertWithTitle:Crash_Detected_Title() |
203 |
message:Crash_Detected_Message() |
204 |
yesAnswer:OK_STRING() |
205 |
noAnswer:CANCEL_STRING()]; |
206 |
|
207 |
installation.appIdentifier = [OTRSecrets hockeyLiveIdentifier]; |
208 |
|
209 |
[installation install]; |
210 |
[installation sendAllReportsWithCompletion:^(NSArray *filteredReports, BOOL completed, NSError *error) |
211 |
{ |
212 |
if (error) { |
213 |
NSLog(@"Error sending KSCrashInstallationHockey reports: %@", error); |
214 |
} else { |
215 |
NSLog(@"Sending %d KSCrashInstallationHockey reports.", (int)filteredReports.count); |
216 |
} |
217 |
}]; |
218 |
} |
219 |
|
220 |
/** |
221 |
* This creates a UISplitViewController using a leading view controller (the left view controller). It uses a navigation controller with |
222 |
* self.messagesViewController as teh right view controller; |
223 |
* This also creates and sets up teh OTRSplitViewCoordinator |
224 |
* |
225 |
* @param leadingViewController The leading or left most view controller in a UISplitViewController. Should most likely be some sort of UINavigationViewController |
226 |
* @return The base default UISplitViewController |
227 |
* |
228 |
*/ |
229 |
- (UIViewController *)setupDefaultSplitViewControllerWithLeadingViewController:(nonnull UIViewController *)leadingViewController |
230 |
{ |
231 |
|
232 |
YapDatabaseConnection *connection = [OTRDatabaseManager sharedInstance].readWriteDatabaseConnection; |
233 |
self.splitViewCoordinator = [[OTRSplitViewCoordinator alloc] initWithDatabaseConnection:connection]; |
234 |
self.splitViewControllerDelegate = [[OTRSplitViewControllerDelegateObject alloc] init]; |
235 |
self.conversationViewController.delegate = self.splitViewCoordinator; |
236 |
|
237 |
//MessagesViewController Nav |
238 |
UINavigationController *messagesNavigationController = [[UINavigationController alloc ]initWithRootViewController:self.messagesViewController]; |
239 |
|
240 |
//SplitViewController |
241 |
UISplitViewController *splitViewController = [[UISplitViewController alloc] init]; |
242 |
splitViewController.viewControllers = @[leadingViewController,messagesNavigationController]; |
243 |
splitViewController.delegate = self.splitViewControllerDelegate; |
244 |
splitViewController.title = CHAT_STRING(); |
245 |
|
246 |
//setup 'back' button in nav bar |
247 |
messagesNavigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem; |
248 |
messagesNavigationController.topViewController.navigationItem.leftItemsSupplementBackButton = YES; |
249 |
|
250 |
self.splitViewCoordinator.splitViewController = splitViewController; |
251 |
|
252 |
return splitViewController; |
253 |
} |
254 |
|
255 |
- (void)showConversationViewController |
256 |
{ |
257 |
self.window.rootViewController = [self setupDefaultSplitViewControllerWithLeadingViewController:[[UINavigationController alloc] initWithRootViewController:self.conversationViewController]]; |
258 |
} |
259 |
|
260 |
- (void)applicationWillResignActive:(UIApplication *)application |
261 |
{ |
262 |
[OTRProtocolManager sharedInstance].lastInteractionDate = [NSDate date]; |
263 |
/* |
264 |
Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. |
265 |
Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. |
266 |
*/ |
267 |
} |
268 |
|
269 |
- (void)applicationDidEnterBackground:(UIApplication *)application |
270 |
{ |
271 |
[[OTRProtocolManager sharedInstance] goAwayForAllAccounts]; |
272 |
DDLogInfo(@"Application entered background state."); |
273 |
NSAssert(self.backgroundTask == UIBackgroundTaskInvalid, nil); |
274 |
|
275 |
__block NSUInteger unread = 0; |
276 |
[[OTRDatabaseManager sharedInstance].readOnlyDatabaseConnection asyncReadWithBlock:^(YapDatabaseReadTransaction *transaction) { |
277 |
unread = [transaction numberOfUnreadMessages]; |
278 |
} completionBlock:^{ |
279 |
application.applicationIconBadgeNumber = unread; |
280 |
}]; |
281 |
|
282 |
self.backgroundTask = [application beginBackgroundTaskWithExpirationHandler: ^{ |
283 |
DDLogInfo(@"Background task expired, disconnecting all accounts. Remaining: %f", application.backgroundTimeRemaining); |
284 |
if (self.backgroundTimer) |
285 |
{ |
286 |
[self.backgroundTimer invalidate]; |
287 |
self.backgroundTimer = nil; |
288 |
} |
289 |
[[OTRProtocolManager sharedInstance] disconnectAllAccountsSocketOnly:YES timeout:application.backgroundTimeRemaining - .5 completionBlock:^{ |
290 |
[application endBackgroundTask:self.backgroundTask]; |
291 |
self.backgroundTask = UIBackgroundTaskInvalid; |
292 |
}]; |
293 |
}]; |
294 |
|
295 |
dispatch_async(dispatch_get_main_queue(), ^{ |
296 |
self.backgroundTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerUpdate:) userInfo:nil repeats:YES]; |
297 |
}); |
298 |
} |
299 |
|
300 |
- (void) timerUpdate:(NSTimer*)timer { |
301 |
UIApplication *application = [UIApplication sharedApplication]; |
302 |
NSTimeInterval timeRemaining = application.backgroundTimeRemaining; |
303 |
DDLogVerbose(@"Timer update, background time left: %f", timeRemaining); |
304 |
} |
305 |
|
306 |
/** Doesn't stop autoLogin if previous crash when it's a background launch */ |
307 |
- (void)autoLoginFromBackground:(BOOL)fromBackground |
308 |
{ |
309 |
[[OTRProtocolManager sharedInstance] loginAccounts:[OTRAccountsManager allAutoLoginAccounts]]; |
310 |
} |
311 |
|
312 |
- (void)applicationWillEnterForeground:(UIApplication *)application |
313 |
{ |
314 |
[Appirater appEnteredForeground:YES]; |
315 |
} |
316 |
|
317 |
- (void)applicationDidBecomeActive:(UIApplication *)application |
318 |
{ |
319 |
[OTRProtocolManager sharedInstance].lastInteractionDate = [NSDate date]; |
320 |
[self autoLoginFromBackground:NO]; |
321 |
/* |
322 |
Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. |
323 |
*/ |
324 |
[self batteryStateDidChange:nil]; |
325 |
|
326 |
DDLogInfo(@"Application became active"); |
327 |
|
328 |
if (self.backgroundTimer) |
329 |
{ |
330 |
[self.backgroundTimer invalidate]; |
331 |
self.backgroundTimer = nil; |
332 |
} |
333 |
if (self.backgroundTask != UIBackgroundTaskInvalid) |
334 |
{ |
335 |
[application endBackgroundTask:self.backgroundTask]; |
336 |
self.backgroundTask = UIBackgroundTaskInvalid; |
337 |
} |
338 |
|
339 |
[UIApplication.sharedApplication removeExtraForegroundNotifications]; |
340 |
|
341 |
if (self.fetchTimer) { |
342 |
if (self.fetchTimer.isValid) { |
343 |
NSDictionary *userInfo = self.fetchTimer.userInfo; |
344 |
void (^completion)(UIBackgroundFetchResult) = [userInfo objectForKey:@"completion"]; |
345 |
// We should probbaly return accurate fetch results |
346 |
if (completion) { |
347 |
completion(UIBackgroundFetchResultNewData); |
348 |
} |
349 |
[self.fetchTimer invalidate]; |
350 |
} |
351 |
self.fetchTimer = nil; |
352 |
} |
353 |
} |
354 |
|
355 |
- (void)applicationWillTerminate:(UIApplication *)application |
356 |
{ |
357 |
/* |
358 |
Called when the application is about to terminate. |
359 |
Save data if appropriate. |
360 |
See also applicationDidEnterBackground:. |
361 |
*/ |
362 |
|
363 |
|
364 |
[[OTRProtocolManager sharedInstance] disconnectAllAccounts]; |
365 |
|
366 |
//FIXME? [OTRManagedAccount resetAccountsConnectionStatus]; |
367 |
//[OTRUtilities deleteAllBuddiesAndMessages]; |
368 |
} |
369 |
|
370 |
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { |
371 |
[self autoLoginFromBackground:YES]; |
372 |
|
373 |
self.fetchTimer = [NSTimer scheduledTimerWithTimeInterval:28.5 target:self selector:@selector(fetchTimerUpdate:) userInfo:@{@"completion": completionHandler} repeats:NO]; |
374 |
} |
375 |
|
376 |
- (void) fetchTimerUpdate:(NSTimer*)timer { |
377 |
void (^completion)(UIBackgroundFetchResult) = timer.userInfo[@"completion"]; |
378 |
NSTimeInterval timeout = [[UIApplication sharedApplication] backgroundTimeRemaining] - .5; |
379 |
|
380 |
[[OTRProtocolManager sharedInstance] disconnectAllAccountsSocketOnly:YES timeout:timeout completionBlock:^{ |
381 |
dispatch_async(dispatch_get_main_queue(), ^{ |
382 |
[UIApplication.sharedApplication removeExtraForegroundNotifications]; |
383 |
// We should probably return accurate fetch results |
384 |
if (completion) { |
385 |
completion(UIBackgroundFetchResultNewData); |
386 |
} |
387 |
}); |
388 |
}]; |
389 |
self.fetchTimer = nil; |
390 |
} |
391 |
|
392 |
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler |
393 |
{ |
394 |
[self application:application performFetchWithCompletionHandler:completionHandler]; |
395 |
|
396 |
[[OTRProtocolManager sharedInstance].pushController receiveRemoteNotification:userInfo completion:^(OTRBuddy * _Nullable buddy, NSError * _Nullable error) { |
397 |
// Only show notification if buddy lookup succeeds |
398 |
if (buddy) { |
399 |
[application showLocalNotificationForKnockFrom:buddy]; |
400 |
} |
401 |
}]; |
402 |
} |
403 |
|
404 |
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray * __nullable restorableObjects))restorationHandler { |
405 |
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) { |
406 |
NSURL *url = userActivity.webpageURL; |
407 |
if ([url otr_isInviteLink]) { |
408 |
__block XMPPJID *jid = nil; |
409 |
__block NSString *fingerprint = nil; |
410 |
NSString *otr = [OTRAccount fingerprintStringTypeForFingerprintType:OTRFingerprintTypeOTR]; |
411 |
[url otr_decodeShareLink:^(XMPPJID * _Nullable inJid, NSArray<NSURLQueryItem*> * _Nullable queryItems) { |
412 |
jid = inJid; |
413 |
[queryItems enumerateObjectsUsingBlock:^(NSURLQueryItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { |
414 |
if ([obj.name isEqualToString:otr]) { |
415 |
fingerprint = obj.value; |
416 |
*stop = YES; |
417 |
} |
418 |
}]; |
419 |
}]; |
420 |
if (jid) { |
421 |
[OTRProtocolManager handleInviteForJID:jid otrFingerprint:fingerprint buddyAddedCallback:nil]; |
422 |
} |
423 |
return YES; |
424 |
} |
425 |
} |
426 |
return NO; |
427 |
} |
428 |
|
429 |
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { |
430 |
|
431 |
|
432 |
} |
433 |
|
434 |
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation |
435 |
{ |
436 |
if ([url.scheme isEqualToString:@"xmpp"]) { |
437 |
XMPPURI *xmppURI = [[XMPPURI alloc] initWithURL:url]; |
438 |
XMPPJID *jid = xmppURI.jid; |
439 |
NSString *otrFingerprint = xmppURI.queryParameters[@"otr-fingerprint"]; |
440 |
// NSString *action = xmppURI.queryAction; // && [action isEqualToString:@"subscribe"] |
441 |
if (jid) { |
442 |
[OTRProtocolManager handleInviteForJID:jid otrFingerprint:otrFingerprint buddyAddedCallback:^ (OTRBuddy *buddy) { |
443 |
OTRXMPPBuddy *xmppBuddy = (OTRXMPPBuddy *)buddy; |
444 |
if (xmppBuddy != nil) { |
445 |
NSDictionary *userInfo = [[NSDictionary alloc] initWithObjectsAndKeys:xmppBuddy.threadIdentifier, kOTRNotificationThreadKey, xmppBuddy.threadCollection, kOTRNotificationThreadCollection, nil]; |
446 |
[self enterThreadWithUserInfo:userInfo]; |
447 |
} |
448 |
}]; |
449 |
return YES; |
450 |
} |
451 |
} |
452 |
return NO; |
453 |
} |
454 |
|
455 |
|
456 |
|
457 |
- (void) showSubscriptionRequestForBuddy:(NSDictionary*)userInfo { |
458 |
// This is probably in response to a user requesting subscriptions from us |
459 |
[self.splitViewCoordinator showConversationsViewController]; |
460 |
} |
461 |
|
462 |
- (void) enterThreadWithUserInfo:(NSDictionary*)userInfo { |
463 |
NSString *threadKey = userInfo[kOTRNotificationThreadKey]; |
464 |
NSString *threadCollection = userInfo[kOTRNotificationThreadCollection]; |
465 |
NSParameterAssert(threadKey); |
466 |
NSParameterAssert(threadCollection); |
467 |
if (!threadKey || !threadCollection) { return; } |
468 |
__block id <OTRThreadOwner> thread = nil; |
469 |
[[OTRDatabaseManager sharedInstance].readOnlyDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) { |
470 |
thread = [transaction objectForKey:threadKey inCollection:threadCollection]; |
471 |
}]; |
472 |
if (thread) { |
473 |
[self.splitViewCoordinator enterConversationWithThread:thread sender:self]; |
474 |
} |
475 |
} |
476 |
|
477 |
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings |
478 |
{ |
479 |
[[NSNotificationCenter defaultCenter] postNotificationName:OTRUserNotificationsChanged object:self userInfo:@{@"settings": notificationSettings}]; |
480 |
if (notificationSettings.types == UIUserNotificationTypeNone) { |
481 |
NSLog(@"Push notifications disabled by user."); |
482 |
} else { |
483 |
[application registerForRemoteNotifications]; |
484 |
} |
485 |
} |
486 |
|
487 |
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(nonnull NSData *)deviceToken |
488 |
{ |
489 |
[[OTRProtocolManager sharedInstance].pushController didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; |
490 |
} |
491 |
|
492 |
- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err { |
493 |
[[NSNotificationCenter defaultCenter] postNotificationName:OTRFailedRemoteNotificationRegistration object:self userInfo:@{kOTRNotificationErrorKey:err}]; |
494 |
DDLogError(@"Error in registration. Error: %@%@", [err localizedDescription], [err userInfo]); |
495 |
} |
496 |
|
497 |
// To improve usability, keep the app open when you're plugged in |
498 |
- (void) batteryStateDidChange:(NSNotification*)notification { |
499 |
UIDeviceBatteryState currentState = [[UIDevice currentDevice] batteryState]; |
500 |
if (currentState == UIDeviceBatteryStateCharging || currentState == UIDeviceBatteryStateFull) { |
501 |
[[UIApplication sharedApplication] setIdleTimerDisabled:YES]; |
502 |
} else { |
503 |
[[UIApplication sharedApplication] setIdleTimerDisabled:NO]; |
504 |
} |
505 |
} |
506 |
|
507 |
#pragma mark UNUserNotificationCenterDelegate methods (iOS 10+) |
508 |
|
509 |
- (void) userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler { |
510 |
[OTRAppDelegate visibleThread:^(YapCollectionKey * _Nullable ck) { |
511 |
if ([ck.key isEqualToString:notification.request.content.threadIdentifier]) { |
512 |
completionHandler(UNNotificationPresentationOptionNone); |
513 |
} else { |
514 |
completionHandler(UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert); |
515 |
} |
516 |
} completionQueue:nil]; |
517 |
} |
518 |
|
519 |
- (void) userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler { |
520 |
NSDictionary *userInfo = response.notification.request.content.userInfo; |
521 |
if ([userInfo[kOTRNotificationType] isEqualToString:kOTRNotificationTypeNone]) { |
522 |
// Nothing |
523 |
} else if ([userInfo[kOTRNotificationType] isEqualToString:kOTRNotificationTypeSubscriptionRequest]) { |
524 |
// This is a subscription request |
525 |
[self showSubscriptionRequestForBuddy:userInfo]; |
526 |
} else { |
527 |
[self enterThreadWithUserInfo:userInfo]; |
528 |
} |
529 |
completionHandler(); |
530 |
} |
531 |
|
532 |
#pragma - mark Class Methods |
533 |
+ (instancetype)appDelegate |
534 |
{ |
535 |
return (OTRAppDelegate*)[[UIApplication sharedApplication] delegate]; |
536 |
} |
537 |
|
538 |
#pragma mark - Theming |
539 |
|
540 |
- (Class) themeClass { |
541 |
return [OTRTheme class]; |
542 |
} |
543 |
|
544 |
@end |