diff --git a/Podfile b/Podfile index f24c6de03..bbfd31953 100644 --- a/Podfile +++ b/Podfile @@ -2,8 +2,9 @@ platform :ios, '7.0' link_with ["Signal", "SignalTests"] -pod 'UICKeyChainStore', :podspec => 'Podspecs/UICKeyChainStore.podspec' +pod 'UICKeyChainStore', :podspec => 'Podspecs/UICKeyChainStore.podspec' pod 'OpenSSL', '~> 1.0.108' pod 'MMDrawerController', '~> 0.5.0' pod 'libPhoneNumber-iOS', '~> 0.7' -pod 'PastelogKit', '~> 1.0' +pod 'PastelogKit', '~> 1.0' +pod 'AFNetworking', '~> 2.3.1' diff --git a/Podspecs/UICKeyChainStore.podspec b/Podspecs/UICKeyChainStore.podspec index f87d2f0e0..14b9d904c 100644 --- a/Podspecs/UICKeyChainStore.podspec +++ b/Podspecs/UICKeyChainStore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "UICKeyChainStore" - s.version = "1.0.6" + s.version = "1.0.7" s.summary = "UICKeyChainStore is a simple wrapper for Keychain on iOS and OS X. Makes using Keychain APIs as easy as NSUserDefaults." s.homepage = "https://github.com/kishikawakatsumi/UICKeyChainStore" s.social_media_url = "https://twitter.com/k_katsumi" diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index f7ad8b45d..8705dc46b 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -378,10 +378,12 @@ A1C32D5117A06544000A904E /* AddressBook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1C32D4D17A0652C000A904E /* AddressBook.framework */; }; AA0C8E498E2046B0B81EEE6E /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8313AE91B4954215858A5662 /* libPods.a */; }; B60C16651988999D00E97A6C /* VersionMigrations.m in Sources */ = {isa = PBXBuildFile; fileRef = B60C16641988999D00E97A6C /* VersionMigrations.m */; }; + B67ADDC41989FF8700E1A773 /* CallServerRequestsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B67ADDC31989FF8700E1A773 /* CallServerRequestsManager.m */; }; B67EBF5D19194AC60084CCFD /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = B67EBF5C19194AC60084CCFD /* Settings.bundle */; }; B69CD25119773E79005CE69A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B69CD25019773E79005CE69A /* XCTest.framework */; }; B6B1013C196D213F007E3930 /* SGNKeychainUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B1013B196D213F007E3930 /* SGNKeychainUtil.m */; }; B6B6C3C71919440C00C0B76B /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = B6B6C3C51919440C00C0B76B /* Localizable.strings */; }; + B6B9ECFC198B31BA00C620D3 /* PushManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B9ECFB198B31BA00C620D3 /* PushManager.m */; }; B90418E6183E9DD40038554A /* DateUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = B90418E5183E9DD40038554A /* DateUtil.m */; }; B90418E7183E9DD40038554A /* DateUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = B90418E5183E9DD40038554A /* DateUtil.m */; }; B942EB0E183A9633000887BB /* SearchBarTitleView.m in Sources */ = {isa = PBXBuildFile; fileRef = B942EB0D183A9633000887BB /* SearchBarTitleView.m */; }; @@ -1071,6 +1073,8 @@ B60C16631988999D00E97A6C /* VersionMigrations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VersionMigrations.h; sourceTree = ""; }; B60C16641988999D00E97A6C /* VersionMigrations.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VersionMigrations.m; sourceTree = ""; }; B657DDC91911A40500F45B0C /* Signal.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Signal.entitlements; sourceTree = ""; }; + B67ADDC21989FF8700E1A773 /* CallServerRequestsManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CallServerRequestsManager.h; sourceTree = ""; }; + B67ADDC31989FF8700E1A773 /* CallServerRequestsManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CallServerRequestsManager.m; sourceTree = ""; }; B67EBF5C19194AC60084CCFD /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = Settings.bundle; path = SettingsBundle/Settings.bundle; sourceTree = SOURCE_ROOT; }; B69CD25019773E79005CE69A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; B6B1013A196D213F007E3930 /* SGNKeychainUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SGNKeychainUtil.h; sourceTree = ""; }; @@ -1083,6 +1087,8 @@ B6B6C3CC1919454200C0B76B /* ro-RO */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ro-RO"; path = "ro-RO.lproj/Localizable.strings"; sourceTree = ""; }; B6B6C3CD1919455400C0B76B /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; B6B6C3CE1919456C00C0B76B /* sv-SE */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sv-SE"; path = "sv-SE.lproj/Localizable.strings"; sourceTree = ""; }; + B6B9ECFA198B31BA00C620D3 /* PushManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PushManager.h; sourceTree = ""; }; + B6B9ECFB198B31BA00C620D3 /* PushManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PushManager.m; sourceTree = ""; }; B90418E4183E9DD40038554A /* DateUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DateUtil.h; sourceTree = ""; }; B90418E5183E9DD40038554A /* DateUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DateUtil.m; sourceTree = ""; }; B942EB0C183A9633000887BB /* SearchBarTitleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SearchBarTitleView.h; sourceTree = ""; }; @@ -1676,6 +1682,8 @@ 76EB043C18170B33006006FC /* rtp */, 76EB047718170B33006006FC /* tcp */, 76EB048518170B33006006FC /* udp */, + B6B9ECFA198B31BA00C620D3 /* PushManager.h */, + B6B9ECFB198B31BA00C620D3 /* PushManager.m */, ); path = network; sourceTree = ""; @@ -1705,6 +1713,8 @@ 76EB042818170B33006006FC /* http */ = { isa = PBXGroup; children = ( + B67ADDC21989FF8700E1A773 /* CallServerRequestsManager.h */, + B67ADDC31989FF8700E1A773 /* CallServerRequestsManager.m */, 76EB042918170B33006006FC /* HttpManager.h */, 76EB042A18170B33006006FC /* HttpManager.m */, 76EB042B18170B33006006FC /* HttpRequest.h */, @@ -2985,10 +2995,12 @@ 70B80119190C55660042E3F0 /* TextFormat.m in Sources */, E197B61818BBEC1A00F073E5 /* RemoteIOAudio.m in Sources */, 70B8011C190C55660042E3F0 /* Utilities.m in Sources */, + B67ADDC41989FF8700E1A773 /* CallServerRequestsManager.m in Sources */, 76EB059418170B33006006FC /* HttpManager.m in Sources */, 76EB05EC18170B33006006FC /* CallState.m in Sources */, 76EB05D218170B33006006FC /* ZrtpInitiator.m in Sources */, 76EB05E018170B33006006FC /* NetworkStream.m in Sources */, + B6B9ECFC198B31BA00C620D3 /* PushManager.m in Sources */, 76EB05D618170B33006006FC /* ZrtpResponder.m in Sources */, 70B8010E190C55660042E3F0 /* CodedInputStream.m in Sources */, 7095B7B018F46D35002C66E2 /* PhoneNumberUtil.m in Sources */, @@ -3511,7 +3523,7 @@ LLVM_LTO = NO; OTHER_LDFLAGS = "$(inherited)"; PRODUCT_NAME = Signal; - PROVISIONING_PROFILE = "036480DA-A21D-4CC6-BF48-98E8AE1EE981"; + PROVISIONING_PROFILE = "A4026C2D-D5F0-40C5-B1B4-5EA8E8A1876B"; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = 1; TEST_AFTER_BUILD = YES; @@ -3747,7 +3759,7 @@ LLVM_LTO = NO; OTHER_LDFLAGS = "$(inherited)"; PRODUCT_NAME = Signal; - PROVISIONING_PROFILE = "036480DA-A21D-4CC6-BF48-98E8AE1EE981"; + PROVISIONING_PROFILE = "A4026C2D-D5F0-40C5-B1B4-5EA8E8A1876B"; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = 1; TEST_AFTER_BUILD = YES; @@ -3795,7 +3807,7 @@ LLVM_LTO = NO; OTHER_LDFLAGS = "$(inherited)"; PRODUCT_NAME = Signal; - PROVISIONING_PROFILE = "036480DA-A21D-4CC6-BF48-98E8AE1EE981"; + PROVISIONING_PROFILE = "A4026C2D-D5F0-40C5-B1B4-5EA8E8A1876B"; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = 1; TEST_AFTER_BUILD = YES; diff --git a/Signal/Signal-Info.plist b/Signal/Signal-Info.plist index 91f847381..731a56035 100644 --- a/Signal/Signal-Info.plist +++ b/Signal/Signal-Info.plist @@ -47,9 +47,8 @@ UIBackgroundModes - remote-notification - fetch audio + remote-notification voip UIRequiredDeviceCapabilities diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 0ad5e7b8f..7b4729957 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -8,6 +8,7 @@ #import "LeftSideMenuViewController.h" #import "MMDrawerController.h" #import "NotificationTracker.h" +#import "PushManager.h" #import "PriorityQueue.h" #import "RecentCallManager.h" #import "Release.h" @@ -16,6 +17,7 @@ #import "Util.h" #import #import "Environment.h" +#import "CallServerRequestsManager.h" #define kSignalVersionKey @"SignalUpdateVersionKey" @@ -31,9 +33,7 @@ @end -@implementation AppDelegate { - FutureSource* futureApnIdSource; -} +@implementation AppDelegate #pragma mark Detect updates - perform migrations @@ -44,13 +44,11 @@ if (!previousVersion) { DDLogError(@"No previous version found. Possibly first launch since install."); - [Environment setCurrent:[Release releaseEnvironmentWithLogging:nil]]; [Environment resetAppData]; // We clean previous keychain entries in case their are some entries remaining. } else if ([currentVersion compare:previousVersion options:NSNumericSearch] == NSOrderedDescending){ // Application was updated, let's see if we have a migration scheme for it. - if ([previousVersion isEqualToString:@"1.0.2"]) { - + // Migrate from custom preferences to NSUserDefaults } } @@ -66,31 +64,32 @@ - (void)protectPreferenceFiles{ - // We have two kind of data to deal with for now, preference files (/Library/Preferences), logs (/Library/Caches) and pr - - NSString *path = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0]; - NSLog(@"PATH: %@", path); - - NSString *path2 = [NSHomeDirectory() stringByAppendingString:@"/Documents"]; - - NSArray *directoryEnum = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path2 error:nil]; + NSMutableArray *pathsToExclude = [NSMutableArray array]; - NSLog(@"%@",directoryEnum); + [pathsToExclude addObject:[[[NSHomeDirectory() stringByAppendingString:@"/Library/Preferences/"] stringByAppendingString:[[NSBundle mainBundle] bundleIdentifier]] stringByAppendingString:@".plist"]]; - NSLog(@"%@ vs %@", NSHomeDirectory(), [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0]); + NSError *error; + + NSString *logPath = [NSHomeDirectory() stringByAppendingString:@"/Library/Caches/Logs/"]; + NSArray *logsFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:logPath error:&error]; - NSString *preferencesPath = [NSHomeDirectory() stringByAppendingString:@"/Library/Preferences"]; - NSString *userDefaultsString = [NSString stringWithFormat:@"%@/%@.plist", preferencesPath,[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"]]; + for (NSUInteger i = 0; i < [logsFiles count]; i++) { + [pathsToExclude addObject:[logPath stringByAppendingString:[logsFiles objectAtIndex:i]]]; + } - NSURL *userDefaultsURL = [NSURL fileURLWithPath:userDefaultsString]; - NSError *error; - [userDefaultsURL setResourceValue: [NSNumber numberWithBool: YES] - forKey: NSURLIsExcludedFromBackupKey error: &error]; + for (NSUInteger i = 0; i < [pathsToExclude count]; i++) { + [[NSURL fileURLWithPath:[pathsToExclude objectAtIndex:i]] setResourceValue: [NSNumber numberWithBool: YES] + forKey: NSURLIsExcludedFromBackupKey error: &error]; + } if (error) { + NSLog(@"Error: %@", error.description); UIAlertView *alert = [[UIAlertView alloc]initWithTitle:NSLocalizedString(@"WARNING", @"") message:NSLocalizedString(@"DISABLING_BACKUP_FAILED", @"") delegate:nil cancelButtonTitle:NSLocalizedString(@"OK", @"") otherButtonTitles:nil, nil]; [alert show]; + + return; } + } - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { @@ -107,10 +106,6 @@ self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.notificationTracker = [NotificationTracker notificationTracker]; - // start register for apn id - futureApnIdSource = [FutureSource new]; - [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge|UIRemoteNotificationTypeSound| UIRemoteNotificationTypeAlert)]; - CategorizingLogger* logger = [CategorizingLogger categorizingLogger]; [logger addLoggingCallback:^(NSString *category, id details, NSUInteger index) {}]; [Environment setCurrent:[Release releaseEnvironmentWithLogging:logger]]; @@ -119,8 +114,6 @@ [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; LeftSideMenuViewController *leftSideMenuViewController = [LeftSideMenuViewController new]; - leftSideMenuViewController.centerTabBarViewController.inboxFeedViewController.apnId = futureApnIdSource; - leftSideMenuViewController.centerTabBarViewController.settingsViewController.apnId = futureApnIdSource; self.drawerController = [[MMDrawerController alloc] initWithCenterViewController:leftSideMenuViewController.centerTabBarViewController leftDrawerViewController:leftSideMenuViewController]; self.window.rootViewController = _drawerController; @@ -135,7 +128,6 @@ [[[Environment phoneManager] currentCallObservable] watchLatestValue:^(CallState* latestCall) { if (latestCall == nil){ - DDLogError(@"Latest Call is nil."); return; } @@ -143,17 +135,18 @@ andOptionallyKnownContact:[latestCall potentiallySpecifiedContact]]; [_drawerController.centerViewController presentViewController:callViewController animated:YES completion:nil]; } onThread:[NSThread mainThread] untilCancelled:nil]; - + + return YES; } - (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken { - DDLogDebug(@"Device registered for push"); - [futureApnIdSource trySetResult:deviceToken]; + [[PushManager sharedManager] registerForPushWithToken:deviceToken]; } + - (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error { + [[PushManager sharedManager]verifyPushActivated]; DDLogError(@"Failed to register for push notifications: %@", error); - [futureApnIdSource trySetFailure:error]; } -(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { @@ -181,6 +174,11 @@ -(void) applicationDidBecomeActive:(UIApplication *)application { [[AppAudioManager sharedInstance] awake]; application.applicationIconBadgeNumber = 0; + + if ([Environment isRegistered]) { + [[PushManager sharedManager] verifyPushActivated]; + [[AppAudioManager sharedInstance] requestRequiredPermissionsIfNeeded]; + } } @end diff --git a/Signal/src/audio/AppAudioManager.m b/Signal/src/audio/AppAudioManager.m index 33f897d41..9f4355804 100644 --- a/Signal/src/audio/AppAudioManager.m +++ b/Signal/src/audio/AppAudioManager.m @@ -159,7 +159,10 @@ AppAudioManager* sharedAppAudioManager; -(void) requestRequiredPermissionsIfNeeded { [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) { - //todo: take action? + if (!granted) { + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"ACTION_REQUIRED_TITLE", @"") message:NSLocalizedString(@"AUDIO_PERMISSION_MESSAGE", @"") delegate:nil cancelButtonTitle:NSLocalizedString(@"OK", @"") otherButtonTitles:nil, nil]; + [alertView show]; + } }]; } diff --git a/Signal/src/audio/incall_audio/processing/DesiredBufferDepthController.m b/Signal/src/audio/incall_audio/processing/DesiredBufferDepthController.m index 3064696a7..42bc0bdef 100644 --- a/Signal/src/audio/incall_audio/processing/DesiredBufferDepthController.m +++ b/Signal/src/audio/incall_audio/processing/DesiredBufferDepthController.m @@ -17,7 +17,7 @@ NSTimeInterval audioDurationPerPacket = (NSTimeInterval)(AUDIO_FRAMES_PER_PACKET*[SpeexCodec frameSizeInSamples]) / SAMPLE_RATE; - double initialDesiredBufferDepth = [[[Environment getCurrent]preferences] getCachedOrDefaultDesiredBufferDepth]; + double initialDesiredBufferDepth = [[Environment preferences] getCachedOrDefaultDesiredBufferDepth]; DropoutTracker* dropoutTracker = [DropoutTracker dropoutTrackerWithAudioDurationPerPacket:audioDurationPerPacket]; @@ -59,7 +59,7 @@ } -(void) terminate { - [[[Environment getCurrent]preferences] setCachedDesiredBufferDepth:[decayingDesiredBufferDepth currentEstimate]]; + [[Environment preferences] setCachedDesiredBufferDepth:[decayingDesiredBufferDepth currentEstimate]]; } @end diff --git a/Signal/src/call/RecentCallManager.m b/Signal/src/call/RecentCallManager.m index 69ab61882..c2bd01457 100644 --- a/Signal/src/call/RecentCallManager.m +++ b/Signal/src/call/RecentCallManager.m @@ -51,7 +51,7 @@ typedef BOOL (^SearchTermConditionalBlock)(RecentCall*, NSUInteger, BOOL*); require(phoneManager != nil); [[phoneManager currentCallObservable] watchLatestValue:^(CallState* latestCall) { - if (latestCall != nil && [[[Environment getCurrent] preferences] getHistoryLogEnabled]) { + if (latestCall != nil && [[Environment preferences] getHistoryLogEnabled]) { [self addCall:latestCall]; } } onThread:[NSThread mainThread] untilCancelled:untilCancelledToken]; @@ -101,7 +101,7 @@ typedef BOOL (^SearchTermConditionalBlock)(RecentCall*, NSUInteger, BOOL*); - (void)addRecentCall:(RecentCall *)recentCall { [_allRecents insertObject:recentCall atIndex:0]; - [[[Environment getCurrent] preferences] setFreshInstallTutorialsEnabled:NO]; + [[Environment preferences] setFreshInstallTutorialsEnabled:NO]; [observableRecentsController updateValue:[_allRecents copy]]; [self saveContactsToDefaults]; } diff --git a/Signal/src/contact/Contact.m b/Signal/src/contact/Contact.m index db1b9b441..106dd0e4a 100644 --- a/Signal/src/contact/Contact.m +++ b/Signal/src/contact/Contact.m @@ -74,7 +74,7 @@ static NSString *const DEFAULTS_KEY_DATE = @"DefaultsKeyDate"; } - (UIImage *)image { - if ([[[Environment getCurrent] preferences] getContactImagesEnabled]) { + if ([[Environment preferences] getContactImagesEnabled]) { return image; } else { return nil; diff --git a/Signal/src/environment/Environment.h b/Signal/src/environment/Environment.h index 99f250370..4da5d58df 100644 --- a/Signal/src/environment/Environment.h +++ b/Signal/src/environment/Environment.h @@ -71,7 +71,7 @@ andCurrentRegionCodeForPhoneNumbers:(NSString*)currentRegionCodeForPhoneNumbers +(bool) hasEnabledTestingOrLegacyOption:(NSString*)flag; +(PhoneManager*) phoneManager; --(PropertyListPreferences*)preferences; ++(PropertyListPreferences*)preferences; +(BOOL)isRegistered; +(void)setRegistered:(BOOL)status; diff --git a/Signal/src/environment/Environment.m b/Signal/src/environment/Environment.m index 2484da6cd..ce2c0e757 100644 --- a/Signal/src/environment/Environment.m +++ b/Signal/src/environment/Environment.m @@ -159,14 +159,13 @@ phoneDirectoryManager; [[NSUserDefaults standardUserDefaults] setObject:status?@YES:@NO forKey:isRegisteredUserDefaultString]; } --(PropertyListPreferences*)preferences{ ++(PropertyListPreferences*)preferences{ return [[PropertyListPreferences alloc]init]; } +(void)resetAppData{ [SGNKeychainUtil wipeKeychain]; - [NSUserDefaults resetStandardUserDefaults]; - [[[Environment getCurrent] preferences] clear]; + [[Environment preferences] clear]; } @end diff --git a/Signal/src/environment/PreferencesUtil.h b/Signal/src/environment/PreferencesUtil.h index 5cf08547f..e7bfc6ae5 100644 --- a/Signal/src/environment/PreferencesUtil.h +++ b/Signal/src/environment/PreferencesUtil.h @@ -22,5 +22,7 @@ -(void) setContactImagesEnabled:(BOOL)enabled; -(void) setAutocorrectEnabled:(BOOL)enabled; -(void) setHistoryLogEnabled:(BOOL)enabled; +-(BOOL) encounteredRevokedPushPermission; +-(void) setRevokedPushPermission:(BOOL)revoked; @end diff --git a/Signal/src/environment/PreferencesUtil.m b/Signal/src/environment/PreferencesUtil.m index 8f9b18354..c91e44c52 100644 --- a/Signal/src/environment/PreferencesUtil.m +++ b/Signal/src/environment/PreferencesUtil.m @@ -20,6 +20,7 @@ #define CONTACT_IMAGES_ENABLED_KEY @"Contact Images Enabled Key" #define AUTOCORRECT_ENABLED_KEY @"Autocorrect Enabled Key" #define HISTORY_LOG_ENABLED_KEY @"History Log Enabled Key" +#define PUSH_REVOKED_KEY @"Push Revoked Key" @implementation PropertyListPreferences (PropertyUtil) @@ -107,4 +108,11 @@ [self setValueForKey:HISTORY_LOG_ENABLED_KEY toValue:[NSNumber numberWithBool:enabled]]; } +-(BOOL) encounteredRevokedPushPermission{ + return [[self tryGetValueForKey:PUSH_REVOKED_KEY] boolValue]; +} +-(void) setRevokedPushPermission:(BOOL)revoked{ + [self setValueForKey:PUSH_REVOKED_KEY toValue:[NSNumber numberWithBool:revoked]]; +} + @end diff --git a/Signal/src/network/PushManager.h b/Signal/src/network/PushManager.h new file mode 100644 index 000000000..363197415 --- /dev/null +++ b/Signal/src/network/PushManager.h @@ -0,0 +1,23 @@ +// +// PushManager.h +// Signal +// +// Created by Frederic Jacobs on 31/07/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import + +@interface PushManager : NSObject + ++ (instancetype)sharedManager; + + +- (void)verifyPushActivated; + +- (void)askForPushRegistration; + +- (void)registerForPushWithToken:(NSData*)token; + +@end + diff --git a/Signal/src/network/PushManager.m b/Signal/src/network/PushManager.m new file mode 100644 index 000000000..16cb46f32 --- /dev/null +++ b/Signal/src/network/PushManager.m @@ -0,0 +1,88 @@ +// +// PushManager.m +// Signal +// +// Created by Frederic Jacobs on 31/07/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// +#import "PreferencesUtil.h" +#import "PushManager.h" +#import "Environment.h" +#import "CallServerRequestsManager.h" + +@interface PushManager () + +@property int retries; + +@end + +@implementation PushManager + ++ (instancetype)sharedManager { + static PushManager *sharedManager = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedManager = [[self alloc] init]; + }); + return sharedManager; +} + +- (void)verifyPushActivated{ + UIRemoteNotificationType notificationTypes = [[UIApplication sharedApplication] enabledRemoteNotificationTypes]; + + BOOL needsPushSettingChangeAlert = NO; + + // enhancement: do custom message depending on the setting? + + if (notificationTypes == UIRemoteNotificationTypeNone) { + needsPushSettingChangeAlert = YES; + } else if (notificationTypes == UIRemoteNotificationTypeBadge) { + needsPushSettingChangeAlert = YES; + } else if (notificationTypes == UIRemoteNotificationTypeAlert) { + needsPushSettingChangeAlert = YES; + } else if (notificationTypes == UIRemoteNotificationTypeSound) { + needsPushSettingChangeAlert = YES; + } else if (notificationTypes == (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert)) { + needsPushSettingChangeAlert = YES; + } else if (notificationTypes == (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)) { + needsPushSettingChangeAlert = YES; + } + + if (needsPushSettingChangeAlert) { + [[Environment preferences] setRevokedPushPermission:YES]; + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"ACTION_REQUIRED_TITLE", @"") message:NSLocalizedString(@"PUSH_SETTINGS_MESSAGE", @"") delegate:nil cancelButtonTitle:NSLocalizedString(@"OK", @"") otherButtonTitles:nil, nil]; + [alertView show]; + } else if (!needsPushSettingChangeAlert){ + if ([[Environment preferences] encounteredRevokedPushPermission]) { + [self askForPushRegistration]; + } + } + +} + +- (void)askForPushRegistration{ + [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeBadge)]; + self.retries = 3; +} + +- (void)registerForPushWithToken:(NSData*)token{ + [[CallServerRequestsManager sharedManager] registerPushToken:token success:^(NSURLSessionDataTask *task, id responseObject) { + if ([task.response isKindOfClass: [NSHTTPURLResponse class]]){ + NSInteger statusCode = [(NSHTTPURLResponse*) task.response statusCode]; + if (statusCode == 200) { + DDLogInfo(@"Device sent push ID to server"); + [[Environment preferences] setRevokedPushPermission:NO]; + } + } + } failure:^(NSURLSessionDataTask *task, NSError *error) { + if (self.retries > 0) { + [self registerForPushWithToken:token]; + self.retries--; + } else{ + [[Environment preferences] setRevokedPushPermission:YES]; + } + }]; +} + + +@end diff --git a/Signal/src/network/http/CallServerRequestsManager.h b/Signal/src/network/http/CallServerRequestsManager.h new file mode 100644 index 000000000..a52e28b1e --- /dev/null +++ b/Signal/src/network/http/CallServerRequestsManager.h @@ -0,0 +1,18 @@ +// +// CallServerRequests.h +// Signal +// +// Created by Frederic Jacobs on 31/07/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// + +#import +#import + +@interface CallServerRequestsManager : NSObject + ++ (instancetype)sharedManager; + +- (void)registerPushToken:(NSData*)deviceToken success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; + +@end diff --git a/Signal/src/network/http/CallServerRequestsManager.m b/Signal/src/network/http/CallServerRequestsManager.m new file mode 100644 index 000000000..ca7c308b6 --- /dev/null +++ b/Signal/src/network/http/CallServerRequestsManager.m @@ -0,0 +1,63 @@ +// +// CallServerRequests.m +// Signal +// +// Created by Frederic Jacobs on 31/07/14. +// Copyright (c) 2014 Open Whisper Systems. All rights reserved. +// +#import "HttpRequest.h" +#import "CallServerRequestsManager.h" +#import "DataUtil.h" +#import "Environment.h" +#import "HostNameEndPoint.h" +#import "SGNKeychainUtil.h" + +#define defaultRequestTimeout + +@interface CallServerRequestsManager () + +@property (nonatomic, retain)AFHTTPSessionManager *operationManager; + +@end + + +@implementation CallServerRequestsManager + ++ (instancetype)sharedManager { + static CallServerRequestsManager *sharedManager = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedManager = [[self alloc] init]; + }); + return sharedManager; +} + +- (id)init{ + self = [super init]; + + if (self) { + HostNameEndPoint *endpoint = [[[Environment getCurrent]masterServerSecureEndPoint] hostNameEndPoint]; + NSURL *endPointURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://%@:%hu", endpoint.hostname, endpoint.port]]; + NSURLSessionConfiguration *sessionConf = [NSURLSessionConfiguration ephemeralSessionConfiguration]; + self.operationManager = [[AFHTTPSessionManager alloc] initWithBaseURL:endPointURL sessionConfiguration:sessionConf]; + [self.operationManager setSecurityPolicy:[AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone]]; + self.operationManager.securityPolicy.allowInvalidCertificates = YES; // We use a custom certificate, not signed by a CA. + self.operationManager.responseSerializer = [AFJSONResponseSerializer serializer]; + } + return self; +} + +- (void)registerPushToken:(NSData*)deviceToken success:(void (^)(NSURLSessionDataTask *task, id responseObject))success +failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure{ + self.operationManager.requestSerializer = [self basicAuthenticationSerializer]; + + [self.operationManager PUT:[NSString stringWithFormat:@"/apn/%@",[deviceToken encodedAsHexString]] parameters:@{} success:success failure:failure]; +} + +- (AFHTTPRequestSerializer*)basicAuthenticationSerializer{ + AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializer serializer]; + [serializer setValue:[HttpRequest computeBasicAuthorizationTokenForLocalNumber:[SGNKeychainUtil localNumber]andPassword:[SGNKeychainUtil serverAuthPassword]] forHTTPHeaderField:@"Authorization"]; + return serializer; +} + +@end diff --git a/Signal/src/phone/signaling/SignalUtil.m b/Signal/src/phone/signaling/SignalUtil.m index 79c9fb458..75e840d33 100644 --- a/Signal/src/phone/signaling/SignalUtil.m +++ b/Signal/src/phone/signaling/SignalUtil.m @@ -99,8 +99,6 @@ NSString* query = [NSString stringWithFormat:@"/apn/%@", [deviceToken encodedAsHexString]]; - DDLogInfo(@"Registered APN Device Token with Signal Server"); - return [HttpRequest httpRequestWithBasicAuthenticationAndMethod:@"PUT" andLocation:query]; } diff --git a/Signal/src/phone/signaling/number directory/PhoneNumberDirectoryFilterManager.m b/Signal/src/phone/signaling/number directory/PhoneNumberDirectoryFilterManager.m index 06b104f37..7cf2821fa 100644 --- a/Signal/src/phone/signaling/number directory/PhoneNumberDirectoryFilterManager.m +++ b/Signal/src/phone/signaling/number directory/PhoneNumberDirectoryFilterManager.m @@ -24,7 +24,7 @@ -(void) startUntilCancelled:(id)cancelToken { lifetimeToken = cancelToken; - phoneNumberDirectoryFilter = [[[Environment getCurrent] preferences] tryGetSavedPhoneNumberDirectory]; + phoneNumberDirectoryFilter = [[Environment preferences] tryGetSavedPhoneNumberDirectory]; if (phoneNumberDirectoryFilter == nil) { phoneNumberDirectoryFilter = [PhoneNumberDirectoryFilter phoneNumberDirectoryFilterDefault]; } @@ -103,7 +103,7 @@ @synchronized(self) { phoneNumberDirectoryFilter = directory; } - [[[Environment getCurrent]preferences] setSavedPhoneNumberDirectory:directory]; + [[Environment preferences] setSavedPhoneNumberDirectory:directory]; [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_DIRECTORY_WAS_UPDATED object:nil]; [self scheduleUpdate]; }]; diff --git a/Signal/src/view controllers/InboxFeedViewController.h b/Signal/src/view controllers/InboxFeedViewController.h index a1369cae1..c4322c74f 100644 --- a/Signal/src/view controllers/InboxFeedViewController.h +++ b/Signal/src/view controllers/InboxFeedViewController.h @@ -22,6 +22,5 @@ @property (nonatomic, strong) IBOutlet UILabel *freshAppTutorialMiddleLabel; -@property (nonatomic, assign) FutureSource *apnId; @end diff --git a/Signal/src/view controllers/InboxFeedViewController.m b/Signal/src/view controllers/InboxFeedViewController.m index 2102b11f2..8cea147a7 100644 --- a/Signal/src/view controllers/InboxFeedViewController.m +++ b/Signal/src/view controllers/InboxFeedViewController.m @@ -49,7 +49,7 @@ static NSString *const FOOTER_TABLE_CELL_IDENTIFIER = @"InboxFeedFooterCell"; [self setupLabelLocalizationAndStyles]; if (![Environment isRegistered]) { - RegisterViewController *registerViewController = [RegisterViewController registerViewControllerForApn:_apnId]; + RegisterViewController *registerViewController = [RegisterViewController registerViewController]; [self presentViewController:registerViewController animated:NO completion:nil]; } _inboxFeedTableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero]; @@ -181,7 +181,7 @@ static NSString *const FOOTER_TABLE_CELL_IDENTIFIER = @"InboxFeedFooterCell"; } - (void)updateTutorialVisibility { - _freshInboxView.hidden = ![[[Environment getCurrent] preferences] getFreshInstallTutorialsEnabled]; + _freshInboxView.hidden = ![[Environment preferences] getFreshInstallTutorialsEnabled]; _inboxFeedTableView.hidden = !_freshInboxView.hidden; } diff --git a/Signal/src/view controllers/RegisterViewController.h b/Signal/src/view controllers/RegisterViewController.h index 53066621d..c2f7abf11 100644 --- a/Signal/src/view controllers/RegisterViewController.h +++ b/Signal/src/view controllers/RegisterViewController.h @@ -5,7 +5,6 @@ #import "FutureSource.h" @interface RegisterViewController : UIViewController { -@private Future* futureApnId; @private FutureSource* registered; @private FutureSource* futureChallengeAcceptedSource; @private CancelTokenSource* life; @@ -36,6 +35,6 @@ - (IBAction)changeCountryCodeTapped; - (IBAction)initiateVoiceVerificationButtonHandler; -+ (RegisterViewController*)registerViewControllerForApn:(Future *)apnId; ++ (RegisterViewController*)registerViewController; @end diff --git a/Signal/src/view controllers/RegisterViewController.m b/Signal/src/view controllers/RegisterViewController.m index 1595fe39c..42087fee8 100644 --- a/Signal/src/view controllers/RegisterViewController.m +++ b/Signal/src/view controllers/RegisterViewController.m @@ -6,6 +6,7 @@ #import "PhoneNumberDirectoryFilterManager.h" #import "PhoneNumberUtil.h" #import "PreferencesUtil.h" +#import "PushManager.h" #import "RegisterViewController.h" #import "SignalUtil.h" #import "SGNKeychainUtil.h" @@ -52,11 +53,8 @@ _enteredPhoneNumber = [NSMutableString string]; } -+ (RegisterViewController*)registerViewControllerForApn:(Future *)apnId { - require(apnId != nil); - ++ (RegisterViewController*)registerViewController { RegisterViewController *viewController = [RegisterViewController new]; - viewController->futureApnId = apnId; viewController->registered = [FutureSource new]; viewController->life = [CancelTokenSource cancelTokenSource]; [[viewController->life getToken] whenCancelledTryCancel:viewController->registered]; @@ -133,7 +131,7 @@ againstTimeout:20.0 untilCancelled:cancelToken]; - Future *futurePhoneRegistrationVerified = [futurePhoneRegistrationStarted then:^(id _) { + return [futurePhoneRegistrationStarted then:^(id _) { [self showViewNumber:CHALLENGE_VIEW_NUMBER]; [Environment setRegistered:YES]; [self.challengeNumberLabel setText:[phoneNumber description]]; @@ -143,24 +141,6 @@ return futureChallengeAcceptedSource; }]; - Future *futureApnToRegister = [futurePhoneRegistrationVerified then:^(HttpResponse* okResponse) { - return [futureApnId catch:^id(id error) { - DDLogError(@"Could not get APN. Runs in Simulator?"); - return nil; - }]; - }]; - - return [futureApnToRegister then:^Future*(NSData* deviceToken) { - if (deviceToken == nil){ - DDLogError(@"Couldn't get a device token for APN. Runs in Simulator?"); - return futureApnToRegister; - } - - HttpRequest* request = [HttpRequest httpRequestToRegisterForApnSignalingWithDeviceToken:deviceToken]; - return [HttpManager asyncOkResponseFromMasterServer:request - unlessCancelled:cancelToken - andErrorHandler:[Environment errorNoter]]; - }]; } - (void)registerPhoneNumberTapped { @@ -219,6 +199,7 @@ [registered trySetResult:@YES]; [self dismissView]; [futureChallengeAcceptedSource trySetResult:result]; + [[PushManager sharedManager] askForPushRegistration]; }]; [futureDone finallyDo:^(Future *completed) { diff --git a/Signal/src/view controllers/SettingsViewController.h b/Signal/src/view controllers/SettingsViewController.h index 50950e2ac..b35678251 100644 --- a/Signal/src/view controllers/SettingsViewController.h +++ b/Signal/src/view controllers/SettingsViewController.h @@ -35,8 +35,6 @@ @property (nonatomic, strong) IBOutlet UITableViewCell *sendDebugLog; -@property (nonatomic, assign) FutureSource *apnId; - - (IBAction)registerTapped; - (IBAction)privacyAndSecurityTapped; diff --git a/Signal/src/view controllers/SettingsViewController.m b/Signal/src/view controllers/SettingsViewController.m index b4de6ebcf..89c323967 100644 --- a/Signal/src/view controllers/SettingsViewController.m +++ b/Signal/src/view controllers/SettingsViewController.m @@ -106,7 +106,7 @@ static NSString *const CHECKBOX_EMPTY_IMAGE_NAME = @"checkbox_empty"; [button setImage:[UIImage imageNamed:CHECKBOX_CHECKMARK_IMAGE_NAME] forState:UIControlStateSelected]; } - PropertyListPreferences *prefs = [[Environment getCurrent] preferences]; + PropertyListPreferences *prefs = [Environment preferences]; _hideContactImagesButton.selected = ![prefs getContactImagesEnabled]; _disableAutocorrectButton.selected = ![prefs getAutocorrectEnabled]; _disableHistoryButton.selected = ![prefs getHistoryLogEnabled]; @@ -155,7 +155,7 @@ static NSString *const CHECKBOX_EMPTY_IMAGE_NAME = @"checkbox_empty"; #pragma mark - Actions - (void)registerTapped { - RegisterViewController *registerViewController = [RegisterViewController registerViewControllerForApn:_apnId]; + RegisterViewController *registerViewController = [RegisterViewController registerViewController]; [self presentViewController:registerViewController animated:YES completion:nil]; } @@ -181,17 +181,17 @@ static NSString *const CHECKBOX_EMPTY_IMAGE_NAME = @"checkbox_empty"; - (IBAction)hideContactImagesButtonTapped { _hideContactImagesButton.selected = !_hideContactImagesButton.selected; - [[[Environment getCurrent] preferences] setContactImagesEnabled:!_hideContactImagesButton.selected]; + [[Environment preferences] setContactImagesEnabled:!_hideContactImagesButton.selected]; } - (IBAction)disableAutocorrectButtonTapped { _disableAutocorrectButton.selected = !_disableAutocorrectButton.selected; - [[[Environment getCurrent] preferences] setAutocorrectEnabled:!_disableAutocorrectButton.selected]; + [[Environment preferences] setAutocorrectEnabled:!_disableAutocorrectButton.selected]; } - (IBAction)disableHistoryButtonTapped { _disableHistoryButton.selected = !_disableHistoryButton.selected; - [[[Environment getCurrent] preferences] setHistoryLogEnabled:!_disableHistoryButton.selected]; + [[Environment preferences] setHistoryLogEnabled:!_disableHistoryButton.selected]; } - (void)clearHistory { diff --git a/Signal/src/view controllers/TabBarParentViewController.m b/Signal/src/view controllers/TabBarParentViewController.m index a78401443..82dfd3bc8 100644 --- a/Signal/src/view controllers/TabBarParentViewController.m +++ b/Signal/src/view controllers/TabBarParentViewController.m @@ -43,7 +43,6 @@ _inviteContactsNavigationController = [[UINavigationController alloc] initWithRootViewController:_inviteContactsViewController]; _contactsViewController = [ContactBrowseViewController new]; _contactNavigationController = [[UINavigationController alloc] initWithRootViewController:_contactsViewController]; - [[AppAudioManager sharedInstance] requestRequiredPermissionsIfNeeded]; } return self; } diff --git a/Signal/src/views/SearchBarTitleView.m b/Signal/src/views/SearchBarTitleView.m index 2edac0530..9940c6bc6 100644 --- a/Signal/src/views/SearchBarTitleView.m +++ b/Signal/src/views/SearchBarTitleView.m @@ -65,7 +65,7 @@ } - (void)updateAutoCorrectionType { - BOOL autoCorrectEnabled = [[[Environment getCurrent] preferences] getAutocorrectEnabled]; + BOOL autoCorrectEnabled = [[Environment preferences] getAutocorrectEnabled]; _searchTextField.autocorrectionType = autoCorrectEnabled ? UITextAutocorrectionTypeYes : UITextAutocorrectionTypeNo; } diff --git a/Signal/test/network/http/HttpRequestResponseTest.m b/Signal/test/network/http/HttpRequestResponseTest.m index 2d8d724e3..d3547cae5 100644 --- a/Signal/test/network/http/HttpRequestResponseTest.m +++ b/Signal/test/network/http/HttpRequestResponseTest.m @@ -18,7 +18,7 @@ [Environment setCurrent:testEnv]; [SGNKeychainUtil setLocalNumberTo:[PhoneNumber phoneNumberFromE164:@"+19025555555"]]; [UICKeyChainStore setString:@"shall_not_password" forKey:@"Password"]; - [[[Environment getCurrent] preferences] setValueForKey:@"PasswordCounter" toValue:@2357]; + [[Environment preferences] setValueForKey:@"PasswordCounter" toValue:@2357]; HttpRequest* h = [HttpRequest httpRequestToInitiateToRemoteNumber:[PhoneNumber phoneNumberFromE164:@"+19023334444"]]; test([[h method] isEqualToString:@"GET"]); test([[h location] isEqualToString:@"/session/1/+19023334444"]); diff --git a/Signal/test/phone/signaling/SessionDescriptorTest.m b/Signal/test/phone/signaling/SessionDescriptorTest.m index 8e2faf11d..015cfec0a 100644 --- a/Signal/test/phone/signaling/SessionDescriptorTest.m +++ b/Signal/test/phone/signaling/SessionDescriptorTest.m @@ -38,8 +38,8 @@ }; [Environment setCurrent:testEnv]; - [[[Environment getCurrent] preferences] setValueForKey:@"Signaling Mac Key" toValue:[@"0000000000000000000000000000000000000000" decodedAsHexString]]; - [[[Environment getCurrent]preferences] setValueForKey:@"Signaling Cipher Key" toValue:[@"00000000000000000000000000000000" decodedAsHexString]]; + [[Environment preferences] setValueForKey:@"Signaling Mac Key" toValue:[@"0000000000000000000000000000000000000000" decodedAsHexString]]; + [[Environment preferences] setValueForKey:@"Signaling Cipher Key" toValue:[@"00000000000000000000000000000000" decodedAsHexString]]; ResponderSessionDescriptor* d = [ResponderSessionDescriptor responderSessionDescriptorFromEncryptedRemoteNotification:notification]; diff --git a/Signal/test/util/CryptoToolsTest.m b/Signal/test/util/CryptoToolsTest.m index 496ecd8ee..c0c797f4e 100644 --- a/Signal/test/util/CryptoToolsTest.m +++ b/Signal/test/util/CryptoToolsTest.m @@ -69,7 +69,6 @@ uint16_t b = [CryptoTools generateSecureRandomUInt16]; uint16_t c = [CryptoTools generateSecureRandomUInt16]; uint16_t d = [CryptoTools generateSecureRandomUInt16]; - int n = sizeof(uint32_t); // extremely unlikely to fail if any reasonable amount of entropy is going into d and d2 test(!(a==b==c==d)); } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index b7badc459..7b33a3781 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -1,5 +1,7 @@ +"ACTION_REQUIRED_TITLE" = "Action Required"; "ANSWER_CALL_BUTTON_TITLE" = "Answer"; "APN" = "Incoming Call"; +"AUDIO_PERMISSION_MESSAGE" = "Signal requires access to your microphone to work properly. You can restore the permission in the Settings app >> Privacy >> Microphone >> Signal"; "CALL_BUTTON_TITLE" = "Call"; "CHALLENGE_CODE_BUTTON_TITLE" = "VERIFY"; "CHOOSE_COUNTRY_CODE" = "Choose Country Code"; @@ -33,7 +35,6 @@ "DIALER_NUMBER_8" = "8"; "DIALER_NUMBER_9" = "9"; "DIALER_NUMBER_PLUS" = "+"; -"DIALER_NUMBER_PLUS" = "+"; "DIALER_NUMBER_POUND" = "#"; "DISABLING_BACKUP_FAILED" = "We encountered an issue while disabling the backup of your call log. Call logs might leak in your iTunes/iCloud backups."; "END_CALL_BAD_INTERACTION_WITH_SERVER" = "Server Failed!"; @@ -85,6 +86,7 @@ "NOTIFICATION_TEXT" = "Some of your contacts have recently registered numbers with Signal!"; "NO_FAVOURITES_TEXT" = "To favorite a contact, tap the star on a contact page."; "OK" = "Ok"; +"PUSH_SETTINGS_MESSAGE" = "Signal requires push notification alerts and sounds to be enabled to work properly. Please change it in the Settings app >> Notification Center >> Signal."; "QUICK_LINKS_TITLE" = "Quick Links"; "RECENT_CALLS_SORTING_TITLE" = "Sorting Method"; "RECENT_NAV_BAR_TITLE" = "Call Log"; @@ -102,12 +104,10 @@ "REGISTER_TEXTBLOCK" = "To get started making secure calls, please confirm your country code and enter your device's phone number."; "REGISTER_VALIDATION_ENTER_CODE" = "Validation Code"; "REGISTER_VALIDATION_SENT" = "We sent your validation code"; - "REGISTER_WELCOME_TEXT" = "Welcome to Signal"; "REGISTRATION_ERROR" = "Registration Error"; "REGISTRATION_BODY" = "We couldn't reach the Signal server. Please try again."; "REJECT_CALL_BUTTON_TITLE" = "Reject"; -"SETTINGS_ANONYMOUS_FEEDBACK" = "Send Anonymous Feedback"; "SETTINGS_CALL_QUALITY" = "Call Quality"; "SETTINGS_CHANGE_VOLUME" = "Change Volume With Buttons"; "SETTINGS_CLEAR_HISTORY_LOG" = "Clear History Log";