UX and Notifications fixes

- Removes large confusing UX bar and related assets. Replaced with UISwitch.
- Enhanced user experience for missed calls.
- Fixes issue where missed call would appear as incoming call in call log.
- Fixing issues where PushKit handler not called on launch.
This commit is contained in:
Frederic Jacobs 2015-05-23 15:54:50 +02:00
parent 93de0a4326
commit 57f86008d1
76 changed files with 404 additions and 2768 deletions

View File

@ -898,7 +898,6 @@
B633C54C1A1D190B0059AC12 /* quit@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "quit@2x.png"; sourceTree = "<group>"; };
B633C5501A1D190B0059AC12 /* savephoto@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "savephoto@2x.png"; sourceTree = "<group>"; };
B634CBB31AB10D2300C49B99 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = translations/hr.lproj/Localizable.strings; sourceTree = "<group>"; };
B634CBB41AB10D3700C49B99 /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = translations/fa.lproj/Localizable.strings; sourceTree = "<group>"; };
B634CBB51AB10D5400C49B99 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = translations/ro.lproj/Localizable.strings; sourceTree = "<group>"; };
B63761E119E1F487005735D1 /* AFHTTPSessionManager+SignalMethods.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "AFHTTPSessionManager+SignalMethods.h"; sourceTree = "<group>"; };
B63761E219E1F487005735D1 /* AFHTTPSessionManager+SignalMethods.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "AFHTTPSessionManager+SignalMethods.m"; sourceTree = "<group>"; };
@ -942,7 +941,6 @@
B640C4761A477B0F005C7C8A /* TSAttachementsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSAttachementsTest.m; sourceTree = "<group>"; };
B646D10E1AA5461A004133BA /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = translations/fr.lproj/Localizable.strings; sourceTree = "<group>"; };
B646D10F1AA54626004133BA /* fil */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fil; path = translations/fil.lproj/Localizable.strings; sourceTree = "<group>"; };
B646D1131AA54667004133BA /* hi_IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hi_IN; path = translations/hi_IN.lproj/Localizable.strings; sourceTree = "<group>"; };
B646D1141AA54674004133BA /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = translations/hu.lproj/Localizable.strings; sourceTree = "<group>"; };
B646D1151AA5467E004133BA /* it_IT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it_IT; path = translations/it_IT.lproj/Localizable.strings; sourceTree = "<group>"; };
B65031CE1A7862AA002EBBBD /* SignedPreKeyDeletionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SignedPreKeyDeletionTests.m; sourceTree = "<group>"; };
@ -982,7 +980,6 @@
B68CB7E21AA5485B0065AC3F /* sv_SE */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv_SE; path = translations/sv_SE.lproj/Localizable.strings; sourceTree = "<group>"; };
B68CB7E31AA548660065AC3F /* th_TH */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th_TH; path = translations/th_TH.lproj/Localizable.strings; sourceTree = "<group>"; };
B68CB7E41AA548700065AC3F /* tr_TR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr_TR; path = translations/tr_TR.lproj/Localizable.strings; sourceTree = "<group>"; };
B68CB7E51AA5487D0065AC3F /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = translations/uk.lproj/Localizable.strings; sourceTree = "<group>"; };
B68CB7E61AA548870065AC3F /* zh_CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh_CN; path = translations/zh_CN.lproj/Localizable.strings; sourceTree = "<group>"; };
B692BF051A76EF0F002786DA /* TSDatabaseSecondaryIndexes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSDatabaseSecondaryIndexes.h; sourceTree = "<group>"; };
B692BF061A76EF0F002786DA /* TSDatabaseSecondaryIndexes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSDatabaseSecondaryIndexes.m; sourceTree = "<group>"; };
@ -1090,7 +1087,6 @@
B6C93C4D199567AD00EDF894 /* DebugLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugLogger.m; sourceTree = "<group>"; };
B6E314C71A38FAAF00A41AFB /* TSFingerprintGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSFingerprintGenerator.h; sourceTree = "<group>"; };
B6E314C81A38FAAF00A41AFB /* TSFingerprintGenerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSFingerprintGenerator.m; sourceTree = "<group>"; };
B6E80FA61AA5453C0000253B /* et_EE */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = et_EE; path = translations/et_EE.lproj/Localizable.strings; sourceTree = "<group>"; };
B6F509961AA53F760068F56A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = translations/en.lproj/Localizable.strings; sourceTree = "<group>"; };
B6FAAAE61A41BC6C007FEC1D /* TSAttachmentPointer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSAttachmentPointer.h; path = Attachements/TSAttachmentPointer.h; sourceTree = "<group>"; };
B6FAAAE71A41BC6C007FEC1D /* TSAttachmentPointer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSAttachmentPointer.m; path = Attachements/TSAttachmentPointer.m; sourceTree = "<group>"; };
@ -2793,10 +2789,8 @@
sv_SE,
th_TH,
tr_TR,
uk,
zh_CN,
hr,
fa,
ro,
);
mainGroup = D221A07E169C9E5E00537ABF;
@ -3435,11 +3429,9 @@
B676BCEF1AA544E7009637B8 /* de */,
B676BCF01AA544F5009637B8 /* el_GR */,
B676BCF11AA5451E009637B8 /* es */,
B6E80FA61AA5453C0000253B /* et_EE */,
B63FBC9E1AA545CB00548746 /* fi */,
B646D10E1AA5461A004133BA /* fr */,
B646D10F1AA54626004133BA /* fil */,
B646D1131AA54667004133BA /* hi_IN */,
B646D1141AA54674004133BA /* hu */,
B646D1151AA5467E004133BA /* it_IT */,
B60341CD1AA5469800A01E42 /* ja_JP */,
@ -3455,10 +3447,8 @@
B68CB7E21AA5485B0065AC3F /* sv_SE */,
B68CB7E31AA548660065AC3F /* th_TH */,
B68CB7E41AA548700065AC3F /* tr_TR */,
B68CB7E51AA5487D0065AC3F /* uk */,
B68CB7E61AA548870065AC3F /* zh_CN */,
B634CBB31AB10D2300C49B99 /* hr */,
B634CBB41AB10D3700C49B99 /* fa */,
B634CBB51AB10D5400C49B99 /* ro */,
);
name = Localizable.strings;
@ -3607,8 +3597,8 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
CURRENT_PROJECT_VERSION = 1;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@ -3662,8 +3652,8 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
CURRENT_PROJECT_VERSION = 1;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",

View File

@ -65,7 +65,8 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<BuildableProductRunnable>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D221A088169C9E5E00537ABF"
@ -83,7 +84,8 @@
useCustomWorkingDirectory = "NO"
buildConfiguration = "App Store Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D221A088169C9E5E00537ABF"

View File

@ -82,5 +82,8 @@
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"pre-rendered" : true
}
}

View File

@ -1,12 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "archive_active.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,12 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "archive_inactive.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,12 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "inbox_active.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,12 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "inbox_inactive.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

File diff suppressed because one or more lines are too long

View File

@ -40,7 +40,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>2.1.0</string>
<string>2.1.35</string>
<key>LOGS_EMAIL</key>
<string>support@whispersystems.org</string>
<key>LOGS_URL</key>

View File

@ -37,7 +37,6 @@
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self setupAppearance];
[[PushManager sharedManager] registerPushKitNotificationFuture];
if (getenv("runningTests_dontStartApp")) {
@ -89,9 +88,14 @@
[self prepareScreenshotProtection];
if ([TSAccountManager isRegistered]) {
if ([self applicationIsActive]) {
if (application.applicationState == UIApplicationStateInactive) {
[TSSocketManager becomeActiveFromForeground];
} else if (application.applicationState == UIApplicationStateBackground) {
[TSSocketManager becomeActiveFromBackgroundExpectMessage:NO];
} else {
DDLogWarn(@"The app was launched in an unknown way");
}
[[PushManager sharedManager] validateUserNotificationSettings];
[self refreshContacts];
[TSPreKeyManager refreshPreKeys];
@ -157,7 +161,6 @@
[self protectScreen];
if ([TSAccountManager isRegistered]) {
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
[self updateBadge];
[TSSocketManager resignActivity];
}
@ -165,6 +168,8 @@
- (void)updateBadge {
if ([TSAccountManager isRegistered]) {
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:1];
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:(NSInteger)[[TSMessagesManager sharedManager] unreadMessagesCount]];
}
}

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="7702" systemVersion="14D136" 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="7706" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="tuk-0x-yCb">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7701"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
<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"/>
@ -19,219 +19,6 @@
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="uoq-UX-3fJ" userLabel="toolbar like view">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DfA-eN-PyK" userLabel="box icon">
<rect key="frame" x="-23" y="-15" width="46" height="30"/>
<constraints>
<constraint firstAttribute="width" constant="66" id="XZ0-7V-0w0"/>
<constraint firstAttribute="height" constant="66" id="a8F-hg-6gc"/>
<constraint firstAttribute="width" constant="180" id="sZf-gg-kCA"/>
</constraints>
<state key="normal" image="inbox-inactive@1x">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<state key="selected" image="inbox-active@1x"/>
<state key="highlighted" image="inbox-active@1x"/>
<variation key="default">
<mask key="constraints">
<exclude reference="XZ0-7V-0w0"/>
<exclude reference="a8F-hg-6gc"/>
<exclude reference="sZf-gg-kCA"/>
</mask>
</variation>
<variation key="heightClass=regular-widthClass=compact">
<mask key="constraints">
<include reference="XZ0-7V-0w0"/>
<include reference="a8F-hg-6gc"/>
<exclude reference="sZf-gg-kCA"/>
</mask>
</variation>
<connections>
<action selector="selectedInbox:" destination="MY2-bB-USa" eventType="touchUpInside" id="mFX-FS-Ofg"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="WVY-Zl-dpP">
<rect key="frame" x="-23" y="-15" width="46" height="30"/>
<constraints>
<constraint firstAttribute="width" constant="66" id="alU-LZ-piL"/>
<constraint firstAttribute="height" constant="66" id="mRu-04-FYf"/>
<constraint firstAttribute="width" constant="180" id="s5u-fl-v0s"/>
<constraint firstAttribute="height" constant="66" id="sc4-SD-Hae"/>
</constraints>
<state key="normal" image="archive-inactive@1x">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<state key="selected" image="archive-active@1x"/>
<state key="highlighted" image="archive-active@1x"/>
<variation key="default">
<mask key="constraints">
<exclude reference="alU-LZ-piL"/>
<exclude reference="mRu-04-FYf"/>
<exclude reference="s5u-fl-v0s"/>
<exclude reference="sc4-SD-Hae"/>
</mask>
</variation>
<variation key="heightClass=regular-widthClass=compact">
<mask key="constraints">
<include reference="alU-LZ-piL"/>
<exclude reference="mRu-04-FYf"/>
<exclude reference="s5u-fl-v0s"/>
<include reference="sc4-SD-Hae"/>
</mask>
</variation>
<connections>
<action selector="selectedArchive:" destination="MY2-bB-USa" eventType="touchUpInside" id="Spp-Ns-lKG"/>
</connections>
</button>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="hSN-Ka-PzS" userLabel="Line">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<rect key="contentStretch" x="0.0" y="0.0" width="1" height="0.5"/>
<constraints>
<constraint firstAttribute="height" id="WuF-7s-PGe">
<variation key="heightClass=regular-widthClass=compact" constant="1"/>
</constraint>
</constraints>
<variation key="default">
<mask key="constraints">
<exclude reference="WuF-7s-PGe"/>
</mask>
</variation>
<variation key="heightClass=regular-widthClass=compact">
<mask key="constraints">
<include reference="WuF-7s-PGe"/>
</mask>
</variation>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="4" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GV1-fp-iPR">
<rect key="frame" x="0.0" y="-21" width="42" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="24" id="HGR-Rl-oe8"/>
<constraint firstAttribute="height" constant="21" id="lqu-cE-Dfh"/>
</constraints>
<fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="15"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
<variation key="default">
<mask key="constraints">
<exclude reference="HGR-Rl-oe8"/>
<exclude reference="lqu-cE-Dfh"/>
</mask>
</variation>
<variation key="heightClass=regular-widthClass=compact">
<mask key="constraints">
<include reference="HGR-Rl-oe8"/>
<include reference="lqu-cE-Dfh"/>
</mask>
</variation>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Q8M-tN-1ve">
<rect key="frame" x="20" y="60" width="400" height="1"/>
<color key="backgroundColor" red="0.80000000000000004" green="0.80000000000000004" blue="0.80000000000000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<variation key="heightClass=regular" ambiguous="YES">
<rect key="frame" x="0.0" y="0.0" width="0.0" height="0.0"/>
</variation>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="Q8M-tN-1ve" firstAttribute="bottom" secondItem="hSN-Ka-PzS" secondAttribute="bottom" id="2lB-4O-ZD9"/>
<constraint firstItem="WVY-Zl-dpP" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="uoq-UX-3fJ" secondAttribute="leading" constant="220" id="5YJ-Oq-Roj"/>
<constraint firstAttribute="trailing" secondItem="hSN-Ka-PzS" secondAttribute="trailing" id="615-3N-9QN"/>
<constraint firstItem="WVY-Zl-dpP" firstAttribute="top" secondItem="uoq-UX-3fJ" secondAttribute="top" id="77j-Xx-EHO"/>
<constraint firstAttribute="trailing" secondItem="WVY-Zl-dpP" secondAttribute="trailing" constant="66" id="8iQ-re-cvy"/>
<constraint firstAttribute="bottom" secondItem="Q8M-tN-1ve" secondAttribute="bottom" constant="65" id="8x2-HZ-R58"/>
<constraint firstItem="Q8M-tN-1ve" firstAttribute="top" secondItem="DfA-eN-PyK" secondAttribute="top" id="99q-Q4-CHy"/>
<constraint firstAttribute="centerX" secondItem="hSN-Ka-PzS" secondAttribute="centerX" id="AGV-i5-F3D"/>
<constraint firstAttribute="trailing" secondItem="Q8M-tN-1ve" secondAttribute="trailing" constant="-40" id="BOJ-NL-FPg"/>
<constraint firstAttribute="bottom" secondItem="WVY-Zl-dpP" secondAttribute="bottom" id="HWa-M6-gjL"/>
<constraint firstItem="Q8M-tN-1ve" firstAttribute="top" secondItem="uoq-UX-3fJ" secondAttribute="top" id="INk-NO-2j1"/>
<constraint firstItem="hSN-Ka-PzS" firstAttribute="top" secondItem="uoq-UX-3fJ" secondAttribute="top" id="Pq1-0y-L80"/>
<constraint firstAttribute="bottom" secondItem="DfA-eN-PyK" secondAttribute="bottom" id="SJd-sZ-n13"/>
<constraint firstItem="DfA-eN-PyK" firstAttribute="centerX" secondItem="GV1-fp-iPR" secondAttribute="centerX" id="UgY-Jr-rPo"/>
<constraint firstItem="DfA-eN-PyK" firstAttribute="centerX" secondItem="GV1-fp-iPR" secondAttribute="centerX" id="WOe-oP-TIu"/>
<constraint firstAttribute="trailing" secondItem="WVY-Zl-dpP" secondAttribute="trailing" id="YSd-C9-yEn"/>
<constraint firstItem="WVY-Zl-dpP" firstAttribute="leading" secondItem="uoq-UX-3fJ" secondAttribute="leading" constant="220" id="Ydg-FL-fwr"/>
<constraint firstItem="hSN-Ka-PzS" firstAttribute="leading" secondItem="uoq-UX-3fJ" secondAttribute="leading" id="cJB-rq-7bY"/>
<constraint firstItem="Q8M-tN-1ve" firstAttribute="leading" secondItem="hSN-Ka-PzS" secondAttribute="leading" id="eIX-uj-15n"/>
<constraint firstItem="DfA-eN-PyK" firstAttribute="top" secondItem="GV1-fp-iPR" secondAttribute="top" constant="-15" id="fp7-UH-t0O">
<variation key="heightClass=regular-widthClass=compact" constant="-21"/>
</constraint>
<constraint firstAttribute="height" constant="66" id="lM2-AA-dzm"/>
<constraint firstItem="DfA-eN-PyK" firstAttribute="top" secondItem="GV1-fp-iPR" secondAttribute="top" constant="-18" id="lZW-eB-ucq"/>
<constraint firstItem="DfA-eN-PyK" firstAttribute="leading" secondItem="uoq-UX-3fJ" secondAttribute="leading" constant="66" id="tIb-ee-zav"/>
</constraints>
<variation key="default">
<mask key="subviews">
<exclude reference="DfA-eN-PyK"/>
<exclude reference="WVY-Zl-dpP"/>
<exclude reference="hSN-Ka-PzS"/>
<exclude reference="GV1-fp-iPR"/>
<exclude reference="Q8M-tN-1ve"/>
</mask>
<mask key="constraints">
<exclude reference="lM2-AA-dzm"/>
<exclude reference="SJd-sZ-n13"/>
<exclude reference="UgY-Jr-rPo"/>
<exclude reference="WOe-oP-TIu"/>
<exclude reference="fp7-UH-t0O"/>
<exclude reference="lZW-eB-ucq"/>
<exclude reference="tIb-ee-zav"/>
<exclude reference="5YJ-Oq-Roj"/>
<exclude reference="77j-Xx-EHO"/>
<exclude reference="8iQ-re-cvy"/>
<exclude reference="HWa-M6-gjL"/>
<exclude reference="YSd-C9-yEn"/>
<exclude reference="Ydg-FL-fwr"/>
<exclude reference="2lB-4O-ZD9"/>
<exclude reference="8x2-HZ-R58"/>
<exclude reference="99q-Q4-CHy"/>
<exclude reference="BOJ-NL-FPg"/>
<exclude reference="INk-NO-2j1"/>
<exclude reference="eIX-uj-15n"/>
<exclude reference="615-3N-9QN"/>
<exclude reference="AGV-i5-F3D"/>
<exclude reference="Pq1-0y-L80"/>
<exclude reference="cJB-rq-7bY"/>
</mask>
</variation>
<variation key="heightClass=regular-widthClass=compact">
<mask key="subviews">
<include reference="DfA-eN-PyK"/>
<include reference="WVY-Zl-dpP"/>
<include reference="hSN-Ka-PzS"/>
<include reference="GV1-fp-iPR"/>
<include reference="Q8M-tN-1ve"/>
</mask>
<mask key="constraints">
<include reference="lM2-AA-dzm"/>
<include reference="SJd-sZ-n13"/>
<exclude reference="UgY-Jr-rPo"/>
<include reference="WOe-oP-TIu"/>
<include reference="fp7-UH-t0O"/>
<exclude reference="lZW-eB-ucq"/>
<include reference="tIb-ee-zav"/>
<exclude reference="5YJ-Oq-Roj"/>
<include reference="77j-Xx-EHO"/>
<include reference="8iQ-re-cvy"/>
<exclude reference="HWa-M6-gjL"/>
<exclude reference="YSd-C9-yEn"/>
<exclude reference="Ydg-FL-fwr"/>
<include reference="2lB-4O-ZD9"/>
<exclude reference="8x2-HZ-R58"/>
<include reference="99q-Q4-CHy"/>
<include reference="BOJ-NL-FPg"/>
<exclude reference="INk-NO-2j1"/>
<include reference="eIX-uj-15n"/>
<include reference="615-3N-9QN"/>
<include reference="AGV-i5-F3D"/>
<include reference="Pq1-0y-L80"/>
<include reference="cJB-rq-7bY"/>
</mask>
</variation>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="lKc-rv-FH5" userLabel="empty state view">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<subviews>
@ -261,7 +48,7 @@
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="centerX" secondItem="Srx-i1-WhD" secondAttribute="centerX" id="JId-fq-hNc"/>
<constraint firstItem="Srx-i1-WhD" firstAttribute="top" secondItem="lKc-rv-FH5" secondAttribute="top" constant="240" id="uWX-dT-9XP"/>
<constraint firstAttribute="centerY" secondItem="Srx-i1-WhD" secondAttribute="centerY" constant="95" id="XST-YY-qXT"/>
</constraints>
<variation key="default">
<mask key="subviews">
@ -269,7 +56,7 @@
</mask>
<mask key="constraints">
<exclude reference="JId-fq-hNc"/>
<exclude reference="uWX-dT-9XP"/>
<exclude reference="XST-YY-qXT"/>
</mask>
</variation>
<variation key="heightClass=regular-widthClass=compact">
@ -278,7 +65,7 @@
</mask>
<mask key="constraints">
<include reference="JId-fq-hNc"/>
<include reference="uWX-dT-9XP"/>
<include reference="XST-YY-qXT"/>
</mask>
</variation>
</view>
@ -304,94 +91,63 @@
</tableView>
</subviews>
<constraints>
<constraint firstAttribute="centerX" secondItem="uoq-UX-3fJ" secondAttribute="centerX" constant="-5" id="6gc-oZ-IMJ"/>
<constraint firstItem="7uh-gm-z8v" firstAttribute="top" secondItem="uoq-UX-3fJ" secondAttribute="bottom" id="7iL-hd-xP7"/>
<constraint firstAttribute="width" secondItem="uoq-UX-3fJ" secondAttribute="width" id="AP2-DE-zts"/>
<constraint firstItem="PaA-ol-uQT" firstAttribute="leading" secondItem="lKc-rv-FH5" secondAttribute="leading" id="0co-lj-Jsm"/>
<constraint firstAttribute="width" secondItem="lKc-rv-FH5" secondAttribute="width" id="Bu7-qv-yue"/>
<constraint firstAttribute="width" secondItem="lKc-rv-FH5" secondAttribute="width" id="EYc-tW-W2D">
<variation key="heightClass=regular-widthClass=compact" constant="0.0"/>
</constraint>
<constraint firstAttribute="trailingMargin" secondItem="PaA-ol-uQT" secondAttribute="trailing" constant="-16" id="EdI-ia-IjN">
<variation key="heightClass=regular-widthClass=compact" constant="-26"/>
</constraint>
<constraint firstAttribute="trailingMargin" secondItem="uoq-UX-3fJ" secondAttribute="trailing" constant="-16" id="Fc5-tN-hro">
<variation key="heightClass=regular-widthClass=compact" constant="-16"/>
</constraint>
<constraint firstItem="7uh-gm-z8v" firstAttribute="top" secondItem="uoq-UX-3fJ" secondAttribute="bottom" id="GCa-EH-8Vh"/>
<constraint firstItem="lKc-rv-FH5" firstAttribute="top" secondItem="lZK-q4-iwt" secondAttribute="bottom" id="KiY-Ag-0iP"/>
<constraint firstItem="uoq-UX-3fJ" firstAttribute="leading" secondItem="EFA-Fu-XJm" secondAttribute="leadingMargin" constant="-16" id="La3-rh-BpY"/>
<constraint firstItem="PaA-ol-uQT" firstAttribute="leading" secondItem="EFA-Fu-XJm" secondAttribute="leadingMargin" constant="-16" id="OD6-Pc-Zug"/>
<constraint firstAttribute="centerX" secondItem="uoq-UX-3fJ" secondAttribute="centerX" id="fEl-Ut-x0g"/>
<constraint firstItem="7uh-gm-z8v" firstAttribute="top" secondItem="lKc-rv-FH5" secondAttribute="bottom" constant="66" id="lCx-f0-NYv"/>
<constraint firstItem="lKc-rv-FH5" firstAttribute="centerY" secondItem="PaA-ol-uQT" secondAttribute="centerY" id="OOk-XM-IMs"/>
<constraint firstAttribute="trailingMargin" secondItem="PaA-ol-uQT" secondAttribute="trailing" constant="-26" id="ZkG-Yf-ISG"/>
<constraint firstItem="PaA-ol-uQT" firstAttribute="top" secondItem="lKc-rv-FH5" secondAttribute="top" id="gdn-tV-9au"/>
<constraint firstItem="PaA-ol-uQT" firstAttribute="bottom" secondItem="7uh-gm-z8v" secondAttribute="top" id="jLN-Fv-npv"/>
<constraint firstAttribute="trailingMargin" secondItem="lKc-rv-FH5" secondAttribute="trailing" constant="-26" id="luy-6c-SFS">
<variation key="heightClass=regular-widthClass=compact" constant="-10"/>
</constraint>
<constraint firstAttribute="trailingMargin" secondItem="PaA-ol-uQT" secondAttribute="trailing" constant="-26" id="mrL-SK-eKx"/>
<constraint firstAttribute="centerX" secondItem="lKc-rv-FH5" secondAttribute="centerX" constant="-5" id="obF-St-qwu">
<variation key="heightClass=regular-widthClass=compact" constant="0.0"/>
</constraint>
<constraint firstItem="PaA-ol-uQT" firstAttribute="top" secondItem="lZK-q4-iwt" secondAttribute="bottom" id="ouV-vM-Vyv">
<variation key="heightClass=regular-widthClass=compact" constant="0.0"/>
</constraint>
<constraint firstItem="7uh-gm-z8v" firstAttribute="top" secondItem="PaA-ol-uQT" secondAttribute="bottom" constant="66" id="s5v-Hl-a3k"/>
<constraint firstItem="PaA-ol-uQT" firstAttribute="width" secondItem="EFA-Fu-XJm" secondAttribute="width" id="wn4-O9-ILm">
<variation key="heightClass=regular-widthClass=compact" constant="10"/>
</constraint>
<constraint firstItem="uoq-UX-3fJ" firstAttribute="top" secondItem="lZK-q4-iwt" secondAttribute="bottom" constant="-1" id="xlM-od-Zuu"/>
</constraints>
<variation key="default">
<mask key="subviews">
<exclude reference="uoq-UX-3fJ"/>
<exclude reference="lKc-rv-FH5"/>
<exclude reference="PaA-ol-uQT"/>
</mask>
<mask key="constraints">
<exclude reference="7iL-hd-xP7"/>
<exclude reference="GCa-EH-8Vh"/>
<exclude reference="lCx-f0-NYv"/>
<exclude reference="s5v-Hl-a3k"/>
<exclude reference="EdI-ia-IjN"/>
<exclude reference="0co-lj-Jsm"/>
<exclude reference="OD6-Pc-Zug"/>
<exclude reference="ouV-vM-Vyv"/>
<exclude reference="wn4-O9-ILm"/>
<exclude reference="ZkG-Yf-ISG"/>
<exclude reference="gdn-tV-9au"/>
<exclude reference="jLN-Fv-npv"/>
<exclude reference="mrL-SK-eKx"/>
<exclude reference="Bu7-qv-yue"/>
<exclude reference="EYc-tW-W2D"/>
<exclude reference="KiY-Ag-0iP"/>
<exclude reference="OOk-XM-IMs"/>
<exclude reference="luy-6c-SFS"/>
<exclude reference="obF-St-qwu"/>
<exclude reference="6gc-oZ-IMJ"/>
<exclude reference="AP2-DE-zts"/>
<exclude reference="Fc5-tN-hro"/>
<exclude reference="La3-rh-BpY"/>
<exclude reference="fEl-Ut-x0g"/>
<exclude reference="xlM-od-Zuu"/>
</mask>
</variation>
<variation key="heightClass=regular-widthClass=compact">
<mask key="subviews">
<include reference="uoq-UX-3fJ"/>
<include reference="lKc-rv-FH5"/>
<include reference="PaA-ol-uQT"/>
</mask>
<mask key="constraints">
<include reference="7iL-hd-xP7"/>
<exclude reference="GCa-EH-8Vh"/>
<include reference="lCx-f0-NYv"/>
<include reference="s5v-Hl-a3k"/>
<include reference="EdI-ia-IjN"/>
<include reference="0co-lj-Jsm"/>
<exclude reference="OD6-Pc-Zug"/>
<include reference="ouV-vM-Vyv"/>
<include reference="wn4-O9-ILm"/>
<include reference="ZkG-Yf-ISG"/>
<include reference="gdn-tV-9au"/>
<include reference="jLN-Fv-npv"/>
<include reference="mrL-SK-eKx"/>
<include reference="Bu7-qv-yue"/>
<exclude reference="EYc-tW-W2D"/>
<include reference="KiY-Ag-0iP"/>
<include reference="OOk-XM-IMs"/>
<exclude reference="luy-6c-SFS"/>
<include reference="obF-St-qwu"/>
<exclude reference="6gc-oZ-IMJ"/>
<include reference="AP2-DE-zts"/>
<exclude reference="Fc5-tN-hro"/>
<exclude reference="La3-rh-BpY"/>
<include reference="fEl-Ut-x0g"/>
<exclude reference="xlM-od-Zuu"/>
</mask>
</variation>
</view>
@ -413,10 +169,7 @@
</barButtonItem>
</navigationItem>
<connections>
<outlet property="archiveButton" destination="WVY-Zl-dpP" id="IdE-Kz-1r0"/>
<outlet property="emptyBoxLabel" destination="Srx-i1-WhD" id="wap-un-Cz5"/>
<outlet property="inboxButton" destination="DfA-eN-PyK" id="p0D-Qj-YzC"/>
<outlet property="inboxCountLabel" destination="GV1-fp-iPR" id="MHq-Gs-bgt"/>
<outlet property="tableView" destination="PaA-ol-uQT" id="nQU-tR-wbL"/>
<segue destination="Duq-aU-MmN" kind="presentation" identifier="2.0_6.0_Call_Segue" id="gHJ-y4-zWg"/>
<segue destination="lIF-0m-2N3" kind="presentation" identifier="showSignupFlow" id="DR8-fx-0PD"/>
@ -4798,6 +4551,9 @@ A0 09 9A FF A8 8A 09 99</string>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
<variation key="heightClass=regular-widthClass=compact" misplaced="YES">
<rect key="frame" x="10" y="274" width="130" height="21"/>
</variation>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
@ -4950,8 +4706,6 @@ A0 09 9A FF A8 8A 09 99</string>
<resources>
<image name="_arrow_button" width="12" height="23"/>
<image name="add-conversation" width="44" height="44"/>
<image name="archive-active@1x" width="66" height="66"/>
<image name="archive-inactive@1x" width="66" height="66"/>
<image name="btnCamera--white" width="52" height="40"/>
<image name="btnCancel--white" width="44" height="44"/>
<image name="btnGroup--white" width="44" height="44"/>
@ -4962,8 +4716,6 @@ A0 09 9A FF A8 8A 09 99</string>
<image name="contact-options-action" width="44" height="44"/>
<image name="empty-group-avatar" width="53" height="53"/>
<image name="endcall.png" width="100" height="100"/>
<image name="inbox-active@1x" width="66" height="66"/>
<image name="inbox-inactive@1x" width="66" height="66"/>
<image name="logoSignal" width="138" height="139"/>
<image name="mute-active" width="80" height="80"/>
<image name="mute-inactive" width="80" height="80"/>
@ -4973,8 +4725,8 @@ A0 09 9A FF A8 8A 09 99</string>
<image name="speaker-inactive" width="80" height="80"/>
</resources>
<inferredMetricsTieBreakers>
<segue reference="D0d-4f-lcI"/>
<segue reference="ZcV-Ck-baL"/>
<segue reference="DR8-fx-0PD"/>
<segue reference="xo7-5J-BJb"/>
<segue reference="D0d-4f-lcI"/>
</inferredMetricsTieBreakers>
</document>

View File

@ -3,11 +3,12 @@
#import "FunctionalUtil.h"
#import "ObservableValue.h"
#import "PreferencesUtil.h"
#import "PushManager.h"
#import "NSDate+millisecondTimeStamp.h"
#import "TSCall.h"
#import "TSStorageManager.h"
#import "TSContactThread.h"
#import "TSMessagesManager.h"
#import "TSStorageManager.h"
@interface RecentCallManager ()
@property YapDatabaseConnection *dbConnection;
@ -41,7 +42,8 @@
[call.futureTermination finallyDo:^(TOCFuture* interactionCompletion) {
bool isOutgoingCall = call.initiatedLocally;
bool isMissedCall = interactionCompletion.hasFailed;
bool isMissedCall = [self isMissedCall:interactionCompletion];
Contact* contact = [self tryGetContactForCall:call];
RPRecentCallType callType = isOutgoingCall ? RPRecentCallTypeOutgoing
@ -54,6 +56,18 @@
}];
}
- (BOOL)isMissedCall:(TOCFuture*)interactionCompletion {
if ([interactionCompletion hasResult]) {
if ([[interactionCompletion forceGetResult] isKindOfClass:[CallTermination class]]) {
CallTermination *termination = (CallTermination*)interactionCompletion.forceGetResult;
if (termination.type == CallTerminationType_HangupRemote) {
return YES;
}
}
}
return NO;
}
- (Contact*)tryGetContactForCall:(CallState*)call {
if (call.potentiallySpecifiedContact != nil) return call.potentiallySpecifiedContact;
return [self tryGetContactForNumber:call.remoteNumber];
@ -82,7 +96,10 @@
NSDate *date = [NSDate dateWithTimeIntervalSince1970:(callDateSeconds+60)]; // archive has to happen in the future of the original call
[thread archiveThreadWithTransaction:transaction referenceDate:date];
}
[call saveWithTransaction:transaction];
[[TSMessagesManager sharedManager] notifyUserForCall:call inThread:thread];
}];
}

View File

@ -7,6 +7,7 @@
#import "HostNameEndPoint.h"
#import "RecentCallManager.h"
#import "ContactsManager.h"
#import "MessagesViewController.h"
#import "PreferencesUtil.h"
#import "PhoneNumberDirectoryFilterManager.h"
#import "SignalKeyingStorage.h"
@ -197,6 +198,17 @@ phoneDirectoryManager;
if ([thread isGroupThread]) {
[self messageGroup:(TSGroupThread*)thread];
} else {
Environment *env = [self getCurrent];
SignalsViewController *vc = env.signalsViewController;
UIViewController *topvc = vc.navigationController.topViewController;
if ([topvc isKindOfClass:[MessagesViewController class]]) {
MessagesViewController *mvc = (MessagesViewController*)topvc;
if ([mvc.thread.uniqueId isEqualToString:threadId]) {
[mvc popKeyBoard];
return;
}
}
[self messageIdentifier:((TSContactThread*)thread).contactIdentifier withCompose:YES];
}
}
@ -209,7 +221,7 @@ phoneDirectoryManager;
[vc.presentedViewController dismissViewControllerAnimated:YES completion:nil];
}
[vc.navigationController popToRootViewControllerAnimated:YES];
[vc.navigationController popToRootViewControllerAnimated:NO];
vc.contactIdentifierFromCompose = identifier;
vc.composeMessage = compose;
[vc performSegueWithIdentifier:@"showSegue" sender:nil];
@ -223,7 +235,7 @@ phoneDirectoryManager;
[vc.presentedViewController dismissViewControllerAnimated:YES completion:nil];
}
[vc.navigationController popToRootViewControllerAnimated:YES];
[vc.navigationController popToRootViewControllerAnimated:NO];
[vc performSegueWithIdentifier:@"showSegue" sender:groupThread];
}
@ -235,7 +247,7 @@ phoneDirectoryManager;
[vc.presentedViewController dismissViewControllerAnimated:YES completion:nil];
}
[vc.navigationController popToRootViewControllerAnimated:YES];
[vc.navigationController popToRootViewControllerAnimated:NO];
vc.groupFromCompose = model;
vc.composeMessage = compose;
[vc performSegueWithIdentifier:@"showSegue" sender:nil];

View File

@ -257,6 +257,7 @@
[[PushManager sharedManager] requestPushTokenWithSuccess:^(NSData *pushToken, NSData *voipToken) {
[TSAccountManager registerForPushNotifications:pushToken voipToken:voipToken success:^{
[UIApplication.sharedApplication setNetworkActivityIndicatorVisible:NO];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:NEEDS_TO_REGISTER_PUSH_KEY];
[waitingController dismissViewControllerAnimated:YES completion:nil];
} failure:failure];

View File

@ -10,12 +10,16 @@
#import <Foundation/Foundation.h>
#define Signal_Thread_UserInfo_Key @"Signal_Thread_Id"
#define Signal_Call_UserInfo_Key @"Signal_Call_Id"
#define Signal_Call_Accept_Identifier @"Signal_Call_Accept"
#define Signal_Call_Decline_Identifier @"Signal_Call_Decline"
#define Signal_CallBack_Identifier @"Signal_CallBack"
#define Signal_Call_Category @"Signal_IncomingCall"
#define Signal_Message_Category @"Signal_Message"
#define Signal_CallBack_Category @"Signal_CallBack"
#define Signal_Message_View_Identifier @"Signal_Message_Read"
#define Signal_Message_MarkAsRead_Identifier @"Signal_Message_MarkAsRead"
@ -66,6 +70,7 @@ typedef void (^registrationTokensSuccessBlock)(NSData *pushToken, NSData *voipTo
-(TOCFuture*)registerPushKitNotificationFuture;
- (BOOL)supportsVOIPPush;
- (UILocalNotification*)closeVOIPBackgroundTask;
#pragma mark Push Notifications Delegate Methods

View File

@ -15,6 +15,7 @@
#import "PreferencesUtil.h"
#import "PushManager.h"
#import "Environment.h"
#import "PreferencesUtil.h"
#import "RPServerRequestsManager.h"
#import "TSAccountManager.h"
#import "TSSocketManager.h"
@ -26,6 +27,7 @@
@property TOCFutureSource *registerWithServerFutureSource;
@property UIAlertView *missingPermissionsAlertView;
@property (nonatomic, strong) NotificationTracker *notificationTracker;
@property UILocalNotification *lastCallNotification;
@property (nonatomic) UIBackgroundTaskIdentifier callBackgroundTask;
@end
@ -53,6 +55,7 @@
delegate:nil
cancelButtonTitle:NSLocalizedString(@"OK", @"")
otherButtonTitles:nil, nil];
_callBackgroundTask = UIBackgroundTaskInvalid;
}
return self;
}
@ -60,7 +63,6 @@
#pragma mark Manage Incoming Push
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
if ([self isRedPhonePush:userInfo]) {
ResponderSessionDescriptor* call;
if (![self.notificationTracker shouldProcessNotification:userInfo]){
@ -82,35 +84,51 @@
[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;
NSString *displayName = nameString?nameString:callerId;
PropertyListPreferences *prefs = [Environment preferences];
if ([prefs notificationPreviewType] == NotificationNoNameNoPreview) {
notification.alertBody = NSLocalizedString(@"INCOMING_CALL", nil);
} else {
notification.alertBody = [NSString stringWithFormat:NSLocalizedString(@"INCOMING_CALL_FROM", nil), displayName];
}
notification.alertBody = [NSString stringWithFormat:@"Incoming call from %@", displayName];
notification.category = Signal_Call_Category;
notification.soundName = @"r.caf";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
_lastCallNotification = notification;
if (_callBackgroundTask == 0) {
if (_callBackgroundTask == UIBackgroundTaskInvalid) {
_callBackgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
_callBackgroundTask = 0;
[Environment.phoneManager hangupOrDenyCall];
[Environment.phoneManager backgroundTimeExpired];
[self closeVOIPBackgroundTask];
}];
}
}
} else {
if (![self applicationIsActive]) {
[TSSocketManager becomeActiveFromBackground];
[TSSocketManager becomeActiveFromBackgroundExpectMessage:YES];
}
}
}
- (UILocalNotification*)closeVOIPBackgroundTask {
[[UIApplication sharedApplication] endBackgroundTask:_callBackgroundTask];
_callBackgroundTask = UIBackgroundTaskInvalid;
UILocalNotification *notif = _lastCallNotification;
_lastCallNotification = nil;
return notif;
}
/**
* 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
*
@ -147,6 +165,11 @@
dispatch_get_main_queue(), ^{
completionHandler();
});
} else if([identifier isEqualToString:Signal_CallBack_Identifier]){
NSString * contactId = [notification.userInfo objectForKeyedSubscript:Signal_Call_UserInfo_Key];
PhoneNumber *number = [PhoneNumber tryParsePhoneNumberFromUserSpecifiedText:contactId];
Contact *contact = [[Environment.getCurrent contactsManager] latestContactForPhoneNumber:number];
[Environment.phoneManager initiateOutgoingCallToContact:contact atRemoteNumber:number];
} else{
NSString *threadId = [notification.userInfo objectForKey:Signal_Thread_UserInfo_Key];
[Environment messageThreadId:threadId];
@ -294,7 +317,9 @@
UIUserNotificationSettings *settings =
[UIUserNotificationSettings settingsForTypes:(UIUserNotificationType)[self allNotificationTypes]
categories:[NSSet setWithObjects:[self userNotificationsCallCategory], [self userNotificationsMessageCategory], nil]];
categories:[NSSet setWithObjects:[self userNotificationsCallCategory],
[self userNotificationsMessageCategory],
[self userNotificationsCallBackCategory], nil]];
[UIApplication.sharedApplication registerUserNotificationSettings:settings];
return self.userNotificationFutureSource.future;
@ -303,7 +328,7 @@
- (UIUserNotificationCategory*)userNotificationsMessageCategory{
UIMutableUserNotificationAction *action_view = [UIMutableUserNotificationAction new];
action_view.identifier = Signal_Message_View_Identifier;
action_view.title = NSLocalizedString(@"View", @"");
action_view.title = NSLocalizedString(@"PUSH_MANAGER_VIEW", @"");
action_view.activationMode = UIUserNotificationActivationModeForeground;
action_view.destructive = NO;
action_view.authenticationRequired = YES;
@ -339,6 +364,22 @@
return callCategory;
}
- (UIUserNotificationCategory*)userNotificationsCallBackCategory{
UIMutableUserNotificationAction *action_accept = [UIMutableUserNotificationAction new];
action_accept.identifier = Signal_CallBack_Identifier;
action_accept.title = NSLocalizedString(@"CALLBACK_BUTTON_TITLE", @"");
action_accept.activationMode = UIUserNotificationActivationModeForeground;
action_accept.destructive = NO;
action_accept.authenticationRequired = NO;
UIMutableUserNotificationCategory *callCategory = [UIMutableUserNotificationCategory new];
callCategory.identifier = Signal_CallBack_Category;
[callCategory setActions:@[action_accept] forContext:UIUserNotificationActionContextMinimal];
[callCategory setActions:@[action_accept] forContext:UIUserNotificationActionContextDefault];
return callCategory;
}
- (BOOL)needToRegisterForRemoteNotifications
{
return self.wantRemoteNotifications && (!UIApplication.sharedApplication.isRegisteredForRemoteNotifications);

View File

@ -31,6 +31,8 @@
-(void) hangupOrDenyCall;
-(void) answerCall;
-(BOOL) toggleMute;
-(void) backgroundTimeExpired;
-(ObservableValue*) currentCallObservable;
+(PhoneManager*) phoneManagerWithErrorHandler:(ErrorHandlerBlock)errorHandler;

View File

@ -135,13 +135,19 @@
-(CallController*) curCallController {
return currentCallControllerObservable.currentValue;
}
-(void) answerCall {
-(void)answerCall {
[[self curCallController] acceptCall];
}
-(void) hangupOrDenyCall {
- (void)hangupOrDenyCall {
[[self curCallController] hangupOrDenyCall];
}
- (void)backgroundTimeExpired {
[[self curCallController] backgroundTimeExpired];
}
-(BOOL) toggleMute{
return [self.curCallController toggleMute];
}

View File

@ -37,6 +37,7 @@
-(void)advanceCallProgressTo:(enum CallProgressType)type;
-(void)hangupOrDenyCall;
-(void)acceptCall;
- (void)backgroundTimeExpired;
-(void)advanceCallProgressToConversingWithShortAuthenticationString:(NSString*)sas;
-(void)terminateWithReason:(enum CallTerminationType)reason
withFailureInfo:(id)failureInfo

View File

@ -90,6 +90,13 @@
[self unrestrictedAdvanceCallProgressTo:type];
}
- (void)backgroundTimeExpired {
[self terminateWithReason:CallTerminationType_BackgroundTimeExpired
withFailureInfo:nil
andRelatedInfo:nil];
}
-(void)hangupOrDenyCall {
bool didDeny = [interactiveCallAcceptedOrDenied trySetResult:@NO];

View File

@ -25,6 +25,7 @@ enum CallTerminationType {
// -- uh oh --
CallTerminationType_BadInteractionWithServer, /// The signaling or relay server did something we didn't expect or understand.
CallTerminationType_UncategorizedFailure, /// Something went wrong. We didn't handle it properly, so we don't know what exactly it was.
CallTerminationType_BackgroundTimeExpired, /// The application expired available time while in background.
};
/**

View File

@ -18,7 +18,7 @@
self = [super initWithTimestamp:timeStamp inThread:thread];
if (self) {
_callType = callType;
_callType = callType;
}
return self;

View File

@ -8,6 +8,7 @@
#import "TSErrorMessage.h"
#import "NSDate+millisecondTimeStamp.h"
#import "TSMessagesManager.h"
#import "TSErrorMessage_privateConstructor.h"
@ -20,11 +21,14 @@
_errorType = errorMessageType;
}
[[TSMessagesManager sharedManager] notifyUserForError:self inThread:thread];
return self;
}
- (instancetype)initWithSignal:(IncomingPushMessageSignal*)signal transaction:(YapDatabaseReadWriteTransaction*)transaction failedMessageType:(TSErrorMessageType)errorMessageType{
TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:signal.source transaction:transaction];
return [self initWithTimestamp:signal.timestamp inThread:contactThread failedMessageType:errorMessageType];
}

View File

@ -50,7 +50,7 @@ NSString * const TSAttachementsRelationshipEdgeName = @"TSAttachmentEdge";
self = [super initWithTimestamp:timestamp inThread:thread];
if (self) {
_body = body;
_body = body;
_attachments = [attachments mutableCopy];
}
return self;
@ -62,7 +62,7 @@ NSString * const TSAttachementsRelationshipEdgeName = @"TSAttachmentEdge";
- (NSString *)description{
if(self.attachments > 0){
return @"Attachment";
return NSLocalizedString(@"ATTACHMENT", nil);
} else {
return self.body;
}

View File

@ -11,6 +11,7 @@
#import "TSIncomingMessage.h"
#import "TSOutgoingMessage.h"
#import "TSInvalidIdentityKeySendingErrorMessage.h"
@class TSCall;
@interface TSMessagesManager : NSObject
@ -21,6 +22,8 @@
- (void)handleMessageSignal:(IncomingPushMessageSignal*)messageSignal;
- (void)processException:(NSException*)exception outgoingMessage:(TSOutgoingMessage*)message inThread:(TSThread*)thread;
- (void)notifyUserForError:(TSErrorMessage*)message inThread:(TSThread*)thread;
- (void)notifyUserForCall:(TSCall*)call inThread:(TSThread*)thread;
- (void)handleReceivedMessage:(IncomingPushMessageSignal*)message withContent:(PushMessageContent*)content attachments:(NSArray*)attachments;
- (void)handleReceivedMessage:(IncomingPushMessageSignal*)message withContent:(PushMessageContent*)content attachments:(NSArray*)attachments completionBlock:(void (^)(NSString* messageIdentifier))completionBlock ;
@ -28,5 +31,6 @@
- (void)handleSendToMyself:(TSOutgoingMessage*)outgoingMessage;
- (NSUInteger)unreadMessagesCount;
- (NSUInteger)unreadMessagesCountExcept:(TSThread*)thread;
@end

View File

@ -41,6 +41,7 @@
#import "Environment.h"
#import "PreferencesUtil.h"
#import "ContactsManager.h"
#import "TSCall.h"
#import <CocoaLumberjack/DDLog.h>
#import <YapDatabase/YapDatabaseSecondaryIndex.h>
@ -348,7 +349,12 @@
}
NSString *name = [thread name];
[self notifyUserForIncomingMessage:incomingMessage from:name inThread:thread];
if (incomingMessage && thread) {
[self notifyUserForIncomingMessage:incomingMessage
from:name
inThread:thread];
}
}];
}
@ -407,6 +413,67 @@
return numberOfItems;
}
- (NSUInteger)unreadMessagesCountExcept:(TSThread*)thread {
__block NSUInteger numberOfItems;
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
numberOfItems = [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInAllGroups];
numberOfItems = numberOfItems - [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInGroup:thread.uniqueId];
}];
return numberOfItems;
}
- (void)notifyUserForCall:(TSCall*)call inThread:(TSThread*)thread {
if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive){
// Remove previous notification of call and show missed notification.
UILocalNotification *notif = [[PushManager sharedManager] closeVOIPBackgroundTask];
TSContactThread *cThread = (TSContactThread*)thread;
if (call.callType == RPRecentCallTypeMissed) {
[[UIApplication sharedApplication] cancelLocalNotification:notif];
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.category = Signal_CallBack_Category;
notification.userInfo = @{Signal_Call_UserInfo_Key:cThread.contactIdentifier};
notification.soundName = @"NewMessage.aifc";
notification.alertBody = [NSString stringWithFormat:NSLocalizedString(@"MSGVIEW_MISSED_CALL", nil), [thread name]];
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
}
}
- (void)notifyUserForError:(TSErrorMessage*)message inThread:(TSThread*)thread {
NSString *messageDescription = message.description;
if (([UIApplication sharedApplication].applicationState != UIApplicationStateActive) && messageDescription) {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.category = Signal_Message_Category;
notification.userInfo = @{Signal_Thread_UserInfo_Key:thread.uniqueId};
notification.soundName = @"NewMessage.aifc";
NSString *alertBodyString = @"";
NSString *authorName = [thread name];
switch ([[Environment preferences] notificationPreviewType]) {
case NotificationNamePreview:
case NotificationNameNoPreview:
alertBodyString = [NSString stringWithFormat:@"%@: %@", authorName,messageDescription];
break;
case NotificationNoNameNoPreview:
alertBodyString = messageDescription;
break;
}
notification.alertBody = alertBodyString;
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
} else {
if ([Environment.preferences soundInForeground]) {
AudioServicesPlayAlertSound(_newMessageSound);
}
}
}
- (void)notifyUserForIncomingMessage:(TSIncomingMessage*)message from:(NSString*)name inThread:(TSThread*)thread {
NSString *messageDescription = message.description;
@ -424,7 +491,8 @@
sender = message.authorId;
}
notification.alertBody = [NSString stringWithFormat:@"New message from %@ in group \"%@\": %@", sender, name, messageDescription];
NSString *threadName = [NSString stringWithFormat:@"\"%@\"", name];
notification.alertBody = [NSString stringWithFormat:NSLocalizedString(@"APN_MESSAGE_IN_GROUP_DETAILED", nil), sender, threadName, messageDescription];
} else {
notification.alertBody = [NSString stringWithFormat:@"%@: %@", name, messageDescription];
}

View File

@ -16,7 +16,8 @@
self = [super initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/%@/%@", textSecureKeysAPI, recipientInformation, deviceId]]];
[self setHTTPMethod:@"GET"];
self.HTTPMethod = @"GET";
self.parameters = nil;
return self;
}

View File

@ -24,7 +24,7 @@ extern NSString * const SocketConnectingNotification;
@interface TSSocketManager : NSObject <SRWebSocketDelegate>
+ (void)becomeActiveFromForeground;
+ (void)becomeActiveFromBackground;
+ (void)becomeActiveFromBackgroundExpectMessage:(BOOL)expected;
+ (void)resignActivity;
+ (void)sendNotification;

View File

@ -21,7 +21,7 @@
#define kWebSocketHeartBeat 30
#define kWebSocketReconnectTry 5
#define kBackgroundConnectTimer 10
#define kBackgroundConnectTimer 25
#define kBackgroundConnectKeepAlive 15
NSString * const SocketOpenedNotification = @"SocketOpenedNotification";
@ -38,7 +38,10 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
@property (nonatomic) UIBackgroundTaskIdentifier fetchingTaskIdentifier;
@property BOOL didFetchInBackground;
@property BOOL didConnectBg;
@property BOOL didRetreiveMessageBg;
@property BOOL shouldDownloadMessage;
@property (nonatomic, retain) NSTimer *backgroundKeepAliveTimer;
@property (nonatomic, retain) NSTimer *backgroundConnectTimer;
@ -51,7 +54,10 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
if (self) {
self.websocket = nil;
[self addObserver:self forKeyPath:@"status" options:0 context:kSocketStatusObservationContext];
[self addObserver:self
forKeyPath:@"status"
options:0
context:kSocketStatusObservationContext];
}
return self;
@ -63,7 +69,9 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
sharedMyManager.fetchingTaskIdentifier = UIBackgroundTaskInvalid;
sharedMyManager.didFetchInBackground = NO;
sharedMyManager.didConnectBg = NO;
sharedMyManager.shouldDownloadMessage = NO;
sharedMyManager.didRetreiveMessageBg = NO;
});
return sharedMyManager;
}
@ -118,7 +126,7 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
#pragma mark - Delegate methods
- (void)webSocketDidOpen:(SRWebSocket *)webSocket {
self.pingTimer = [NSTimer scheduledTimerWithTimeInterval:kWebSocketHeartBeat
self.pingTimer = [NSTimer timerWithTimeInterval:kWebSocketHeartBeat
target:self
selector:@selector(webSocketHeartBeat)
userInfo:nil
@ -131,6 +139,7 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
self.status = kSocketStatusOpen;
[self.reconnectTimer invalidate];
self.reconnectTimer = nil;
self.didConnectBg = YES;
}
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error {
@ -143,7 +152,7 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(NSData*)data {
WebSocketMessage *wsMessage = [WebSocketMessage parseFromData:data];
self.didFetchInBackground = YES;
self.didRetreiveMessageBg = YES;
if (wsMessage.type == WebSocketMessageTypeRequest) {
[self processWebSocketRequestMessage:wsMessage.request];
@ -158,7 +167,6 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
DDLogInfo(@"Got message with verb: %@ and path: %@", message.verb, message.path);
[self sendWebSocketMessageAcknowledgement:message];
[self keepAliveBackground];
if ([message.path isEqualToString:@"/api/v1/message"] && [message.verb isEqualToString:@"PUT"]){
@ -184,7 +192,7 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
if (self.fetchingTaskIdentifier) {
[self.backgroundKeepAliveTimer invalidate];
self.backgroundKeepAliveTimer = [NSTimer scheduledTimerWithTimeInterval:kBackgroundConnectKeepAlive
self.backgroundKeepAliveTimer = [NSTimer timerWithTimeInterval:kBackgroundConnectKeepAlive
target:self
selector:@selector(backgroundTimeExpired)
userInfo:nil
@ -243,8 +251,8 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
}
- (void)scheduleRetry {
if (!self.reconnectTimer || ![self.reconnectTimer isValid]) {
self.reconnectTimer = [NSTimer scheduledTimerWithTimeInterval:kWebSocketReconnectTry
if (![self.reconnectTimer isValid]) {
self.reconnectTimer = [NSTimer timerWithTimeInterval:kWebSocketReconnectTry
target:[self class]
selector:@selector(becomeActive)
userInfo:nil
@ -252,6 +260,8 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
// Additionally, we want the reconnect timer to work in the background too.
[[NSRunLoop mainRunLoop] addTimer:self.reconnectTimer
forMode:NSDefaultRunLoopMode];
} else {
DDLogWarn(@"Not scheduling retry, valid timer");
}
}
@ -259,7 +269,6 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
+ (void)becomeActiveFromForeground {
TSSocketManager *sharedInstance = [self sharedManager];
[sharedInstance.backgroundKeepAliveTimer invalidate];
if (sharedInstance.fetchingTaskIdentifier != UIBackgroundTaskInvalid) {
[sharedInstance closeBackgroundTask];
@ -268,26 +277,23 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
[self becomeActive];
}
+ (void)becomeActiveFromBackground {
+ (void)becomeActiveFromBackgroundExpectMessage:(BOOL)expected {
TSSocketManager *sharedInstance = [TSSocketManager sharedManager];
if (sharedInstance.fetchingTaskIdentifier == UIBackgroundTaskInvalid) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sharedInstance.backgroundConnectTimer = [NSTimer scheduledTimerWithTimeInterval:kBackgroundConnectTimer
sharedInstance.backgroundConnectTimer = [NSTimer timerWithTimeInterval:kBackgroundConnectTimer
target:sharedInstance
selector:@selector(backgroundConnectTimerExpired)
userInfo:nil
repeats:NO];
NSRunLoop *loop = [NSRunLoop currentRunLoop];
[loop addTimer:[TSSocketManager sharedManager].backgroundConnectTimer
forMode:NSDefaultRunLoopMode];
[loop run];
});
NSRunLoop *loop = [NSRunLoop mainRunLoop];
[loop addTimer:[TSSocketManager sharedManager].backgroundConnectTimer
forMode:NSDefaultRunLoopMode];
[sharedInstance.backgroundKeepAliveTimer invalidate];
sharedInstance.didFetchInBackground = NO;
sharedInstance.didConnectBg = NO;
sharedInstance.didRetreiveMessageBg = NO;
sharedInstance.shouldDownloadMessage = expected;
sharedInstance.fetchingTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[TSSocketManager resignActivity];
[[TSSocketManager sharedManager] closeBackgroundTask];
@ -312,9 +318,16 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
[self.backgroundKeepAliveTimer invalidate];
[self.backgroundConnectTimer invalidate];
if (!self.didFetchInBackground) {
/*
If VOIP Push worked, we should just have to check if message was retreived and if not, alert the user.
But we have to rely on the server for the fallback in failed cases since background push is unreliable.
https://devforums.apple.com/message/1135227
if ((self.shouldDownloadMessage && !self.didRetreiveMessageBg) || !self.didConnectBg) {
[self backgroundConnectTimedOut];
}
*/
[[UIApplication sharedApplication] endBackgroundTask:self.fetchingTaskIdentifier];
self.fetchingTaskIdentifier = UIBackgroundTaskInvalid;
@ -323,6 +336,7 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
- (void)backgroundConnectTimedOut {
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody = NSLocalizedString(@"APN_FETCHED_FAILED", nil);
notification.soundName = @"NewMessage.aifc";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}

View File

@ -29,11 +29,11 @@ typedef enum {
#define textSecureHTTPTimeOut 10
#define textSecureWebSocketAPI @"wss://textsecure-service.whispersystems.org/v1/websocket/"
#define textSecureServerURL @"https://textsecure-service.whispersystems.org/"
#define textSecureWebSocketAPI @"wss://textsecure-service.whispersystems.org/v1/websocket/"
#define textSecureServerURL @"https://textsecure-service.whispersystems.org/"
//#define textSecureWebSocketAPI @"wss://textsecure-service-staging.whispersystems.org/v1/websocket/"
//#define textSecureServerURL @"https://textsecure-service-staging.whispersystems.org/"
//#define textSecureWebSocketAPI @"wss://textsecure-service-staging.whispersystems.org/v1/websocket/"
//#define textSecureServerURL @"https://textsecure-service-staging.whispersystems.org/"
#define textSecureGeneralAPI @"v1"
#define textSecureAccountsAPI @"v1/accounts"

View File

@ -24,10 +24,14 @@
@property (nonatomic,strong) MPMoviePlayerController* videoPlayer;
@property (nonatomic,strong) AVAudioPlayer* audioPlayer;
@property (nonatomic,strong) AVAudioRecorder* audioRecorder;
- (void)setupWithThread:(TSThread*)thread;
- (void)setupWithTSIdentifier:(NSString*)identifier;
- (void)setupWithTSGroup:(TSGroupModel*)model;
- (void)setComposeOnOpen:(BOOL)compose;
- (TSThread*)thread;
- (void)popKeyBoard;
@end

View File

@ -9,6 +9,7 @@
#import "AppDelegate.h"
#import <AddressBookUI/AddressBookUI.h>
#import "MessagesViewController.h"
#import "FullImageViewController.h"
#import "FingerprintViewController.h"
@ -82,9 +83,13 @@ typedef enum : NSUInteger {
@interface MessagesViewController () {
UIImage* tappedImage;
BOOL isGroupConversation;
UIView *_unreadContainer;
UIImageView *_unreadBackground;
UILabel *_unreadLabel;
NSUInteger _unreadCount;
}
@property (nonatomic, weak) UIView *navView;
@property (nonatomic, retain) TSThread *thread;
@property (nonatomic, strong) YapDatabaseConnection *editingDatabaseConnection;
@ -113,6 +118,11 @@ typedef enum : NSUInteger {
@end
@interface UINavigationItem(){
UIView *backButtonView;
}
@end
@implementation MessagesViewController
- (void)setupWithTSIdentifier:(NSString *)identifier {
@ -146,6 +156,9 @@ typedef enum : NSUInteger {
isGroupConversation = [self.thread isKindOfClass:[TSGroupThread class]];
}
- (TSThread *)thread {
return _thread;
}
- (void)hideInputIfNeeded {
if([_thread isKindOfClass:[TSGroupThread class]] && ![((TSGroupThread*)_thread).groupModel.groupMemberIds containsObject:[SignalKeyingStorage.localNumber toE164]]) {
@ -229,7 +242,7 @@ typedef enum : NSUInteger {
}
- (void)startReadTimer {
self.readTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(markAllMessagesAsRead) userInfo:nil repeats:YES];
self.readTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(markAllMessagesAsRead) userInfo:nil repeats:YES];
}
- (void)cancelReadTimer {
@ -237,23 +250,32 @@ typedef enum : NSUInteger {
}
- (void)viewDidAppear:(BOOL)animated {
[self updateBackButton];
[super viewDidAppear:animated];
[self markAllMessagesAsRead];
[self startReadTimer];
_isVisible = YES;
[self initializeTitleLabelGestureRecognizer];
[self updateBackButton];
if (_composeOnOpen) {
[self popKeyBoard];
}
}
- (void)updateBackButton {
[self setUnreadCount:[[TSMessagesManager sharedManager] unreadMessagesCountExcept:self.thread]];
}
- (void)viewWillDisappear:(BOOL)animated {
if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
// back button was pressed.
[self.navController hideDropDown:self];
}
[super viewWillDisappear:animated];
[_unreadContainer removeFromSuperview];
_unreadContainer = nil;
[_audioPlayerPoller invalidate];
[_audioPlayer stop];
@ -1328,6 +1350,9 @@ typedef enum : NSUInteger {
- (void)yapDatabaseModified:(NSNotification *)notification {
[self updateBackButton];
if(isGroupConversation) {
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
TSGroupThread* gThread = (TSGroupThread*)self.thread;
@ -1657,6 +1682,58 @@ typedef enum : NSUInteger {
}
}
#pragma mark Unread Badge
- (void)setUnreadCount:(NSUInteger)unreadCount {
if (_unreadCount != unreadCount) {
_unreadCount = unreadCount;
if (_unreadCount > 0) {
if (_unreadContainer == nil) {
static UIImage *backgroundImage = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(17.0f, 17.0f), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, 17.0f, 17.0f));
backgroundImage = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:8 topCapHeight:8];
UIGraphicsEndImageContext();
});
_unreadContainer = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 10.0f, 10.0f)];
_unreadContainer.userInteractionEnabled = NO;
_unreadContainer.layer.zPosition = 2000;
[self.navigationController.navigationBar addSubview:_unreadContainer];
_unreadBackground = [[UIImageView alloc] initWithImage:backgroundImage];
[_unreadContainer addSubview:_unreadBackground];
_unreadLabel = [[UILabel alloc] init];
_unreadLabel.backgroundColor = [UIColor clearColor];
_unreadLabel.textColor = [UIColor whiteColor];
_unreadLabel.font = [UIFont systemFontOfSize:12];
[_unreadContainer addSubview:_unreadLabel];
}
_unreadContainer.hidden = false;
_unreadLabel.text = [NSString stringWithFormat:@"%lu", (unsigned long)unreadCount];
[_unreadLabel sizeToFit];
CGPoint offset = CGPointMake(17.0f, 2.0f);
_unreadBackground.frame = CGRectMake(offset.x, offset.y,
MAX(_unreadLabel.frame.size.width + 8.0f, 17.0f), 17.0f);
_unreadLabel.frame = CGRectMake(offset.x + floor((2.0f*(_unreadBackground.frame.size.width - _unreadLabel.frame.size.width)/ 2.0f)/2.0f), offset.y + 1.0f,
_unreadLabel.frame.size.width, _unreadLabel.frame.size.height);
} else if (_unreadContainer != nil) {
_unreadContainer.hidden = true;
}
}
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

View File

@ -22,6 +22,7 @@
self.options = @[@(NotificationNamePreview),
@(NotificationNameNoPreview),
@(NotificationNoNameNoPreview)];
[super viewDidLoad];
}
@ -41,11 +42,22 @@
{
UITableViewCell *cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"NotificationSettingsOption"];
PropertyListPreferences *prefs = [Environment preferences];
[[cell textLabel] setText:[prefs nameForNotificationPreviewType:[[self.options objectAtIndex:(NSUInteger)indexPath.row] unsignedIntegerValue]]];
NSUInteger notifType = [[self.options objectAtIndex:(NSUInteger)indexPath.row] unsignedIntegerValue];
[[cell textLabel] setText:[prefs nameForNotificationPreviewType:notifType]];
NotificationType selectedNotifType = [prefs notificationPreviewType];
if (selectedNotifType == notifType) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
return cell;
}
- (NSString*)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
return NSLocalizedString(@"NOTIFICATIONS_FOOTER_WARNING", nil);
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[Environment.preferences setNotificationPreviewType:[[self.options objectAtIndex:(NSUInteger)indexPath.row] unsignedIntegerValue]];

View File

@ -20,13 +20,11 @@
@property (nonatomic) BOOL composeMessage;
@property (nonatomic, retain) IBOutlet UITableView *tableView;
@property (nonatomic, retain) IBOutlet UIButton *inboxButton;
@property (nonatomic, retain) IBOutlet UIButton *archiveButton;
@property (nonatomic, retain) IBOutlet UILabel *inboxCountLabel;
@property (nonatomic, strong) IBOutlet UILabel *emptyBoxLabel;
@property (nonatomic, retain) CallState* latestCall;
-(IBAction)selectedInbox:(id)sender;
-(IBAction)selectedArchive:(id)sender;
@end

View File

@ -48,6 +48,7 @@ static NSString* const kShowSignupFlowSegue = @"showSignupFlow";
@property (nonatomic, strong) YapDatabaseViewMappings *threadMappings;
@property (nonatomic) CellState viewingThreadsIn;
@property (nonatomic) long inboxCount;
@property (nonatomic, retain) UISegmentedControl *segmentedControl;
@end
@ -76,8 +77,21 @@ static NSString* const kShowSignupFlowSegue = @"showSignupFlow";
[[[Environment getCurrent] contactsManager].getObservableContacts watchLatestValue:^(id latestValue) {
[self.tableView reloadData];
} onThread:[NSThread mainThread] untilCancelled:nil];
self.title = NSLocalizedString(@"CONVERSATIONS_VIEW_TITLE", @"");
self.segmentedControl = [[UISegmentedControl alloc] initWithItems:@[NSLocalizedString(@"WHISPER_NAV_BAR_TITLE", nil),
NSLocalizedString(@"ARCHIVE_NAV_BAR_TITLE", nil)]];
[self.segmentedControl addTarget:self action:@selector(swappedSegmentedControl) forControlEvents:UIControlEventValueChanged];
self.navigationItem.titleView = self.segmentedControl;
[self.segmentedControl setSelectedSegmentIndex:0];
}
- (void)swappedSegmentedControl {
if (self.segmentedControl.selectedSegmentIndex == 0) {
[self selectedInbox:nil];
} else {
[self selectedArchive:nil];
}
}
-(void)viewWillAppear:(BOOL)animated
@ -186,7 +200,6 @@ static NSString* const kShowSignupFlowSegue = @"showSignupFlow";
- (void)tableViewCellTappedDelete:(NSIndexPath*)indexPath {
TSThread *thread = [self threadForIndexPath:indexPath];
if([thread isKindOfClass:[TSGroupThread class]]) {
DDLogDebug(@"leaving the group");
TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageBody:@"" attachments:[[NSMutableArray alloc] init]];
message.groupMetaMessage = TSGroupMessageQuit;
[[TSMessagesManager sharedManager] sendMessage:message inThread:thread];
@ -210,17 +223,20 @@ static NSString* const kShowSignupFlowSegue = @"showSignupFlow";
}
-(void) updateInboxCountLabel {
- (NSNumber*)updateInboxCountLabel {
NSUInteger numberOfItems = [[TSMessagesManager sharedManager] unreadMessagesCount];
NSNumber *badgeNumber = [NSNumber numberWithUnsignedInteger:numberOfItems];
NSString *badgeValue = nil;
NSNumber *badgeNumber = [NSNumber numberWithUnsignedInteger:numberOfItems];
NSString *unreadString = NSLocalizedString(@"WHISPER_NAV_BAR_TITLE", nil);
if (![badgeNumber isEqualToNumber:@0]) {
badgeValue = [badgeNumber stringValue];
NSString *badgeValue = [badgeNumber stringValue];
unreadString = [unreadString stringByAppendingFormat:@" (%@)", badgeValue];
}
[_segmentedControl setTitle:unreadString forSegmentAtIndex:0];
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:badgeNumber.integerValue];
self.inboxCountLabel.text = badgeValue;
return badgeNumber;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath{
@ -267,15 +283,11 @@ static NSString* const kShowSignupFlowSegue = @"showSignupFlow";
-(IBAction)selectedInbox:(id)sender {
self.viewingThreadsIn = kInboxState;
[self.inboxButton setSelected:YES];
[self.archiveButton setSelected:NO];
[self changeToGrouping:TSInboxGroup];
}
-(IBAction)selectedArchive:(id)sender {
self.viewingThreadsIn = kArchiveState;
[self.inboxButton setSelected:NO];
[self.archiveButton setSelected:YES];
[self changeToGrouping:TSArchiveGroup];
}
@ -324,8 +336,6 @@ static NSString* const kShowSignupFlowSegue = @"showSignupFlow";
return;
}
[self updateInboxCountLabel];
[self.tableView beginUpdates];
for (YapDatabaseViewSectionChange *sectionChange in sectionChanges)