Notifications enhancements.

- Tap to reply to a message.
- Badges application with the number of unread messages.
- Pick up a phone call from lock screen, or decline it.
- Settings for notification sounds while app in foreground and text displayed on local notifications.
This commit is contained in:
Frederic Jacobs 2015-04-14 21:49:00 +02:00
parent 80b1f2cbcb
commit 13448bdb2d
40 changed files with 947 additions and 204 deletions

View File

@ -340,6 +340,8 @@
B640C4771A477B0F005C7C8A /* TSAttachementsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B640C4761A477B0F005C7C8A /* TSAttachementsTest.m */; };
B65031CF1A7862AA002EBBBD /* SignedPreKeyDeletionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B65031CE1A7862AA002EBBBD /* SignedPreKeyDeletionTests.m */; };
B65EDA1219E1BE6400AAA7CB /* RPAPICall.m in Sources */ = {isa = PBXBuildFile; fileRef = B65EDA1119E1BE6400AAA7CB /* RPAPICall.m */; };
B66B9F721AEA6D1100E2E609 /* NotificationSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B66B9F711AEA6D1100E2E609 /* NotificationSettingsViewController.m */; };
B66B9F7D1AEAF40500E2E609 /* NotificationSettingsOptionsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B66B9F7C1AEAF40500E2E609 /* NotificationSettingsOptionsViewController.m */; };
B66DBF4A19D5BBC8006EA940 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B66DBF4919D5BBC8006EA940 /* Images.xcassets */; };
B671B2461A93B238002BBD9D /* GroupContactsResult.m in Sources */ = {isa = PBXBuildFile; fileRef = B671B2451A93B238002BBD9D /* GroupContactsResult.m */; };
B67ADDC41989FF8700E1A773 /* RPServerRequestsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B67ADDC31989FF8700E1A773 /* RPServerRequestsManager.m */; };
@ -405,6 +407,8 @@
B6F509971AA53F760068F56A /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = B6F509951AA53F760068F56A /* Localizable.strings */; };
B6FAAAE81A41BC6C007FEC1D /* TSAttachmentPointer.m in Sources */ = {isa = PBXBuildFile; fileRef = B6FAAAE71A41BC6C007FEC1D /* TSAttachmentPointer.m */; };
B6FAAAEE1A41C918007FEC1D /* TSAttachmentStream.m in Sources */ = {isa = PBXBuildFile; fileRef = B6FAAAED1A41C918007FEC1D /* TSAttachmentStream.m */; };
B6FE7EB71ADD62FA00A6D22F /* PushKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B6FE7EB61ADD62FA00A6D22F /* PushKit.framework */; };
B6FE7EBA1ADD63AE00A6D22F /* NSData+ows_StripToken.m in Sources */ = {isa = PBXBuildFile; fileRef = B6FE7EB91ADD63AE00A6D22F /* NSData+ows_StripToken.m */; };
B90418E6183E9DD40038554A /* DateUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = B90418E5183E9DD40038554A /* DateUtil.m */; };
B90418E7183E9DD40038554A /* DateUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = B90418E5183E9DD40038554A /* DateUtil.m */; };
B96A3100187DA1B600648F3E /* HelveticaNeueLTStd-Bd.otf in Resources */ = {isa = PBXBuildFile; fileRef = B96A30FE187DA1B600648F3E /* HelveticaNeueLTStd-Bd.otf */; };
@ -946,6 +950,10 @@
B65EDA1019E1BE6400AAA7CB /* RPAPICall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RPAPICall.h; sourceTree = "<group>"; };
B65EDA1119E1BE6400AAA7CB /* RPAPICall.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RPAPICall.m; sourceTree = "<group>"; };
B661C211198EE2EA00548CA1 /* iOSVersions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iOSVersions.h; path = src/environment/iOSVersions.h; sourceTree = "<group>"; };
B66B9F701AEA6D1100E2E609 /* NotificationSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NotificationSettingsViewController.h; sourceTree = "<group>"; };
B66B9F711AEA6D1100E2E609 /* NotificationSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NotificationSettingsViewController.m; sourceTree = "<group>"; };
B66B9F7B1AEAF40500E2E609 /* NotificationSettingsOptionsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NotificationSettingsOptionsViewController.h; sourceTree = "<group>"; };
B66B9F7C1AEAF40500E2E609 /* NotificationSettingsOptionsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NotificationSettingsOptionsViewController.m; sourceTree = "<group>"; };
B66DBF4919D5BBC8006EA940 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
B671B2441A93B238002BBD9D /* GroupContactsResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GroupContactsResult.h; sourceTree = "<group>"; };
B671B2451A93B238002BBD9D /* GroupContactsResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GroupContactsResult.m; sourceTree = "<group>"; };
@ -1088,6 +1096,9 @@
B6FAAAE71A41BC6C007FEC1D /* TSAttachmentPointer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSAttachmentPointer.m; path = Attachements/TSAttachmentPointer.m; sourceTree = "<group>"; };
B6FAAAEC1A41C918007FEC1D /* TSAttachmentStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSAttachmentStream.h; path = Attachements/TSAttachmentStream.h; sourceTree = "<group>"; };
B6FAAAED1A41C918007FEC1D /* TSAttachmentStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSAttachmentStream.m; path = Attachements/TSAttachmentStream.m; sourceTree = "<group>"; };
B6FE7EB61ADD62FA00A6D22F /* PushKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PushKit.framework; path = System/Library/Frameworks/PushKit.framework; sourceTree = SDKROOT; };
B6FE7EB81ADD63AE00A6D22F /* NSData+ows_StripToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+ows_StripToken.h"; sourceTree = "<group>"; };
B6FE7EB91ADD63AE00A6D22F /* NSData+ows_StripToken.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+ows_StripToken.m"; sourceTree = "<group>"; };
B90418E4183E9DD40038554A /* DateUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DateUtil.h; sourceTree = "<group>"; };
B90418E5183E9DD40038554A /* DateUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DateUtil.m; sourceTree = "<group>"; };
B96A30FE187DA1B600648F3E /* HelveticaNeueLTStd-Bd.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "HelveticaNeueLTStd-Bd.otf"; sourceTree = "<group>"; };
@ -1227,6 +1238,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
B6FE7EB71ADD62FA00A6D22F /* PushKit.framework in Frameworks */,
FC3BD9881A30A790005B96BB /* Social.framework in Frameworks */,
FCB11D8C1A129A76002F93FB /* CoreMedia.framework in Frameworks */,
70377AAB1918450100CAF501 /* MobileCoreServices.framework in Frameworks */,
@ -1750,6 +1762,8 @@
76EB04C818170B33006006FC /* util */ = {
isa = PBXGroup;
children = (
B6FE7EB81ADD63AE00A6D22F /* NSData+ows_StripToken.h */,
B6FE7EB91ADD63AE00A6D22F /* NSData+ows_StripToken.m */,
A59E6D701A79E5D100D98E2E /* MIMETypeUtil.h */,
A59E6D711A79E5D100D98E2E /* MIMETypeUtil.m */,
FCFA64B11A24F29E0007FB87 /* UI Categories */,
@ -2449,6 +2463,7 @@
D221A08C169C9E5E00537ABF /* Frameworks */ = {
isa = PBXGroup;
children = (
B6FE7EB61ADD62FA00A6D22F /* PushKit.framework */,
FC3BD9871A30A790005B96BB /* Social.framework */,
B60EDE031A05A01700D73516 /* AudioToolbox.framework */,
FCB11D8B1A129A76002F93FB /* CoreMedia.framework */,
@ -2594,6 +2609,10 @@
FCD274E11A5AFD8000202277 /* PrivacySettingsTableViewController.m */,
FCD274E61A5AFDC900202277 /* AdvancedSettingsTableViewController.h */,
FCD274E71A5AFDC900202277 /* AdvancedSettingsTableViewController.m */,
B66B9F701AEA6D1100E2E609 /* NotificationSettingsViewController.h */,
B66B9F711AEA6D1100E2E609 /* NotificationSettingsViewController.m */,
B66B9F7B1AEAF40500E2E609 /* NotificationSettingsOptionsViewController.h */,
B66B9F7C1AEAF40500E2E609 /* NotificationSettingsOptionsViewController.m */,
FCD274E91A5AFDDB00202277 /* AboutTableViewController.h */,
FCD274EA1A5AFDDB00202277 /* AboutTableViewController.m */,
);
@ -2981,6 +3000,7 @@
B63AF5C71A1F757900D01AAD /* TSContactsIntersectionRequest.m in Sources */,
B6B0968B1A1D25ED008BFAA6 /* TSStorageManager+SignedPreKeyStore.m in Sources */,
76EB05AC18170B33006006FC /* SrtpSocket.m in Sources */,
B6FE7EBA1ADD63AE00A6D22F /* NSData+ows_StripToken.m in Sources */,
B6A5D05C1A7827ED0043D837 /* TSAvailablePreKeysCountRequest.m in Sources */,
FCB11D931A12A4AA002F93FB /* FullImageViewController.m in Sources */,
B6B096871A1D25ED008BFAA6 /* TSStorageManager+IdentityKeyStore.m in Sources */,
@ -3058,6 +3078,7 @@
FCAC963C19FEF9280046DFC5 /* SignalsViewController.m in Sources */,
76EB05DA18170B33006006FC /* LowLatencyConnector.m in Sources */,
76EB05EE18170B33006006FC /* CallTermination.m in Sources */,
B66B9F7D1AEAF40500E2E609 /* NotificationSettingsOptionsViewController.m in Sources */,
E1CD329618BCFF9900B1A496 /* SoundInstance.m in Sources */,
B6FAAAEE1A41C918007FEC1D /* TSAttachmentStream.m in Sources */,
76EB05B418170B33006006FC /* HashChain.m in Sources */,
@ -3128,6 +3149,7 @@
B6FAAAE81A41BC6C007FEC1D /* TSAttachmentPointer.m in Sources */,
B6B096881A1D25ED008BFAA6 /* TSStorageManager+keyFromIntLong.m in Sources */,
B6B50AAB1A4192C500F8F607 /* TSMessagesManager+attachments.m in Sources */,
B66B9F721AEA6D1100E2E609 /* NotificationSettingsViewController.m in Sources */,
76EB059018170B33006006FC /* IgnoredPacketFailure.m in Sources */,
B60FB9B01A4711D4006A5A66 /* TSAttachmentEncryptionResult.m in Sources */,
76EB05D418170B33006006FC /* ZrtpManager.m in Sources */,

View File

@ -21,7 +21,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2.0.3</string>
<string>2.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
@ -38,7 +38,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>2.0.20</string>
<string>2.0.21</string>
<key>LOGS_EMAIL</key>
<string>support@whispersystems.org</string>
<key>LOGS_URL</key>

View File

@ -5,15 +5,15 @@
#import "DebugLogger.h"
#import "DiscardingLog.h"
#import "Environment.h"
#import "InCallViewController.h"
#import "PhoneNumberDirectoryFilterManager.h"
#import "PreferencesUtil.h"
#import "NotificationTracker.h"
#import "PushManager.h"
#import "PriorityQueue.h"
#import "Release.h"
#import "SignalsViewController.h"
#import "TSAccountManager.h"
#import "TSPreKeyManager.h"
#import "TSMessagesManager.h"
#import "TSSocketManager.h"
#import "TSStorageManager.h"
#import "Util.h"
@ -28,10 +28,7 @@
@interface AppDelegate ()
@property (nonatomic, retain) UIWindow *blankWindow;
@property (nonatomic, strong) NotificationTracker *notificationTracker;
@property (nonatomic) TOCFutureSource *callPickUpFuture;
@property (nonatomic, retain) UIWindow *blankWindow;
@end
@ -68,12 +65,12 @@
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self setupAppearance];
[[PushManager sharedManager] registerPushKitNotificationFuture];
if (getenv("runningTests_dontStartApp")) {
return YES;
}
self.notificationTracker = [NotificationTracker notificationTracker];
CategorizingLogger* logger = [CategorizingLogger categorizingLogger];
[logger addLoggingCallback:^(NSString *category, id details, NSUInteger index) {}];
[Environment setCurrent:[Release releaseEnvironmentWithLogging:logger]];
@ -119,7 +116,9 @@
[self prepareScreenshotProtection];
if ([TSAccountManager isRegistered]) {
[TSSocketManager becomeActive];
if ([self applicationIsActive]) {
[TSSocketManager becomeActiveFromForeground];
}
[[PushManager sharedManager] validateUserNotificationSettings];
[self refreshContacts];
[TSPreKeyManager refreshPreKeys];
@ -172,85 +171,27 @@
return NO;
}
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
if ([self isRedPhonePush:userInfo]) {
ResponderSessionDescriptor* call;
if (![self.notificationTracker shouldProcessNotification:userInfo]){
return;
}
@try {
call = [ResponderSessionDescriptor responderSessionDescriptorFromEncryptedRemoteNotification:userInfo];
DDLogDebug(@"Received remote notification. Parsed session descriptor: %@.", call);
self.callPickUpFuture = [TOCFutureSource new];
} @catch (OperationFailed* ex) {
DDLogError(@"Error parsing remote notification. Error: %@.", ex);
return;
}
if (!call) {
DDLogError(@"Decryption of session descriptor failed");
return;
}
[Environment.phoneManager incomingCallWithSession:call];
}
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
if ([self isRedPhonePush:userInfo]) {
[self application:application didReceiveRemoteNotification:userInfo];
} else {
[TSSocketManager becomeActive];
-(void)applicationDidBecomeActive:(UIApplication *)application {
if ([TSAccountManager isRegistered] && [self applicationIsActive]) {
// We're double checking that the app is active, to be sure since we can't verify in production env due to code signing.
[TSSocketManager becomeActiveFromForeground];
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 20 * NSEC_PER_SEC),
dispatch_get_main_queue(), ^{
completionHandler(UIBackgroundFetchResultNewData);
});
}
- (BOOL)isRedPhonePush:(NSDictionary*)pushDict {
NSDictionary *aps = [pushDict objectForKey:@"aps"];
NSString *category = [aps objectForKey:@"category"];
if ([category isEqualToString:Signal_Call_Category]) {
return YES;
} else{
return NO;
}
}
-(void) applicationDidBecomeActive:(UIApplication *)application {
if ([TSAccountManager isRegistered]) {
[TSSocketManager becomeActive];
}
// Hacky way to clear notification center after processed push
[UIApplication.sharedApplication setApplicationIconBadgeNumber:1];
[UIApplication.sharedApplication setApplicationIconBadgeNumber:0];
[self removeScreenProtection];
}
- (void)applicationWillResignActive:(UIApplication *)application{
- (void)applicationWillResignActive:(UIApplication *)application {
[self protectScreen];
if ([TSAccountManager isRegistered]) {
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
[self updateBadge];
}
}
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler{
[self application:application didReceiveRemoteNotification:userInfo];
if ([identifier isEqualToString:Signal_Call_Accept_Identifier]) {
[self.callPickUpFuture trySetResult:@YES];
completionHandler();
} else if ([identifier isEqualToString:Signal_Call_Decline_Identifier]){
[self.callPickUpFuture trySetResult:@NO];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC),
dispatch_get_main_queue(), ^{
completionHandler();
});
} else{
completionHandler();
- (void)updateBadge {
if ([TSAccountManager isRegistered]) {
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:(NSInteger)[[TSMessagesManager sharedManager] unreadMessagesCount]];
}
}
@ -329,4 +270,31 @@
[manager forceUpdate];
}
#pragma mark Push Notifications Delegate Methods
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
[[PushManager sharedManager] application:application didReceiveRemoteNotification:userInfo];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[[PushManager sharedManager] application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{
[[PushManager sharedManager] application:application didReceiveLocalNotification:notification];
}
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void (^)())completionHandler {
[[PushManager sharedManager] application:application handleActionWithIdentifier:identifier forLocalNotification:notification completionHandler:completionHandler];
}
- (BOOL)applicationIsActive {
UIApplication *app = [UIApplication sharedApplication];
if (app.applicationState == UIApplicationStateActive) {
return YES;
}
return NO;
}
@end

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6254" systemVersion="14C109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="tuk-0x-yCb">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="7702" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="tuk-0x-yCb">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6247"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7701"/>
<capability name="Alignment constraints with different attributes" minToolsVersion="5.1"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
@ -4160,6 +4160,69 @@ A0 09 9A FF A8 8A 09 99</string>
</variation>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" id="jp5-vZ-AhJ">
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="jp5-vZ-AhJ" id="sji-CJ-bhq">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Notifications" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="BaC-fn-VoA">
<rect key="frame" x="0.0" y="-21" width="42" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="307" id="HeG-Yg-7m7">
<variation key="heightClass=regular-widthClass=compact" constant="200"/>
</constraint>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
<variation key="default">
<mask key="constraints">
<exclude reference="HeG-Yg-7m7"/>
</mask>
</variation>
<variation key="heightClass=regular-widthClass=compact">
<mask key="constraints">
<include reference="HeG-Yg-7m7"/>
</mask>
</variation>
</label>
</subviews>
<constraints>
<constraint firstItem="BaC-fn-VoA" firstAttribute="leading" secondItem="sji-CJ-bhq" secondAttribute="leadingMargin" id="Tae-ZO-Fxf">
<variation key="heightClass=regular-widthClass=compact" constant="7"/>
</constraint>
<constraint firstAttribute="trailingMargin" secondItem="BaC-fn-VoA" secondAttribute="trailing" constant="70" id="U1d-NR-s31"/>
<constraint firstAttribute="centerY" secondItem="BaC-fn-VoA" secondAttribute="centerY" constant="0.5" id="gMd-UT-AWs">
<variation key="heightClass=regular-widthClass=compact" constant="0.0"/>
</constraint>
<constraint firstItem="BaC-fn-VoA" firstAttribute="top" secondItem="sji-CJ-bhq" secondAttribute="topMargin" constant="3" id="zfO-kb-UBa">
<variation key="heightClass=regular-widthClass=compact" constant="11"/>
</constraint>
</constraints>
<variation key="default">
<mask key="subviews">
<exclude reference="BaC-fn-VoA"/>
</mask>
<mask key="constraints">
<exclude reference="gMd-UT-AWs"/>
<exclude reference="U1d-NR-s31"/>
<exclude reference="zfO-kb-UBa"/>
<exclude reference="Tae-ZO-Fxf"/>
</mask>
</variation>
<variation key="heightClass=regular-widthClass=compact">
<mask key="subviews">
<include reference="BaC-fn-VoA"/>
</mask>
<mask key="constraints">
<include reference="gMd-UT-AWs"/>
<exclude reference="U1d-NR-s31"/>
<exclude reference="zfO-kb-UBa"/>
<include reference="Tae-ZO-Fxf"/>
</mask>
</variation>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" id="Xx7-pz-aLN">
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Xx7-pz-aLN" id="pMA-vR-8Ae">
@ -4404,6 +4467,7 @@ A0 09 9A FF A8 8A 09 99</string>
<outlet property="registeredNumber" destination="ipE-BI-sLL" id="uQB-V6-ooY"/>
<outlet property="settingsAboutTitle" destination="6FV-LR-9Hv" id="fOe-Zy-6i7"/>
<outlet property="settingsAdvancedTitle" destination="qix-5C-dh1" id="vhZ-Zv-TIj"/>
<outlet property="settingsNotifications" destination="BaC-fn-VoA" id="za4-od-AIj"/>
<outlet property="settingsPrivacyTitle" destination="TAs-sK-kbi" id="Ea0-do-XJB"/>
</connections>
</tableViewController>

View File

@ -1,5 +1,6 @@
#import "AppAudioManager.h"
#import <AudioToolbox/AudioServices.h>
#import "AudioRouter.h"
#import "SoundBoard.h"
#import "SoundPlayer.h"

View File

@ -39,7 +39,7 @@
- (void)addCall:(CallState*)call {
require(call != nil);
[call.futureCallLocallyAcceptedOrRejected finallyDo:^(TOCFuture* interactionCompletion) {
[call.futureTermination finallyDo:^(TOCFuture* interactionCompletion) {
bool isOutgoingCall = call.initiatedLocally;
bool isMissedCall = interactionCompletion.hasFailed;
Contact* contact = [self tryGetContactForCall:call];

View File

@ -85,7 +85,7 @@ andCurrentRegionCodeForPhoneNumbers:(NSString*)currentRegionCodeForPhoneNumbers
- (void)setSignalsViewController:(SignalsViewController *)signalsViewController;
- (void)setSignUpFlowNavigationController:(UINavigationController *)signUpFlowNavigationController;
+ (void)messageIdentifier:(NSString*)identifier;
+ (void)messageGroupModel:(TSGroupModel*)model;
+ (void)messageThreadId:(NSString*)threadId;
+ (void)messageIdentifier:(NSString*)identifier withCompose:(BOOL)compose;
+ (void)messageGroupModel:(TSGroupModel*)model withCompose:(BOOL)compose;
@end

View File

@ -11,6 +11,8 @@
#import "PhoneNumberDirectoryFilterManager.h"
#import "SignalKeyingStorage.h"
#import "SignalsViewController.h"
#import "TSContactThread.h"
#import "TSGroupThread.h"
#import "TSStorageManager.h"
static NSString* const kCallSegue = @"2.0_6.0_Call_Segue";
@ -184,7 +186,22 @@ phoneDirectoryManager;
_signUpFlowNavigationController = navigationController;
}
+ (void)messageIdentifier:(NSString*)identifier{
+ (void)messageThreadId:(NSString*)threadId {
TSThread *thread = [TSThread fetchObjectWithUniqueID:threadId];
if (!thread) {
DDLogWarn(@"We get UILocalNotifications with unknown threadId: %@", threadId);
return;
}
if ([thread isGroupThread]) {
[self messageGroup:(TSGroupThread*)thread];
} else {
[self messageIdentifier:((TSContactThread*)thread).contactIdentifier withCompose:YES];
}
}
+ (void)messageIdentifier:(NSString*)identifier withCompose:(BOOL)compose {
Environment *env = [self getCurrent];
SignalsViewController *vc = env.signalsViewController;
@ -194,10 +211,23 @@ phoneDirectoryManager;
[vc.navigationController popToRootViewControllerAnimated:YES];
vc.contactIdentifierFromCompose = identifier;
vc.composeMessage = compose;
[vc performSegueWithIdentifier:@"showSegue" sender:nil];
}
+ (void)messageGroupModel:(TSGroupModel*)model {
+ (void)messageGroup:(TSGroupThread*)groupThread {
Environment *env = [self getCurrent];
SignalsViewController *vc = env.signalsViewController;
if (vc.presentedViewController) {
[vc.presentedViewController dismissViewControllerAnimated:YES completion:nil];
}
[vc.navigationController popToRootViewControllerAnimated:YES];
[vc performSegueWithIdentifier:@"showSegue" sender:groupThread];
}
+ (void)messageGroupModel:(TSGroupModel*)model withCompose:(BOOL)compose {
Environment *env = [self getCurrent];
SignalsViewController *vc = env.signalsViewController;
@ -207,6 +237,7 @@ phoneDirectoryManager;
[vc.navigationController popToRootViewControllerAnimated:YES];
vc.groupFromCompose = model;
vc.composeMessage = compose;
[vc performSegueWithIdentifier:@"showSegue" sender:nil];
}

View File

@ -20,10 +20,10 @@ typedef NS_ENUM(NSUInteger, TSImageQuality) {
@interface PropertyListPreferences (PropertyUtil)
-(PhoneNumberDirectoryFilter*) tryGetSavedPhoneNumberDirectory;
-(void) setSavedPhoneNumberDirectory:(PhoneNumberDirectoryFilter*)phoneNumberDirectoryFilter;
-(NSTimeInterval) getCachedOrDefaultDesiredBufferDepth;
-(void) setCachedDesiredBufferDepth:(double)value;
- (PhoneNumberDirectoryFilter*) tryGetSavedPhoneNumberDirectory;
- (void) setSavedPhoneNumberDirectory:(PhoneNumberDirectoryFilter*)phoneNumberDirectoryFilter;
- (NSTimeInterval) getCachedOrDefaultDesiredBufferDepth;
- (void) setCachedDesiredBufferDepth:(double)value;
- (BOOL) getHasSentAMessage;
- (void) setHasSentAMessage:(BOOL)enabled;
@ -31,20 +31,24 @@ typedef NS_ENUM(NSUInteger, TSImageQuality) {
- (BOOL) getHasArchivedAMessage;
- (void) setHasArchivedAMessage:(BOOL)enabled;
-(BOOL)loggingIsEnabled;
-(void)setLoggingEnabled:(BOOL)flag;
- (BOOL)loggingIsEnabled;
- (void)setLoggingEnabled:(BOOL)flag;
-(BOOL)screenSecurityIsEnabled;
-(void)setScreenSecurity:(BOOL)flag;
- (BOOL)screenSecurityIsEnabled;
- (void)setScreenSecurity:(BOOL)flag;
-(NotificationType)notificationPreviewType;
-(void)setNotificationPreviewType:(NotificationType)type;
- (NotificationType)notificationPreviewType;
- (void)setNotificationPreviewType:(NotificationType)type;
- (NSString*)nameForNotificationPreviewType:(NotificationType)notificationType;
-(TSImageQuality)imageUploadQuality;
-(void)setImageUploadQuality:(TSImageQuality)quality;
- (BOOL)soundInForeground;
- (void)setSoundInForeground:(BOOL)enabled;
-(NSString*)lastRanVersion;
-(NSString*)setAndGetCurrentVersion;
- (TSImageQuality)imageUploadQuality;
- (void)setImageUploadQuality:(TSImageQuality)quality;
- (NSString*)lastRanVersion;
- (NSString*)setAndGetCurrentVersion;
@end

View File

@ -27,6 +27,7 @@
#define HAS_SENT_A_MESSAGE_KEY @"User has sent a message"
#define HAS_ARCHIVED_A_MESSAGE_KEY @"User archived a message"
#define kSignalVersionKey @"SignalUpdateVersionKey"
#define PLAY_SOUND_IN_FOREGROUND_KEY @"NotificationSoundInForeground"
#define BloomFilterCacheName @"bloomfilter"
@ -141,17 +142,6 @@
}
-(NotificationType)notificationPreviewType {
NSNumber *preference = [self tryGetValueForKey:NOTIFICATION_PREVIEW_TYPE_KEY];
if (preference) {
return [preference unsignedIntegerValue];
} else {
return NotificationNamePreview;
}
}
-(TSImageQuality)imageUploadQuality {
// always return average image quality
return TSImageQualityMedium;
@ -161,11 +151,6 @@
[self setValueForKey:IMAGE_UPLOAD_QUALITY_KEY toValue:@(quality)];
}
-(void)setNotificationPreviewType:(NotificationType)type
{
[self setValueForKey:NOTIFICATION_PREVIEW_TYPE_KEY toValue:@(type)];
}
-(void)setScreenSecurity:(BOOL)flag{
[self setValueForKey:SCREEN_SECURITY_KEY toValue:@(flag)];
}
@ -214,6 +199,51 @@
return currentVersion;
}
#pragma mark Notification Preferences
- (BOOL)soundInForeground {
NSNumber *preference = [self tryGetValueForKey:PLAY_SOUND_IN_FOREGROUND_KEY];
if (preference) {
return [preference boolValue];
} else{
return YES;
}
}
- (void)setSoundInForeground:(BOOL)enabled {
[self setValueForKey:PLAY_SOUND_IN_FOREGROUND_KEY toValue:@(enabled)];
}
-(void)setNotificationPreviewType:(NotificationType)type
{
[self setValueForKey:NOTIFICATION_PREVIEW_TYPE_KEY toValue:@(type)];
}
-(NotificationType)notificationPreviewType {
NSNumber *preference = [self tryGetValueForKey:NOTIFICATION_PREVIEW_TYPE_KEY];
if (preference) {
return [preference unsignedIntegerValue];
} else {
return NotificationNamePreview;
}
}
- (NSString*)nameForNotificationPreviewType:(NotificationType)notificationType {
switch (notificationType) {
case NotificationNamePreview:
return NSLocalizedString(@"NOTIFICATIONS_SENDER_AND_MESSAGE", nil);
case NotificationNameNoPreview:
return NSLocalizedString(@"NOTIFICATIONS_SENDER_ONLY", nil);
case NotificationNoNameNoPreview:
return NSLocalizedString(@"NOTIFICATIONS_NONE", nil);
default:
DDLogWarn(@"Undefined NotificationType in Settings");
return @"";
}
}
#pragma mark Bloom filter
- (NSData*)tryRetreiveBloomFilter {

View File

@ -59,8 +59,8 @@
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:waitingController animated:YES completion:nil];
[PushManager.sharedManager registrationAndRedPhoneTokenRequestWithSuccess:^(NSData *pushToken, NSString *signupToken) {
[TSAccountManager registerWithRedPhoneToken:signupToken pushToken:pushToken success:^{
[PushManager.sharedManager registrationAndRedPhoneTokenRequestWithSuccess:^(NSData *pushToken, NSData *voipToken, NSString *signupToken) {
[TSAccountManager registerWithRedPhoneToken:signupToken pushToken:pushToken voipToken:voipToken success:^{
[UIApplication.sharedApplication setNetworkActivityIndicatorVisible:NO];
[self clearMigrationFlag];
Environment *env = [Environment getCurrent];

View File

@ -9,6 +9,8 @@
#import <CollapsingFutures.h>
#import <Foundation/Foundation.h>
#define Signal_Thread_UserInfo_Key @"Signal_Thread_Id"
#define Signal_Call_Accept_Identifier @"Signal_Call_Accept"
#define Signal_Call_Decline_Identifier @"Signal_Call_Decline"
@ -19,6 +21,8 @@
#define Signal_Message_MarkAsRead_Identifier @"Signal_Message_MarkAsRead"
typedef void(^failedPushRegistrationBlock)(NSError *error);
typedef void (^pushTokensSuccessBlock)(NSData *pushToken, NSData *voipToken);
typedef void (^registrationTokensSuccessBlock)(NSData *pushToken, NSData *voipToken, NSString *signupToken);
/**
* The Push Manager is responsible for registering the device for Signal push notifications.
@ -35,7 +39,7 @@ typedef void(^failedPushRegistrationBlock)(NSError *error);
* @param failure Failure completion block
*/
- (void)registrationAndRedPhoneTokenRequestWithSuccess:(void (^)(NSData* pushToken, NSString* signupToken))success failure:(failedPushRegistrationBlock)failure;
- (void)registrationAndRedPhoneTokenRequestWithSuccess:(registrationTokensSuccessBlock)success failure:(failedPushRegistrationBlock)failure;
/**
* Returns the Push Notification Token of this device
@ -44,7 +48,7 @@ typedef void(^failedPushRegistrationBlock)(NSError *error);
* @param failure Failure block, executed when failed to get push token
*/
- (void)requestPushTokenWithSuccess:(void (^)(NSData* pushToken))success failure:(void(^)(NSError *))failure;
- (void)requestPushTokenWithSuccess:(pushTokensSuccessBlock)success failure:(void(^)(NSError *))failure;
/**
* Registers for Users Notifications. By doing this on launch, we are sure that the correct categories of user notifications is registered.
@ -58,5 +62,15 @@ typedef void(^failedPushRegistrationBlock)(NSError *error);
@property TOCFutureSource *pushNotificationFutureSource;
@property TOCFutureSource *userNotificationFutureSource;
@property TOCFutureSource *pushKitNotificationFutureSource;
-(TOCFuture*)registerPushKitNotificationFuture;
#pragma mark Push Notifications Delegate Methods
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo;
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler;
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void (^)())completionHandler;
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification;
@end

View File

@ -5,19 +5,29 @@
// Created by Frederic Jacobs on 31/07/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
//
#import <PushKit/PushKit.h>
#import "ContactsManager.h"
#import "InCallViewController.h"
#import "NotificationTracker.h"
#import "PreferencesUtil.h"
#import "PushManager.h"
#import "Environment.h"
#import "RPServerRequestsManager.h"
#import "TSAccountManager.h"
#import "TSSocketManager.h"
#define pushManagerDomain @"org.whispersystems.pushmanager"
@interface PushManager ()
@interface PushManager () <PKPushRegistryDelegate>
@property TOCFutureSource *registerWithServerFutureSource;
@property UIAlertView *missingPermissionsAlertView;
@property TOCFutureSource *registerWithServerFutureSource;
@property UIAlertView *missingPermissionsAlertView;
@property (nonatomic, strong) NotificationTracker *notificationTracker;
@property (nonatomic) UIBackgroundTaskIdentifier callBackgroundTask;
@end
@implementation PushManager
@ -36,6 +46,7 @@
{
self = [super init];
if (self) {
self.notificationTracker = [NotificationTracker notificationTracker];
self.missingPermissionsAlertView =
[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"ACTION_REQUIRED_TITLE", @"")
message:NSLocalizedString(@"PUSH_SETTINGS_MESSAGE", @"")
@ -46,6 +57,135 @@
return self;
}
#pragma mark Manage Incoming Push
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
if ([self isRedPhonePush:userInfo]) {
ResponderSessionDescriptor* call;
if (![self.notificationTracker shouldProcessNotification:userInfo]){
return;
}
@try {
call = [ResponderSessionDescriptor responderSessionDescriptorFromEncryptedRemoteNotification:userInfo];
DDLogDebug(@"Received remote notification. Parsed session descriptor: %@.", call);
} @catch (OperationFailed* ex) {
DDLogError(@"Error parsing remote notification. Error: %@.", ex);
return;
}
if (!call) {
DDLogError(@"Decryption of session descriptor failed");
return;
}
[Environment.phoneManager incomingCallWithSession:call];
if (![self applicationIsActive]) {
UILocalNotification *notification = [[UILocalNotification alloc] init];
NSString *callerId = call.initiatorNumber.toE164;
NSString *nameString = [[Environment getCurrent].contactsManager nameStringForPhoneIdentifier:callerId];
NSString *displayName = nameString?nameString:callerId;
notification.alertBody = [NSString stringWithFormat:@"Incoming call from %@", displayName];
notification.category = Signal_Call_Category;
notification.soundName = @"r.caf";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
if (_callBackgroundTask == 0) {
_callBackgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
_callBackgroundTask = 0;
[Environment.phoneManager hangupOrDenyCall];
}];
}
}
} else {
if (![self applicationIsActive]) {
[TSSocketManager becomeActiveFromBackground];
}
}
}
/**
* This code should in principle never be called. The only cases where it would be called are with the old-style "content-available:1" pushes if there is no "voip" token registered
*
*/
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
if ([self isRedPhonePush:userInfo]) {
[self application:application didReceiveRemoteNotification:userInfo];
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 20 * NSEC_PER_SEC),
dispatch_get_main_queue(), ^{
completionHandler(UIBackgroundFetchResultNewData);
});
}
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
if ([notification.category isEqualToString:Signal_Message_Category]) {
NSString *threadId = [notification.userInfo objectForKey:Signal_Thread_UserInfo_Key];
[Environment messageThreadId:threadId];
}
}
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void (^)())completionHandler {
if ([identifier isEqualToString:Signal_Call_Accept_Identifier]) {
[Environment.phoneManager answerCall];
completionHandler();
} else if ([identifier isEqualToString:Signal_Call_Decline_Identifier]){
[Environment.phoneManager hangupOrDenyCall];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC),
dispatch_get_main_queue(), ^{
completionHandler();
});
} else{
NSString *threadId = [notification.userInfo objectForKey:Signal_Thread_UserInfo_Key];
[Environment messageThreadId:threadId];
completionHandler();
}
}
- (BOOL)isRedPhonePush:(NSDictionary*)pushDict {
NSDictionary *aps = [pushDict objectForKey:@"aps"];
NSString *category = [aps objectForKey:@"category"];
if ([category isEqualToString:Signal_Call_Category]) {
return YES;
} else{
return NO;
}
}
#pragma mark PushKit
-(void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type
{
[[PushManager sharedManager].pushKitNotificationFutureSource trySetResult:credentials.token];
}
-(void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type
{
[self application:[UIApplication sharedApplication] didReceiveRemoteNotification:payload.dictionaryPayload];
}
-(TOCFuture*)registerPushKitNotificationFuture{
self.pushKitNotificationFutureSource = [TOCFutureSource new];
PKPushRegistry* voipRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()];
voipRegistry.delegate = self;
voipRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
return self.pushKitNotificationFutureSource.future;
}
#pragma mark Register device for Push Notification locally
- (TOCFuture *)registerPushNotificationFuture
@ -56,53 +196,63 @@
return self.pushNotificationFutureSource.future;
}
- (void)requestPushTokenWithSuccess:(void (^)(NSData *pushToken))success failure:(failedPushRegistrationBlock)failure
- (void)requestPushTokenWithSuccess:(pushTokensSuccessBlock)success failure:(failedPushRegistrationBlock)failure
{
TOCFuture *requestPushTokenFuture = [self registerPushNotificationFuture];
[requestPushTokenFuture catchDo:^(id failureObj) {
[self.missingPermissionsAlertView show];
[self.missingPermissionsAlertView show];
failure(failureObj);
DDLogError(@"This should not happen on iOS8. No push token was provided");
}];
[requestPushTokenFuture thenDo:^(NSData *pushToken) {
TOCFuture *registerPushTokenFuture = [self registerForPushFutureWithToken:pushToken];
[registerPushTokenFuture catchDo:^(id failureObj) {
failure(failureObj);
}];
[registerPushTokenFuture thenDo:^(id value) {
TOCFuture *userRegistration = [self registerForUserNotificationsFuture];
[userRegistration thenDo:^(UIUserNotificationSettings *userNotificationSettings) {
success(pushToken);
TOCFuture *voipPushTokenFuture = [self registerPushKitNotificationFuture];
[voipPushTokenFuture finallyDo:^(TOCFuture *completed) {
NSData *voipPushToken = completed.hasResult?completed.forceGetResult:nil;
TOCFuture *registerPushTokenFuture = [self registerForPushFutureWithToken:pushToken voipToken:voipPushToken];
[registerPushTokenFuture catchDo:^(id failureObj) {
failure(failureObj);
}];
[registerPushTokenFuture thenDo:^(id value) {
TOCFuture *userRegistration = [self registerForUserNotificationsFuture];
[userRegistration thenDo:^(UIUserNotificationSettings *userNotificationSettings) {
success(pushToken, voipPushToken);
}];
}];
}];
}];
}];
}
- (void)registrationAndRedPhoneTokenRequestWithSuccess:(void (^)(NSData *pushToken, NSString *signupToken))success
- (void)registrationAndRedPhoneTokenRequestWithSuccess:(registrationTokensSuccessBlock)success
failure:(failedPushRegistrationBlock)failure
{
if (!self.wantRemoteNotifications) {
[self registerTokenWithRedPhoneServer:[@"Fake PushToken" dataUsingEncoding:NSUTF8StringEncoding]
NSData *fakeToken = [@"Fake PushToken" dataUsingEncoding:NSUTF8StringEncoding];
[self registerTokenWithRedPhoneServer:fakeToken
voipToken:fakeToken
withSuccess:success
failure:failure];
return;
}
[self requestPushTokenWithSuccess:^(NSData *pushToken) {
[self registerTokenWithRedPhoneServer:pushToken withSuccess:success failure:failure];
[self requestPushTokenWithSuccess:^(NSData *pushToken, NSData *voipToken) {
[self registerTokenWithRedPhoneServer:pushToken voipToken:voipToken withSuccess:success failure:failure];
} failure:^(NSError *error) {
[self.missingPermissionsAlertView show];
failure([NSError errorWithDomain:pushManagerDomain code:400 userInfo:@{}]);
}];
}
- (void)registerTokenWithRedPhoneServer:(NSData *)pushToken
withSuccess:(void (^)(NSData *pushToken, NSString *signupToken))success
- (void)registerTokenWithRedPhoneServer:(NSData*)pushToken
voipToken:(NSData*)voipToken
withSuccess:(registrationTokensSuccessBlock)success
failure:(failedPushRegistrationBlock)failure
{
[RPServerRequestsManager.sharedInstance performRequest:[RPAPICall requestTextSecureVerificationCode]
@ -117,7 +267,7 @@
return;
}
success(pushToken, tsToken);
success(pushToken, voipToken, tsToken);
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
failure(error);
@ -127,13 +277,61 @@
- (TOCFuture *)registerForUserNotificationsFuture
{
self.userNotificationFutureSource = [TOCFutureSource new];
UIUserNotificationSettings *settings =
[UIUserNotificationSettings settingsForTypes:(UIUserNotificationType)[self allNotificationTypes]
categories:nil];
categories:[NSSet setWithObjects:[self userNotificationsCallCategory], [self userNotificationsMessageCategory], nil]];
[UIApplication.sharedApplication registerUserNotificationSettings:settings];
return self.userNotificationFutureSource.future;
}
- (UIUserNotificationCategory*)userNotificationsMessageCategory{
UIMutableUserNotificationAction *action_accept = [UIMutableUserNotificationAction new];
action_accept.identifier = Signal_Message_View_Identifier;
action_accept.title = NSLocalizedString(@"View", @"");
action_accept.activationMode = UIUserNotificationActivationModeForeground;
action_accept.destructive = NO;
action_accept.authenticationRequired = YES;
UIMutableUserNotificationAction *action_decline = [UIMutableUserNotificationAction new];
action_decline.identifier = Signal_Message_MarkAsRead_Identifier;
action_decline.title = NSLocalizedString(@"Mark as read", @"");
action_decline.activationMode = UIUserNotificationActivationModeBackground;
action_decline.destructive = NO;
action_decline.authenticationRequired = NO;
UIMutableUserNotificationCategory *messageCategory = [UIMutableUserNotificationCategory new];
messageCategory.identifier = Signal_Message_Category;
[messageCategory setActions:@[action_accept, action_decline] forContext:UIUserNotificationActionContextMinimal];
[messageCategory setActions:@[action_accept, action_decline] forContext:UIUserNotificationActionContextDefault];
return messageCategory;
}
- (UIUserNotificationCategory*)userNotificationsCallCategory{
UIMutableUserNotificationAction *action_accept = [UIMutableUserNotificationAction new];
action_accept.identifier = Signal_Call_Accept_Identifier;
action_accept.title = NSLocalizedString(@"ANSWER_CALL_BUTTON_TITLE", @"");
action_accept.activationMode = UIUserNotificationActivationModeForeground;
action_accept.destructive = NO;
action_accept.authenticationRequired = NO;
UIMutableUserNotificationAction *action_decline = [UIMutableUserNotificationAction new];
action_decline.identifier = Signal_Call_Decline_Identifier;
action_decline.title = NSLocalizedString(@"REJECT_CALL_BUTTON_TITLE", @"");
action_decline.activationMode = UIUserNotificationActivationModeBackground;
action_decline.destructive = NO;
action_decline.authenticationRequired = NO;
UIMutableUserNotificationCategory *callCategory = [UIMutableUserNotificationCategory new];
callCategory.identifier = Signal_Call_Category;
[callCategory setActions:@[action_accept, action_decline] forContext:UIUserNotificationActionContextMinimal];
[callCategory setActions:@[action_accept, action_decline] forContext:UIUserNotificationActionContextDefault];
return callCategory;
}
- (BOOL)needToRegisterForRemoteNotifications
{
return self.wantRemoteNotifications && (!UIApplication.sharedApplication.isRegisteredForRemoteNotifications);
@ -165,11 +363,11 @@
#pragma mark Register Push Notification Token with RedPhone server
- (TOCFuture *)registerForPushFutureWithToken:(NSData *)token
- (TOCFuture *)registerForPushFutureWithToken:(NSData *)pushToken voipToken:(NSData*)voipToken
{
self.registerWithServerFutureSource = [TOCFutureSource new];
[RPServerRequestsManager.sharedInstance performRequest:[RPAPICall registerPushNotificationWithPushToken:token]
[RPServerRequestsManager.sharedInstance performRequest:[RPAPICall registerPushNotificationWithPushToken:pushToken voipToken:voipToken]
success:^(NSURLSessionDataTask *task, id responseObject) {
if ([task.response isKindOfClass:NSHTTPURLResponse.class]) {
NSInteger statusCode = [(NSHTTPURLResponse *)task.response statusCode];
@ -192,4 +390,14 @@
return self.registerWithServerFutureSource.future;
}
- (BOOL)applicationIsActive {
UIApplication *app = [UIApplication sharedApplication];
if (app.applicationState == UIApplicationStateActive) {
return YES;
}
return NO;
}
@end

View File

@ -34,7 +34,7 @@ typedef NS_ENUM(NSInteger, HTTPMethod) {
+ (RPAPICall*)requestVerificationCode;
+ (RPAPICall*)requestVerificationCodeWithVoice;
+ (RPAPICall*)verifyVerificationCode:(NSString*)verificationCode;
+ (RPAPICall*)registerPushNotificationWithPushToken:(NSData*)pushToken;
+ (RPAPICall*)registerPushNotificationWithPushToken:(NSData*)pushToken voipToken:(NSData*)voipToken;
+ (RPAPICall*)requestTextSecureVerificationCode;
+ (RPAPICall*)unregisterWithPushToken:(NSData*)pushToken;
+ (RPAPICall*)fetchBloomFilter;

View File

@ -13,6 +13,7 @@
#import "SignalUtil.h"
#import "SignalKeyingStorage.h"
#import "Util.h"
#import "NSData+ows_StripToken.h"
#define CLAIMED_INTEROP_VERSION_IN_INITIATE_SIGNAL 1
@ -64,10 +65,15 @@
return apiCall;
}
+ (RPAPICall*)registerPushNotificationWithPushToken:(NSData*)pushToken {
+ (RPAPICall*)registerPushNotificationWithPushToken:(NSData*)pushToken voipToken:(NSData*)voipToken {
RPAPICall *apiCall = [self defaultAPICall];
if (voipToken) {
apiCall.parameters = @{@"voip":[voipToken ows_tripToken]};
} else {
DDLogWarn(@"No VoIP push token registered, might experience some issues while in background.");
}
apiCall.method = HTTP_PUT;
apiCall.endPoint = [NSString stringWithFormat:@"/apn/%@", pushToken.encodedAsHexString];
apiCall.endPoint = [NSString stringWithFormat:@"/apn/%@", [pushToken ows_tripToken]];
return apiCall;
}

View File

@ -60,7 +60,7 @@ typedef void(^failedVerificationBlock)(NSError *error);
#if TARGET_OS_IPHONE
+ (void)registerWithRedPhoneToken:(NSString*)tsToken pushToken:(NSData*)pushToken success:(successCompletionBlock)successBlock failure:(failedVerificationBlock)failureBlock;
+ (void)registerWithRedPhoneToken:(NSString*)tsToken pushToken:(NSData*)pushToken voipToken:(NSData*)voipToken success:(successCompletionBlock)successBlock failure:(failedVerificationBlock)failureBlock;
/**
* Register's the device's push notification token with the server
@ -68,7 +68,7 @@ typedef void(^failedVerificationBlock)(NSError *error);
* @param pushToken Apple's Push Token
*/
+ (void)registerForPushNotifications:(NSData*)pushToken success:(successCompletionBlock)success failure:(failedVerificationBlock)failureBlock;
+ (void)registerForPushNotifications:(NSData*)pushToken voipToken:(NSData*)voipToken success:(successCompletionBlock)success failure:(failedVerificationBlock)failureBlock;
#endif

View File

@ -11,6 +11,7 @@
#import "Constraints.h"
#import "NSData+Base64.h"
#import "NSData+hexString.h"
#import "NSData+ows_StripToken.h"
#import "NSURLSessionDataTask+StatusCode.h"
#import "SecurityUtils.h"
@ -71,11 +72,12 @@
return registrationID;
}
+ (void)registerForPushNotifications:(NSData *)pushToken success:(successCompletionBlock)success failure:(failedVerificationBlock)failureBlock{
+ (void)registerForPushNotifications:(NSData *)pushToken voipToken:(NSData*)voipToken success:(successCompletionBlock)success failure:(failedVerificationBlock)failureBlock{
NSString *stringToken = [[pushToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<> "]];
[[TSNetworkManager sharedManager] queueAuthenticatedRequest:[[TSRegisterForPushRequest alloc] initWithPushIdentifier:stringToken] success:^(NSURLSessionDataTask *task, id responseObject) {
NSString *pushTokenString = [pushToken ows_tripToken];
NSString *voipTokenString = [voipToken ows_tripToken];
[[TSNetworkManager sharedManager] queueAuthenticatedRequest:[[TSRegisterForPushRequest alloc] initWithPushIdentifier:pushTokenString voipIdentifier:voipTokenString] success:^(NSURLSessionDataTask *task, id responseObject) {
success();
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(@"NSError: %@", error.debugDescription);
@ -96,7 +98,12 @@
}];
}
+ (void)registerWithRedPhoneToken:(NSString*)tsToken pushToken:(NSData*)pushToken success:(successCompletionBlock)successBlock failure:(failedVerificationBlock)failureBlock{
+ (void)registerWithRedPhoneToken:(NSString*)tsToken
pushToken:(NSData*)pushToken
voipToken:(NSData*)voipToken
success:(successCompletionBlock)successBlock
failure:(failedVerificationBlock)failureBlock
{
NSString *authToken = [self generateNewAccountAuthenticationToken];
NSString *signalingKey = [self generateNewSignalingKeyToken];
@ -118,10 +125,10 @@
[TSStorageManager storeServerToken:authToken signalingKey:signalingKey phoneNumber:phoneNumber];
[self registerForPushNotifications:pushToken success:^{
[self registerForPushNotifications:pushToken voipToken:voipToken success:^{
[self registerPreKeys:^{
successBlock();
[TSSocketManager becomeActive];
[TSSocketManager becomeActiveFromForeground];
} failure:failureBlock];
} failure:^(NSError *error) {
failureBlock([self errorForRegistrationFailure:kTSRegistrationFailureNetwork HTTPStatusCode:0]);
@ -140,10 +147,11 @@
}
+ (void)registerPreKeysAfterPush:(NSData*)pushToken
voipToken:(NSData*)voipToken
success:(successCompletionBlock)successBlock
failure:(failedVerificationBlock)failureBlock
{
[self registerForPushNotifications:pushToken success:^{
[self registerForPushNotifications:pushToken voipToken:voipToken success:^{
[self registerPreKeys:successBlock
failure:failureBlock];
} failure:failureBlock];

View File

@ -27,4 +27,6 @@
- (void)handleSendToMyself:(TSOutgoingMessage*)outgoingMessage;
- (NSUInteger)unreadMessagesCount;
@end

View File

@ -23,6 +23,7 @@
#import "TSInvalidIdentityKeyReceivingErrorMessage.h"
#import "TSInfoMessage.h"
#import "TSDatabaseView.h"
#import "TSStorageManager+keyingMaterial.h"
#import "TSStorageManager+IdentityKeyStore.h"
#import "TSStorageManager+SessionStore.h"
@ -349,7 +350,7 @@
}
NSString *name = [thread name];
[self notifyUserForIncomingMessage:incomingMessage from:name];
[self notifyUserForIncomingMessage:incomingMessage from:name inThread:thread];
}];
}
@ -399,8 +400,57 @@
}];
}
- (void)notifyUserForIncomingMessage:(TSIncomingMessage*)message from:(NSString*)name {
AudioServicesPlayAlertSound(_newMessageSound);
- (NSUInteger)unreadMessagesCount {
__block NSUInteger numberOfItems;
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
numberOfItems = [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInAllGroups];
}];
return numberOfItems;
}
- (void)notifyUserForIncomingMessage:(TSIncomingMessage*)message from:(NSString*)name inThread:(TSThread*)thread {
if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive) {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.category = Signal_Message_Category;
notification.userInfo = @{Signal_Thread_UserInfo_Key:thread.uniqueId};
switch ([[Environment preferences] notificationPreviewType]) {
case NotificationNamePreview:
if ([thread isGroupThread]) {
NSString *sender = [[Environment getCurrent].contactsManager nameStringForPhoneIdentifier:message.authorId];
if (!sender) {
sender = message.authorId;
}
notification.alertBody = [NSString stringWithFormat:@"New message from %@ in group \"%@\": %@", sender, name, message.description];
} else {
notification.alertBody = [NSString stringWithFormat:@"%@: %@", name, message.description];
}
break;
case NotificationNameNoPreview:{
if ([thread isGroupThread]) {
notification.alertBody = [NSString stringWithFormat:@"%@ \"%@\"", NSLocalizedString(@"APN_MESSAGE_IN_GROUP",nil), name];
} else {
notification.alertBody = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"APN_MESSAGE_FROM", nil), name];
}
break;
}
case NotificationNoNameNoPreview:
notification.alertBody = NSLocalizedString(@"APN_Message", nil);
break;
default:
notification.alertBody = NSLocalizedString(@"APN_Message", nil);
break;
}
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
AudioServicesPlayAlertSound(_newMessageSound);
} else {
if ([Environment.preferences soundInForeground]) {
AudioServicesPlayAlertSound(_newMessageSound);
}
}
}
- (void)dealloc {

View File

@ -9,5 +9,7 @@
#import "TSRequest.h"
@interface TSRegisterForPushRequest : TSRequest
- (id) initWithPushIdentifier:(NSString*)identifier;
- (id) initWithPushIdentifier:(NSString*)identifier voipIdentifier:(NSString*)voipId;
@end

View File

@ -12,12 +12,13 @@
@implementation TSRegisterForPushRequest
- (id) initWithPushIdentifier:(NSString*)identifier{
- (id) initWithPushIdentifier:(NSString*)identifier voipIdentifier:(NSString*)voipId
{
self = [super initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/%@", textSecureAccountsAPI, @"apn"]]];
self.HTTPMethod = @"PUT";
self.parameters = [NSMutableDictionary dictionaryWithObjects:@[identifier] forKeys:@[@"apnRegistrationId"]];
self.parameters = [NSMutableDictionary dictionaryWithObjects:@[identifier, voipId] forKeys:@[@"apnRegistrationId", @"voipRegistrationId"]];
return self;
}

View File

@ -23,7 +23,9 @@ extern NSString * const SocketConnectingNotification;
@interface TSSocketManager : NSObject <SRWebSocketDelegate>
+ (void)becomeActive;
+ (void)becomeActiveFromForeground;
+ (void)becomeActiveFromBackground;
+ (void)resignActivity;
+ (void)sendNotification;

View File

@ -19,8 +19,9 @@
#import "Cryptography.h"
#import "IncomingPushMessageSignal.pb.h"
#define kWebSocketHeartBeat 30
#define kWebSocketReconnectTry 5
#define kWebSocketHeartBeat 30
#define kWebSocketReconnectTry 5
#define kBackgroundConnectTimer 120
NSString * const SocketOpenedNotification = @"SocketOpenedNotification";
NSString * const SocketClosedNotification = @"SocketClosedNotification";
@ -32,11 +33,15 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
@property (nonatomic, retain) SRWebSocket *websocket;
@property (nonatomic) SocketStatus status;
@property (nonatomic, retain) NSTimer *backgroundConnectTimer;
@property (nonatomic) UIBackgroundTaskIdentifier fetchingTaskIdentifier;
@end
@implementation TSSocketManager
- (id)init{
- (instancetype)init{
self = [super init];
if (self) {
@ -47,7 +52,7 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
return self;
}
+ (id)sharedManager {
+ (instancetype)sharedManager {
static TSSocketManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
@ -106,7 +111,11 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
#pragma mark - Delegate methods
- (void) webSocketDidOpen:(SRWebSocket *)webSocket {
self.pingTimer = [NSTimer scheduledTimerWithTimeInterval:kWebSocketHeartBeat target:self selector:@selector(webSocketHeartBeat) userInfo:nil repeats:YES];
self.pingTimer = [NSTimer scheduledTimerWithTimeInterval:kWebSocketHeartBeat
target:self
selector:@selector(webSocketHeartBeat)
userInfo:nil
repeats:YES];
self.status = kSocketStatusOpen;
[self.reconnectTimer invalidate];
@ -192,16 +201,77 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
}
}
- (NSString*)webSocketAuthenticationString{
- (NSString*)webSocketAuthenticationString {
return [NSString stringWithFormat:@"?login=%@&password=%@", [[TSAccountManager registeredNumber] stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"],[TSStorageManager serverAuthToken]];
}
- (void)scheduleRetry{
- (void)scheduleRetry {
if (!self.reconnectTimer || ![self.reconnectTimer isValid]) {
self.reconnectTimer = [NSTimer scheduledTimerWithTimeInterval:kWebSocketReconnectTry target:[self class] selector:@selector(becomeActive) userInfo:nil repeats:YES];
self.reconnectTimer = [NSTimer scheduledTimerWithTimeInterval:kWebSocketReconnectTry
target:[self class]
selector:@selector(becomeActive)
userInfo:nil
repeats:YES];
}
}
#pragma mark - Background Connect
+ (void)becomeActiveFromForeground {
TSSocketManager *sharedInstance = [self sharedManager];
[sharedInstance.backgroundConnectTimer invalidate];
sharedInstance.backgroundConnectTimer = nil;
sharedInstance.fetchingTaskIdentifier = 0;
[self becomeActive];
}
+ (void)becomeActiveFromBackground {
TSSocketManager *sharedInstance = [TSSocketManager sharedManager];
if (sharedInstance.fetchingTaskIdentifier == 0) {
sharedInstance.fetchingTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
sharedInstance.fetchingTaskIdentifier = 0;
[TSSocketManager resignActivity];
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSTimer * timer = [NSTimer timerWithTimeInterval:kBackgroundConnectTimer
target:sharedInstance
selector:@selector(closeBackgroundTask)
userInfo:nil
repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:timer
forMode:NSDefaultRunLoopMode];
});
[self becomeActive];
}
}
- (void)closeBackgroundTask {
UIBackgroundTaskIdentifier identifier = self.fetchingTaskIdentifier;
self.fetchingTaskIdentifier = 0;
if ([_websocket readyState] != SR_OPEN) {
[self backgroundConnectTimedOut];
}
[TSSocketManager resignActivity];
[[UIApplication sharedApplication] endBackgroundTask:identifier];
}
- (void)backgroundConnectTimedOut {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = NSLocalizedString(@"APN_FETCHED_FAILED", nil);
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
#pragma mark UI Delegates
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
@ -213,7 +283,7 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
}
}
- (void)notifyStatusChange{
- (void)notifyStatusChange {
switch (self.status) {
case kSocketStatusOpen:
[[NSNotificationCenter defaultCenter] postNotificationName:SocketOpenedNotification object:self];
@ -229,7 +299,7 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
}
}
+ (void)sendNotification{
+ (void)sendNotification {
[[self sharedManager] notifyStatusChange];
}

View File

@ -0,0 +1,15 @@
//
// NSData+ows_StripToken.h
// Signal
//
// Created by Frederic Jacobs on 14/04/15.
// Copyright (c) 2015 Open Whisper Systems. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSData (ows_StripToken)
- (NSString*)ows_tripToken;
@end

View File

@ -0,0 +1,19 @@
//
// NSData+ows_StripToken.m
// Signal
//
// Created by Frederic Jacobs on 14/04/15.
// Copyright (c) 2015 Open Whisper Systems. All rights reserved.
//
#import "NSData+ows_StripToken.h"
@implementation NSData (ows_StripToken)
- (NSString*)ows_tripToken {
return [[[NSString stringWithFormat:@"%@", self]
stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]]
stringByReplacingOccurrencesOfString:@" " withString:@""];
}
@end

View File

@ -78,10 +78,10 @@
[[RPServerRequestsManager sharedInstance] performRequest:[RPAPICall verifyVerificationCode:_challengeTextField.text]
success:^(NSURLSessionDataTask *task, id responseObject) {
[PushManager.sharedManager registrationAndRedPhoneTokenRequestWithSuccess:^(NSData *pushToken,
NSString *signupToken) {
[TSAccountManager registerWithRedPhoneToken:signupToken
[PushManager.sharedManager registrationAndRedPhoneTokenRequestWithSuccess:^(NSData *pushToken, NSData *voipToken, NSString *signupToken) {
[TSAccountManager registerWithRedPhoneToken:signupToken
pushToken:pushToken
voipToken:voipToken
success:^{
success();
}

View File

@ -5,6 +5,10 @@
#import "PhoneNumber.h"
#import "PhoneNumberDirectoryFilterManager.h"
#define PICK_UP_NOTIFICATION @"RedPhoneCallPickUpNotification"
#define HANG_UP_NOTIFICATION @"RedPhoneCallHangUpNotification"
@interface InCallViewController : UIViewController
@property (nonatomic, strong) IBOutlet UIView *conversationContactView;

View File

@ -418,7 +418,7 @@
NSString *identifier = [[[self contactForIndexPath:indexPath] textSecureIdentifiers] firstObject];
[self dismissViewControllerAnimated:YES completion:^(){
[Environment messageIdentifier:identifier];
[Environment messageIdentifier:identifier withCompose:YES];
}];
}

View File

@ -28,4 +28,6 @@
- (void)setupWithTSIdentifier:(NSString*)identifier;
- (void)setupWithTSGroup:(TSGroupModel*)model;
- (void)setComposeOnOpen:(BOOL)compose;
@end

View File

@ -108,6 +108,7 @@ typedef enum : NSUInteger {
@property NSUInteger page;
@property BOOL isVisible;
@property (nonatomic) BOOL composeOnOpen;
@end
@ -135,6 +136,10 @@ typedef enum : NSUInteger {
isGroupConversation = YES;
}
- (void)setComposeOnOpen:(BOOL)compose {
_composeOnOpen = compose;
}
- (void)setupWithThread:(TSThread *)thread {
self.thread = thread;
isGroupConversation = [self.thread isKindOfClass:[TSGroupThread class]];
@ -232,9 +237,14 @@ typedef enum : NSUInteger {
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self markAllMessagesAsRead];
[self startReadTimer];
_isVisible = YES;
[self initializeTitleLabelGestureRecognizer];
if (_composeOnOpen) {
[self popKeyBoard];
}
}
- (void)viewWillDisappear:(BOOL)animated {
@ -1593,6 +1603,10 @@ typedef enum : NSUInteger {
[self.collectionView reloadData];
}
- (void)popKeyBoard {
[self.inputToolbar.contentView.textView becomeFirstResponder];
}
- (void)dismissKeyBoard {
[self.inputToolbar.contentView.textView resignFirstResponder];
}

View File

@ -119,7 +119,7 @@ static NSString* const kUnwindToMessagesViewSegue = @"UnwindToMessagesViewSegue"
#pragma mark - Actions
-(void)createGroup {
TSGroupModel* model = [self makeGroup];
[Environment messageGroupModel:model];
[Environment messageGroupModel:model withCompose:YES];
}

View File

@ -0,0 +1,13 @@
//
// NotificationSettingsOptionsViewController.h
// Signal
//
// Created by Frederic Jacobs on 24/04/15.
// Copyright (c) 2015 Open Whisper Systems. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface NotificationSettingsOptionsViewController : UITableViewController
@end

View File

@ -0,0 +1,55 @@
//
// NotificationSettingsOptionsViewController.m
// Signal
//
// Created by Frederic Jacobs on 24/04/15.
// Copyright (c) 2015 Open Whisper Systems. All rights reserved.
//
#import "NotificationSettingsOptionsViewController.h"
#import "Environment.h"
#import "PropertyListPreferences.h"
#import "PreferencesUtil.h"
@interface NotificationSettingsOptionsViewController ()
@property NSArray *options;
@end
@implementation NotificationSettingsOptionsViewController
- (void)viewDidLoad
{
self.options = @[@(NotificationNamePreview),
@(NotificationNameNoPreview),
@(NotificationNoNameNoPreview)];
[super viewDidLoad];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return (NSInteger)[self.options count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"NotificationSettingsOption"];
PropertyListPreferences *prefs = [Environment preferences];
[[cell textLabel] setText:[prefs nameForNotificationPreviewType:[[self.options objectAtIndex:(NSUInteger)indexPath.row] unsignedIntegerValue]]];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[Environment.preferences setNotificationPreviewType:[[self.options objectAtIndex:(NSUInteger)indexPath.row] unsignedIntegerValue]];
[self.navigationController popViewControllerAnimated:YES];
}
@end

View File

@ -0,0 +1,13 @@
//
// NotificationPreviewViewController.h
// Signal
//
// Created by Dylan Bourgeois on 09/12/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface NotificationSettingsViewController : UITableViewController
@end

View File

@ -0,0 +1,110 @@
//
// NotificationPreviewViewController.m
// Signal
//
// Created by Frederic Jacobs on 09/12/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
//
#import "NotificationSettingsViewController.h"
#import "Environment.h"
#import "PreferencesUtil.h"
#import "NotificationSettingsOptionsViewController.h"
#import "UIUtil.h"
@interface NotificationSettingsViewController ()
@property NSArray *notificationsSections;
@end
@implementation NotificationSettingsViewController
- (instancetype)init {
return [super initWithStyle:UITableViewStyleGrouped];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self setTitle:NSLocalizedString(@"SETTINGS_NOTIFICATIONS", nil)];
self.notificationsSections = @[NSLocalizedString(@"NOTIFICATIONS_SECTION_BACKGROUND", nil),
NSLocalizedString(@"NOTIFICATIONS_SECTION_INAPP",nil)];
}
- (void)viewDidAppear:(BOOL)animated
{
[self.tableView reloadData];
}
#pragma mark - Table view data source
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
return [self.notificationsSections objectAtIndex:(NSUInteger)section];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return (NSInteger)self.notificationsSections.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellIdentifier = @"SignalTableViewCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier];
}
PropertyListPreferences *prefs = Environment.preferences;
if (indexPath.section == 0) {
NotificationType notifType = [prefs notificationPreviewType];
NSString *detailString = [prefs nameForNotificationPreviewType:notifType];
[[cell textLabel]setText:NSLocalizedString(@"NOTIFICATIONS_SHOW", nil)];
[[cell detailTextLabel] setText:detailString];
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
} else {
BOOL soundEnabled = [prefs soundInForeground];
[[cell textLabel]setText:NSLocalizedString(@"NOTIFICATIONS_SOUND", nil)];
[[cell detailTextLabel] setText:nil];
UISwitch *switchv = [[UISwitch alloc] initWithFrame:CGRectZero];
switchv.on = soundEnabled;
[switchv addTarget:self
action:@selector(didToggleSoundNotificationsSwitch:)
forControlEvents:UIControlEventValueChanged];
cell.accessoryView = switchv;
}
return cell;
}
- (void)didToggleSoundNotificationsSwitch:(UISwitch*)sender
{
[Environment.preferences setSoundInForeground:sender.on];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NotificationSettingsOptionsViewController *vc = [[NotificationSettingsOptionsViewController alloc] initWithStyle:UITableViewStyleGrouped];
[self.navigationController pushViewController:vc animated:YES];
}
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryNone;
}
@end

View File

@ -16,6 +16,7 @@
@property IBOutlet UILabel *networkStatusHeader;
@property IBOutlet UILabel *settingsPrivacyTitle;
@property IBOutlet UILabel *settingsNotifications;
@property IBOutlet UILabel *settingsAdvancedTitle;
@property IBOutlet UILabel *settingsAboutTitle;
@property IBOutlet UIButton *destroyAccountButton;

View File

@ -37,6 +37,7 @@
#import "AdvancedSettingsTableViewController.h"
#import "AboutTableViewController.h"
#import "PushManager.h"
#import "NotificationSettingsViewController.h"
#define kProfileCellHeight 87.0f
#define kStandardCellHeight 44.0f
@ -45,22 +46,23 @@
#define kRegisteredNumberRow 0
#define kPrivacyRow 0
#define kAdvancedRow 1
#define kAboutRow 2
#define kNotificationRow 1
#define kAdvancedRow 2
#define kAboutRow 3
#define kNetworkRow 0
#define kUnregisterRow 0
typedef enum {
kRegisteredRows = 1,
kGeneralRows = 3,
kGeneralRows = 4,
kNetworkStatusRows = 1,
kUnregisterRows = 1,
} kRowsForSection;
typedef enum {
kRegisteredNumberSection=0,
kGeneralSection=2,
kNetworkStatusSection=1,
kGeneralSection=2,
kUnregisterSection=3,
} kSection;
@ -86,6 +88,7 @@ typedef enum {
_settingsPrivacyTitle.text = NSLocalizedString(@"SETTINGS_PRIVACY_TITLE",@"");
_settingsAdvancedTitle.text = NSLocalizedString(@"SETTINGS_ADVANCED_TITLE",@"");
_settingsAboutTitle.text = NSLocalizedString(@"SETTINGS_ABOUT",@"");
_settingsNotifications.text = NSLocalizedString(@"SETTINGS_NOTIFICATIONS", nil);
[_destroyAccountButton setTitle:NSLocalizedString(@"SETTINGS_DELETE_ACCOUNT_BUTTON", @"") forState:UIControlStateNormal];
}
@ -134,15 +137,21 @@ typedef enum {
switch (indexPath.row) {
case kPrivacyRow:
{
PrivacySettingsTableViewController * vc = [[PrivacySettingsTableViewController alloc]init];
PrivacySettingsTableViewController *vc = [[PrivacySettingsTableViewController alloc]init];
NSAssert(self.navigationController != nil, @"Navigation controller must not be nil");
NSAssert(vc != nil, @"Privacy Settings View Controller must not be nil");
[self.navigationController pushViewController:vc animated:YES];
break;
}
case kNotificationRow:
{
NotificationSettingsViewController *vc = [[NotificationSettingsViewController alloc] init];
[self.navigationController pushViewController:vc animated:YES];
break;
}
case kAdvancedRow:
{
AdvancedSettingsTableViewController * vc = [[AdvancedSettingsTableViewController alloc]init];
AdvancedSettingsTableViewController *vc = [[AdvancedSettingsTableViewController alloc]init];
NSAssert(self.navigationController != nil, @"Navigation controller must not be nil");
NSAssert(vc != nil, @"Advanced Settings View Controller must not be nil");
[self.navigationController pushViewController:vc animated:YES];
@ -193,7 +202,7 @@ typedef enum {
- (void)proceedToUnregistration{
[TSAccountManager unregisterTextSecureWithSuccess:^{
[PushManager.sharedManager requestPushTokenWithSuccess:^(NSData* pushToken){
[PushManager.sharedManager requestPushTokenWithSuccess:^(NSData* pushToken, NSData *voipToken){
[[RPServerRequestsManager sharedInstance]performRequest:[RPAPICall unregisterWithPushToken:pushToken] success:^(NSURLSessionDataTask *task, id responseObject) {
[Environment resetAppData];
exit(0);

View File

@ -15,8 +15,10 @@
@interface SignalsViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, TableViewCellDelegate>
@property (nonatomic) NSString *contactIdentifierFromCompose;
@property (nonatomic) TSGroupModel *groupFromCompose;
@property (nonatomic) NSString *contactIdentifierFromCompose;
@property (nonatomic) TSGroupModel *groupFromCompose;
@property (nonatomic) BOOL composeMessage;
@property (nonatomic, retain) IBOutlet UITableView *tableView;
@property (nonatomic, retain) IBOutlet UIButton *inboxButton;
@property (nonatomic, retain) IBOutlet UIButton *archiveButton;

View File

@ -211,12 +211,7 @@ static NSString* const kShowSignupFlowSegue = @"showSignupFlow";
-(void) updateInboxCountLabel {
__block NSUInteger numberOfItems;
[_editingDbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
numberOfItems = [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInAllGroups];
}];
NSUInteger numberOfItems = [[TSMessagesManager sharedManager] unreadMessagesCount];
NSNumber *badgeNumber = [NSNumber numberWithUnsignedInteger:numberOfItems];
NSString *badgeValue = nil;
@ -224,6 +219,7 @@ static NSString* const kShowSignupFlowSegue = @"showSignupFlow";
badgeValue = [badgeNumber stringValue];
}
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:badgeNumber.integerValue];
self.inboxCountLabel.text = badgeValue;
}
@ -235,23 +231,30 @@ static NSString* const kShowSignupFlowSegue = @"showSignupFlow";
#pragma mark - Navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:kSegueIndentifier]){
MessagesViewController * vc = [segue destinationViewController];
NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow];
TSThread *thread = [self threadForIndexPath:selectedIndexPath];
if (self.contactIdentifierFromCompose){
[vc setupWithTSIdentifier:self.contactIdentifierFromCompose];
[vc setComposeOnOpen:self.composeMessage];
self.contactIdentifierFromCompose = nil;
self.composeMessage = NO;
}
else if (self.groupFromCompose) {
[vc setupWithTSGroup:self.groupFromCompose];
[vc setComposeOnOpen:self.composeMessage];
self.groupFromCompose = nil;
self.composeMessage = NO;
}
else if (thread) {
[vc setupWithThread:thread];
[vc setComposeOnOpen:NO];
}
else if([sender isKindOfClass:[TSGroupThread class]]){
[vc setupWithThread:sender];
[vc setComposeOnOpen:YES];
}
}
else if ([segue.identifier isEqualToString:kCallSegue]) {
InCallViewController* vc = [segue destinationViewController];