diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index d19645831..06abc7c47 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -415,6 +415,7 @@ 4C13C9F620E57BA30089A98B /* ColorPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C13C9F520E57BA30089A98B /* ColorPickerViewController.swift */; }; 4C20B2B720CA0034001BAC90 /* ThreadViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4542DF51208B82E9007B4E76 /* ThreadViewModel.swift */; }; 4C20B2B920CA10DE001BAC90 /* ConversationSearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C20B2B820CA10DE001BAC90 /* ConversationSearchViewController.swift */; }; + 4C4AEC4520EC343B0020E72B /* DismissableTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C4AEC4420EC343B0020E72B /* DismissableTextField.swift */; }; 70377AAB1918450100CAF501 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70377AAA1918450100CAF501 /* MobileCoreServices.framework */; }; 768A1A2B17FC9CD300E00ED8 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 768A1A2A17FC9CD300E00ED8 /* libz.dylib */; }; 76C87F19181EFCE600C4ACAB /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76C87F18181EFCE600C4ACAB /* MediaPlayer.framework */; }; @@ -1072,6 +1073,7 @@ 45FDA43420A4D22700396358 /* OWSNavigationBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OWSNavigationBar.swift; sourceTree = ""; }; 4C13C9F520E57BA30089A98B /* ColorPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPickerViewController.swift; sourceTree = ""; }; 4C20B2B820CA10DE001BAC90 /* ConversationSearchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationSearchViewController.swift; sourceTree = ""; }; + 4C4AEC4420EC343B0020E72B /* DismissableTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DismissableTextField.swift; sourceTree = ""; }; 69349DE607F5BA6036C9AC60 /* Pods-SignalShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalShareExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SignalShareExtension/Pods-SignalShareExtension.debug.xcconfig"; sourceTree = ""; }; 70377AAA1918450100CAF501 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; 748A5CAEDD7C919FC64C6807 /* Pods_SignalTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SignalTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -2160,6 +2162,7 @@ 45A6DAD51EBBF85500893231 /* ReminderView.swift */, 450D19111F85236600970622 /* RemoteVideoView.h */, 450D19121F85236600970622 /* RemoteVideoView.m */, + 4C4AEC4420EC343B0020E72B /* DismissableTextField.swift */, ); name = Views; path = views; @@ -3247,6 +3250,7 @@ 34E3EF101EFC2684007F6822 /* DebugUIPage.m in Sources */, 340FC8CD20518C77007AEB0F /* OWSBackupJob.m in Sources */, 34D1F0AE1F867BFC0066283D /* OWSMessageCell.m in Sources */, + 4C4AEC4520EC343B0020E72B /* DismissableTextField.swift in Sources */, 451686AB1F520CDA00AC3D4B /* MultiDeviceProfileKeyUpdateJob.swift in Sources */, 45D231771DC7E8F10034FA89 /* SessionResetJob.swift in Sources */, 340FC8A9204DAC8D007AEB0F /* NotificationSettingsOptionsViewController.m in Sources */, diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index df3595799..c2dbe5e0d 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -231,6 +231,13 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations [reminderStackView autoPinWidthToSuperview]; [reminderStackView autoPinToTopLayoutGuideOfViewController:self withInset:0]; + // Fixes ambiguous height of an empty stack view pinned above a scroll view on iOS10. + // Without this users would sometimes see the empty stackview take up most of their screen. + [NSLayoutConstraint autoSetPriority:UILayoutPriorityDefaultLow + forConstraints:^{ + [reminderStackView autoSetDimension:ALDimensionHeight toSize:0]; + }]; + __weak HomeViewController *weakSelf = self; ReminderView *deregisteredView = [ReminderView nagWithText:NSLocalizedString(@"DEREGISTRATION_WARNING", diff --git a/Signal/src/ViewControllers/Registration/RegistrationViewController.m b/Signal/src/ViewControllers/Registration/RegistrationViewController.m index 9c990d626..dd957f5d7 100644 --- a/Signal/src/ViewControllers/Registration/RegistrationViewController.m +++ b/Signal/src/ViewControllers/Registration/RegistrationViewController.m @@ -189,7 +189,13 @@ NSString *const kKeychainKey_LastRegisteredPhoneNumber = @"kKeychainKey_LastRegi [phoneNumberLabel autoVCenterInSuperview]; [phoneNumberLabel autoPinLeadingToSuperviewMargin]; - UITextField *phoneNumberTextField = [UITextField new]; + UITextField *phoneNumberTextField; + if (UIDevice.currentDevice.isShorterThanIPhone5) { + phoneNumberTextField = [DismissableTextField new]; + } else { + phoneNumberTextField = [UITextField new]; + } + phoneNumberTextField.textAlignment = NSTextAlignmentRight; phoneNumberTextField.delegate = self; phoneNumberTextField.keyboardType = UIKeyboardTypeNumberPad; diff --git a/Signal/src/views/DismissableTextField.swift b/Signal/src/views/DismissableTextField.swift new file mode 100644 index 000000000..75e98f2a3 --- /dev/null +++ b/Signal/src/views/DismissableTextField.swift @@ -0,0 +1,66 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation + +protocol DismissInputBarDelegate: class { + func dismissInputBarDidTapDismiss(_ dismissInputBar: DismissInputBar) +} + +class DismissInputBar: UIToolbar { + + weak var dismissDelegate: DismissInputBarDelegate? + + override init(frame: CGRect) { + super.init(frame: frame) + + let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) + let dismissButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(didTapDone)) + dismissButton.imageInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 40) + dismissButton.tintColor = UIColor.ows_systemPrimaryButton + + self.items = [spacer, dismissButton] + self.isTranslucent = false + self.isOpaque = true + self.barTintColor = UIColor.ows_toolbarBackground + + self.autoresizingMask = .flexibleHeight + self.translatesAutoresizingMaskIntoConstraints = false + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc + public func didTapDone() { + self.dismissDelegate?.dismissInputBarDidTapDismiss(self) + } +} + +@objc +public class DismissableTextField: UITextField, DismissInputBarDelegate { + + private let dismissBar: DismissInputBar + + override init(frame: CGRect) { + self.dismissBar = DismissInputBar() + + super.init(frame: frame) + + self.inputAccessoryView = dismissBar + + dismissBar.dismissDelegate = self + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: DismissInputBarDelegate + + func dismissInputBarDidTapDismiss(_ dismissInputBar: DismissInputBar) { + self.resignFirstResponder() + } +} diff --git a/SignalMessaging/categories/UIDevice+featureSupport.swift b/SignalMessaging/categories/UIDevice+featureSupport.swift index 2cec7f614..b436d99b6 100644 --- a/SignalMessaging/categories/UIDevice+featureSupport.swift +++ b/SignalMessaging/categories/UIDevice+featureSupport.swift @@ -34,6 +34,11 @@ public extension UIDevice { } } + @objc + public var isShorterThanIPhone5: Bool { + return UIScreen.main.nativeBounds.height < 1136 + } + @objc public var isIPad: Bool { let isNativeIPad = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.pad diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index f499fe7bb..5a4ab31a5 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -625,11 +625,12 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; } [self sendMessageToService:message - recipient:recipient - thread:thread - attempts:OWSMessageSenderRetryAttempts - success:successHandler - failure:failureHandler]; + recipient:recipient + thread:thread + attempts:OWSMessageSenderRetryAttempts + useWebsocketIfAvailable:YES + success:successHandler + failure:failureHandler]; } else { // Neither a group nor contact thread? This should never happen. OWSFail(@"%@ Unknown message type: %@", self.logTag, NSStringFromClass([message class])); @@ -652,6 +653,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; recipient:recipient thread:thread attempts:OWSMessageSenderRetryAttempts + useWebsocketIfAvailable:YES success:^{ [futureSource trySetResult:@1]; } @@ -772,7 +774,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; - (void)sendMessageToService:(TSOutgoingMessage *)message recipient:(SignalRecipient *)recipient thread:(nullable TSThread *)thread - attempts:(int)remainingAttempts + attempts:(int)remainingAttemptsParam + useWebsocketIfAvailable:(BOOL)useWebsocketIfAvailable success:(void (^)(void))successHandler failure:(RetryableFailureHandler)failureHandler { @@ -806,7 +809,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; return failureHandler(error); } - if (remainingAttempts <= 0) { + if (remainingAttemptsParam <= 0) { // We should always fail with a specific error. OWSProdFail([OWSAnalyticsEvents messageSenderErrorGenericSendFailure]); @@ -814,7 +817,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; [error setIsRetryable:YES]; return failureHandler(error); } - remainingAttempts -= 1; + int remainingAttempts = remainingAttemptsParam - 1; NSArray *deviceMessages; @try { @@ -946,15 +949,13 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; }); return; - } else if (mayHaveLinkedDevices) { + } else if (mayHaveLinkedDevices && !hasDeviceMessages) { // We may have just linked a new secondary device which is not yet reflected in // the SignalRecipient that corresponds to ourself. Proceed. Client should learn // of new secondary devices via 409 "Mismatched devices" response. - DDLogWarn(@"%@ sync message has no device messages but account has secondary devices.", self.logTag); - } else if (hasDeviceMessages) { + DDLogWarn(@"%@ account has secondary devices, but sync message has no device messages", self.logTag); + } else if (!mayHaveLinkedDevices && hasDeviceMessages) { OWSFail(@"%@ sync message has device messages for unknown secondary devices.", self.logTag); - } else { - // Account has secondary devices; proceed as usual. } } else { OWSAssert(deviceMessages.count > 0); @@ -978,7 +979,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; messages:deviceMessages relay:recipient.relay timeStamp:message.timestamp]; - if (TSSocketManager.canMakeRequests) { + if (useWebsocketIfAvailable && TSSocketManager.canMakeRequests) { [TSSocketManager.sharedManager makeRequest:request success:^(id _Nullable responseObject) { [self messageSendDidSucceed:message @@ -988,19 +989,21 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; success:successHandler]; } failure:^(NSInteger statusCode, NSData *_Nullable responseData, NSError *error) { - [self messageSendDidFail:message - recipient:recipient - thread:thread - isLocalNumber:isLocalNumber - deviceMessages:deviceMessages - remainingAttempts:remainingAttempts - statusCode:statusCode - error:error - responseData:responseData - success:successHandler - failure:failureHandler]; + // Websockets can fail in different ways, so we don't decrement remainingAttempts for websocket failure. + // Instead we fall back to REST, which will decrement retries. + // e.g. after linking a new device, sync messages will fail until the websocket re-opens. + [self sendMessageToService:message + recipient:recipient + thread:thread + attempts:remainingAttemptsParam + useWebsocketIfAvailable:NO + success:successHandler + failure:failureHandler]; }]; } else { + if (!useWebsocketIfAvailable && TSSocketManager.canMakeRequests) { + DDLogDebug(@"%@ in %s falling back to REST since first attempt failed.", self.logTag, __PRETTY_FUNCTION__); + } [self.networkManager makeRequest:request success:^(NSURLSessionDataTask *task, id responseObject) { [self messageSendDidSucceed:message @@ -1098,11 +1101,12 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; dispatch_async([OWSDispatch sendingQueue], ^{ DDLogDebug(@"%@ Retrying: %@", self.logTag, message.debugDescription); [self sendMessageToService:message - recipient:recipient - thread:thread - attempts:remainingAttempts - success:successHandler - failure:failureHandler]; + recipient:recipient + thread:thread + attempts:remainingAttempts + useWebsocketIfAvailable:NO + success:successHandler + failure:failureHandler]; }); }; @@ -1250,6 +1254,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; recipient:[SignalRecipient selfRecipient] thread:message.thread attempts:OWSMessageSenderRetryAttempts + useWebsocketIfAvailable:YES success:^{ DDLogInfo(@"Successfully sent sync transcript."); } diff --git a/SignalShareExtension/Info.plist b/SignalShareExtension/Info.plist index 4b8f10bb6..ee9e7c2be 100644 --- a/SignalShareExtension/Info.plist +++ b/SignalShareExtension/Info.plist @@ -60,11 +60,8 @@ SUBQUERY ( $extensionItem.attachments, $attachment, - ( ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.data" || ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url" - ) - AND NOT (ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.vcard") ).@count >= 1 ).@count == 1