From 3ecf0a75375b68a63433a826fd4c4c8c9786980d Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Thu, 7 Dec 2017 14:52:38 -0500 Subject: [PATCH] Cancelling dismisses share extension, remove "import with signal" Usinig the new ShareViewDelegate to dismiss the share extension, might have broken the "import with signal" functionality. But because we want to remove it anyway, I've done that now, rather than fix it up. // FREEBIE --- Signal.xcodeproj/project.pbxproj | 20 +- Signal/Signal-Info.plist | 6 - Signal/src/AppDelegate.m | 153 +------------ SignalMessaging/SignalMessaging.h | 2 +- .../AttachmentApprovalViewController.swift | 9 + .../SendExternalFileViewController.m | 212 ------------------ .../attachments/ShareViewDelegate.swift | 12 + ....h => SharingThreadPickerViewController.h} | 5 +- .../SharingThreadPickerViewController.m | 173 ++++++++++++++ .../contacts/SelectThreadViewController.h | 2 +- .../contacts/SelectThreadViewController.m | 13 +- .../SAEFailedViewController.swift | 4 +- .../SAELoadViewController.swift | 11 +- .../ShareViewController.swift | 18 +- 14 files changed, 244 insertions(+), 396 deletions(-) delete mode 100644 SignalMessaging/attachments/SendExternalFileViewController.m create mode 100644 SignalMessaging/attachments/ShareViewDelegate.swift rename SignalMessaging/attachments/{SendExternalFileViewController.h => SharingThreadPickerViewController.h} (56%) create mode 100644 SignalMessaging/attachments/SharingThreadPickerViewController.m diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 62f20f007..271f939a4 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -204,8 +204,8 @@ 45194F951FD7216600333B2C /* TSUnreadIndicatorInteraction.m in Sources */ = {isa = PBXBuildFile; fileRef = 34C42D651F4734ED0072EC04 /* TSUnreadIndicatorInteraction.m */; }; 45194F961FD7226300333B2C /* SelectThreadViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3400C7941EAF99F4008A8584 /* SelectThreadViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 451A13B11E13DED2000A50FD /* CallNotificationsAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 451A13B01E13DED2000A50FD /* CallNotificationsAdapter.swift */; }; - 451F8A311FD70DE9005CB9DA /* SendExternalFileViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3400C7911EAF89CD008A8584 /* SendExternalFileViewController.m */; }; - 451F8A321FD70DFA005CB9DA /* SendExternalFileViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3400C7901EAF89CD008A8584 /* SendExternalFileViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 451F8A311FD70DE9005CB9DA /* SharingThreadPickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3400C7911EAF89CD008A8584 /* SharingThreadPickerViewController.m */; }; + 451F8A321FD70DFA005CB9DA /* SharingThreadPickerViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 3400C7901EAF89CD008A8584 /* SharingThreadPickerViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 451F8A331FD71083005CB9DA /* SelectThreadViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3400C7951EAF99F4008A8584 /* SelectThreadViewController.m */; }; 451F8A341FD710C3005CB9DA /* ConversationSearcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 451777C71FD61554001225FF /* ConversationSearcher.swift */; }; 451F8A351FD710DE005CB9DA /* Searcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45360B8C1F9521F800FA666C /* Searcher.swift */; }; @@ -281,6 +281,7 @@ 45A6DAD61EBBF85500893231 /* ReminderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45A6DAD51EBBF85500893231 /* ReminderView.swift */; }; 45AE48511E0732D6004D96C2 /* TurnServerInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45AE48501E0732D6004D96C2 /* TurnServerInfo.swift */; }; 45BB93381E688E14001E3939 /* UIDevice+featureSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45BB93371E688E14001E3939 /* UIDevice+featureSupport.swift */; }; + 45BC829D1FD9C4B400011CF3 /* ShareViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45BC829C1FD9C4B400011CF3 /* ShareViewDelegate.swift */; }; 45BD60821DE9547E00A8F436 /* Contacts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 45BD60811DE9547E00A8F436 /* Contacts.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 45C0DC1B1E68FE9000E04C47 /* UIApplication+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45C0DC1A1E68FE9000E04C47 /* UIApplication+OWS.swift */; }; 45C0DC1E1E69011F00E04C47 /* UIStoryboard+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45C0DC1D1E69011F00E04C47 /* UIStoryboard+OWS.swift */; }; @@ -460,8 +461,8 @@ 1C93CF3971B64E8B6C1F9AC1 /* Pods-SignalShareExtension.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalShareExtension.test.xcconfig"; path = "Pods/Target Support Files/Pods-SignalShareExtension/Pods-SignalShareExtension.test.xcconfig"; sourceTree = ""; }; 1CE3CD5C23334683BDD3D78C /* Pods-Signal.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Signal.test.xcconfig"; path = "Pods/Target Support Files/Pods-Signal/Pods-Signal.test.xcconfig"; sourceTree = ""; }; 264242150E87D10A357DB07B /* Pods_SignalMessaging.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SignalMessaging.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 3400C7901EAF89CD008A8584 /* SendExternalFileViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SendExternalFileViewController.h; sourceTree = ""; }; - 3400C7911EAF89CD008A8584 /* SendExternalFileViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SendExternalFileViewController.m; sourceTree = ""; }; + 3400C7901EAF89CD008A8584 /* SharingThreadPickerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SharingThreadPickerViewController.h; sourceTree = ""; }; + 3400C7911EAF89CD008A8584 /* SharingThreadPickerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SharingThreadPickerViewController.m; sourceTree = ""; }; 3400C7941EAF99F4008A8584 /* SelectThreadViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SelectThreadViewController.h; sourceTree = ""; }; 3400C7951EAF99F4008A8584 /* SelectThreadViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SelectThreadViewController.m; sourceTree = ""; }; 3400C7971EAFB772008A8584 /* ThreadViewHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadViewHelper.h; sourceTree = ""; }; @@ -812,6 +813,7 @@ 45AE48501E0732D6004D96C2 /* TurnServerInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TurnServerInfo.swift; sourceTree = ""; }; 45B201741DAECBFD00C461E0 /* Signal-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Signal-Bridging-Header.h"; sourceTree = ""; }; 45BB93371E688E14001E3939 /* UIDevice+featureSupport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIDevice+featureSupport.swift"; sourceTree = ""; }; + 45BC829C1FD9C4B400011CF3 /* ShareViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewDelegate.swift; sourceTree = ""; }; 45BD60811DE9547E00A8F436 /* Contacts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Contacts.framework; path = System/Library/Frameworks/Contacts.framework; sourceTree = SDKROOT; }; 45C0DC1A1E68FE9000E04C47 /* UIApplication+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIApplication+OWS.swift"; sourceTree = ""; }; 45C0DC1D1E69011F00E04C47 /* UIStoryboard+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStoryboard+OWS.swift"; sourceTree = ""; }; @@ -1542,12 +1544,13 @@ 34B3F8471E8DF1700035BE1A /* FullImageViewController.h */, 34B3F8481E8DF1700035BE1A /* FullImageViewController.m */, 45E5471F1FD755E700DFC09E /* AttachmentApprovalViewController.swift */, - 3400C7901EAF89CD008A8584 /* SendExternalFileViewController.h */, - 3400C7911EAF89CD008A8584 /* SendExternalFileViewController.m */, + 3400C7901EAF89CD008A8584 /* SharingThreadPickerViewController.h */, + 3400C7911EAF89CD008A8584 /* SharingThreadPickerViewController.m */, 34533F161EA8D2070006114F /* OWSAudioAttachmentPlayer.h */, 34533F171EA8D2070006114F /* OWSAudioAttachmentPlayer.m */, 34D913491F62D4A500722898 /* SignalAttachment.swift */, 34CA1C281F7164F700E51C51 /* MediaMessageView.swift */, + 45BC829C1FD9C4B400011CF3 /* ShareViewDelegate.swift */, ); path = attachments; sourceTree = ""; @@ -2022,7 +2025,7 @@ 451F8A3C1FD71392005CB9DA /* UIUtil.h in Headers */, 346129D61FD20ADC00532771 /* UIViewController+OWS.h in Headers */, 451F8A401FD7145D005CB9DA /* OWSTableViewController.h in Headers */, - 451F8A321FD70DFA005CB9DA /* SendExternalFileViewController.h in Headers */, + 451F8A321FD70DFA005CB9DA /* SharingThreadPickerViewController.h in Headers */, 3461296F1FD1D74C00532771 /* Release.h in Headers */, 34612A061FD7238600532771 /* OWSContactsSyncing.h in Headers */, 34480B571FD0A7A400BC14EF /* OWSScrubbingLogFormatter.h in Headers */, @@ -2720,6 +2723,7 @@ 451F8A331FD71083005CB9DA /* SelectThreadViewController.m in Sources */, 454A965A1FD6017E008D2A0E /* SignalAttachment.swift in Sources */, 454A965B1FD601BF008D2A0E /* MediaMessageView.swift in Sources */, + 45BC829D1FD9C4B400011CF3 /* ShareViewDelegate.swift in Sources */, 3461295B1FD1D74C00532771 /* Environment.m in Sources */, 346129D51FD20ADC00532771 /* UIViewController+OWS.m in Sources */, 451F8A431FD714FE005CB9DA /* AvatarImageView.swift in Sources */, @@ -2745,7 +2749,7 @@ 3461293C1FD1D46A00532771 /* OWSMath.m in Sources */, 451F8A391FD711D6005CB9DA /* ContactsViewHelper.m in Sources */, 346129AF1FD1F5D900532771 /* SystemContactsFetcher.swift in Sources */, - 451F8A311FD70DE9005CB9DA /* SendExternalFileViewController.m in Sources */, + 451F8A311FD70DE9005CB9DA /* SharingThreadPickerViewController.m in Sources */, 451F8A411FD714B8005CB9DA /* ContactTableViewCell.m in Sources */, 346129C81FD2072E00532771 /* NSAttributedString+OWS.m in Sources */, ); diff --git a/Signal/Signal-Info.plist b/Signal/Signal-Info.plist index 46544c8ce..1266e64c8 100644 --- a/Signal/Signal-Info.plist +++ b/Signal/Signal-Info.plist @@ -15,12 +15,6 @@ Signal LSHandlerRank Alternate - LSItemContentTypes - - public.archive - public.content - public.data - CFBundleExecutable diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 665fa7048..4bd30344c 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -18,7 +18,6 @@ #import "PushManager.h" #import "RegistrationViewController.h" #import "Release.h" -#import "SendExternalFileViewController.h" #import "Signal-Swift.h" #import "SignalApp.h" #import "SignalsNavigationController.h" @@ -328,9 +327,10 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; } - (BOOL)application:(UIApplication *)application - openURL:(NSURL *)url - sourceApplication:(NSString *)sourceApplication - annotation:(id)annotation { + openURL:(NSURL *)url + sourceApplication:(NSString *)sourceApplication + annotation:(id)annotation +{ if ([url.scheme isEqualToString:kURLSchemeSGNLKey]) { if ([url.host hasPrefix:kURLHostVerifyPrefix] && ![TSAccountManager isRegistered]) { id signupController = SignalApp.sharedApp.signUpFlowNavigationController; @@ -341,160 +341,21 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; CodeVerificationViewController *cvvc = (CodeVerificationViewController *)controller; NSString *verificationCode = [url.path substringFromIndex:1]; [cvvc setVerificationCodeAndTryToVerify:verificationCode]; + return YES; } else { DDLogWarn(@"Not the verification view controller we expected. Got %@ instead", NSStringFromClass(controller.class)); } } } else { - DDLogWarn(@"Application opened with an unknown URL action: %@", url.host); - } - } else if ([url.scheme.lowercaseString isEqualToString:@"file"]) { - - if (SignalApp.sharedApp.callService.call != nil) { - DDLogWarn(@"%@ ignoring 'open with Signal' due to ongoing WebRTC call.", self.logTag); - return NO; - } - - NSString *filename = url.lastPathComponent; - if ([filename stringByDeletingPathExtension].length < 1) { - DDLogError(@"Application opened with URL invalid filename: %@", url); - [OWSAlerts showAlertWithTitle: - NSLocalizedString(@"EXPORT_WITH_SIGNAL_ERROR_TITLE", - @"Title for the alert indicating the 'export with signal' attachment had an error.") - message:NSLocalizedString(@"EXPORT_WITH_SIGNAL_ERROR_MESSAGE_INVALID_FILENAME", - @"Message for the alert indicating the 'export with signal' file had an " - @"invalid filename.")]; - return NO; - } - NSString *fileExtension = [filename pathExtension]; - if (fileExtension.length < 1) { - DDLogError(@"Application opened with URL missing file extension: %@", url); - [OWSAlerts showAlertWithTitle: - NSLocalizedString(@"EXPORT_WITH_SIGNAL_ERROR_TITLE", - @"Title for the alert indicating the 'export with signal' attachment had an error.") - message:NSLocalizedString(@"EXPORT_WITH_SIGNAL_ERROR_MESSAGE_UNKNOWN_TYPE", - @"Message for the alert indicating the 'export with signal' file had " - @"unknown type.")]; - return NO; - } - - - NSString *utiType; - NSError *typeError; - [url getResourceValue:&utiType forKey:NSURLTypeIdentifierKey error:&typeError]; - if (typeError) { - OWSFail(@"%@ Determining type of picked document at url: %@ failed with error: %@", - self.logTag, - url, - typeError); - return NO; - } - if (!utiType) { - OWSFail(@"%@ falling back to default filetype for picked document at url: %@", self.logTag, url); - utiType = (__bridge NSString *)kUTTypeData; - return NO; - } - - NSNumber *isDirectory; - NSError *isDirectoryError; - [url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:&isDirectoryError]; - if (isDirectoryError) { - OWSFail(@"%@ Determining if picked document at url: %@ was a directory failed with error: %@", - self.logTag, - url, - isDirectoryError); - return NO; - } else if ([isDirectory boolValue]) { - DDLogInfo(@"%@ User picked directory at url: %@", self.logTag, url); - DDLogError(@"Application opened with URL of unknown UTI type: %@", url); - [OWSAlerts - showAlertWithTitle: - NSLocalizedString(@"ATTACHMENT_PICKER_DOCUMENTS_PICKED_DIRECTORY_FAILED_ALERT_TITLE", - @"Alert title when picking a document fails because user picked a directory/bundle") - message: - NSLocalizedString(@"ATTACHMENT_PICKER_DOCUMENTS_PICKED_DIRECTORY_FAILED_ALERT_BODY", - @"Alert body when picking a document fails because user picked a directory/bundle")]; - return NO; - } - - DataSource *_Nullable dataSource = [DataSourcePath dataSourceWithURL:url]; - if (!dataSource) { - DDLogError(@"Application opened with URL with unloadable content: %@", url); - [OWSAlerts showAlertWithTitle: - NSLocalizedString(@"EXPORT_WITH_SIGNAL_ERROR_TITLE", - @"Title for the alert indicating the 'export with signal' attachment had an error.") - message:NSLocalizedString(@"EXPORT_WITH_SIGNAL_ERROR_MESSAGE_MISSING_DATA", - @"Message for the alert indicating the 'export with signal' data " - @"couldn't be loaded.")]; - return NO; + OWSFail(@"Application opened with an unknown URL action: %@", url.host); } - [dataSource setSourceFilename:filename]; - - SignalAttachment *attachment = [SignalAttachment attachmentWithDataSource:dataSource dataUTI:utiType]; - if (!attachment) { - DDLogError(@"Application opened with URL with invalid content: %@", url); - [OWSAlerts showAlertWithTitle: - NSLocalizedString(@"EXPORT_WITH_SIGNAL_ERROR_TITLE", - @"Title for the alert indicating the 'export with signal' attachment had an error.") - message:NSLocalizedString(@"EXPORT_WITH_SIGNAL_ERROR_MESSAGE_MISSING_ATTACHMENT", - @"Message for the alert indicating the 'export with signal' attachment " - @"couldn't be loaded.")]; - return NO; - } - if ([attachment hasError]) { - DDLogError(@"Application opened with URL with content error: %@ %@", url, [attachment errorName]); - [OWSAlerts showAlertWithTitle: - NSLocalizedString(@"EXPORT_WITH_SIGNAL_ERROR_TITLE", - @"Title for the alert indicating the 'export with signal' attachment had an error.") - message:[attachment errorName]]; - return NO; - } - DDLogInfo(@"Application opened with URL: %@", url); - - if ([TSAccountManager isRegistered]) { - dispatch_async(dispatch_get_main_queue(), ^{ - // Wait up to N seconds for database view registrations to - // complete. - [self showImportUIForAttachment:attachment remainingRetries:5]; - }); - } - - return YES; } else { - DDLogWarn(@"Application opened with an unknown URL scheme: %@", url.scheme); + OWSFail(@"Application opened with an unknown URL scheme: %@", url.scheme); } return NO; } -- (void)showImportUIForAttachment:(SignalAttachment *)attachment remainingRetries:(int)remainingRetries -{ - OWSAssert([NSThread isMainThread]); - OWSAssert(attachment); - OWSAssert(remainingRetries > 0); - - if ([TSDatabaseView hasPendingViewRegistrations]) { - if (remainingRetries < 1) { - DDLogInfo(@"Ignoring 'Import with Signal...' due to pending view registrations."); - } else { - DDLogInfo(@"Delaying 'Import with Signal...' due to pending view registrations."); - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)1.f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - if (![TSDatabaseView hasPendingViewRegistrations]) { - [self showImportUIForAttachment:attachment remainingRetries:remainingRetries - 1]; - } - }); - } - return; - } - - SendExternalFileViewController *viewController = [SendExternalFileViewController new]; - viewController.attachment = attachment; - UINavigationController *navigationController = - [[UINavigationController alloc] initWithRootViewController:viewController]; - [SignalApp.sharedApp.homeViewController presentTopLevelModalViewController:navigationController - animateDismissal:NO - animatePresentation:YES]; -} - (void)applicationDidBecomeActive:(UIApplication *)application { DDLogWarn(@"%@ applicationDidBecomeActive.", self.logTag); diff --git a/SignalMessaging/SignalMessaging.h b/SignalMessaging/SignalMessaging.h index 80b4d85d3..9d9900e8d 100644 --- a/SignalMessaging/SignalMessaging.h +++ b/SignalMessaging/SignalMessaging.h @@ -24,7 +24,7 @@ FOUNDATION_EXPORT const unsigned char SignalMessagingVersionString[]; #import #import #import -#import +#import #import #import #import diff --git a/SignalMessaging/attachments/AttachmentApprovalViewController.swift b/SignalMessaging/attachments/AttachmentApprovalViewController.swift index 104798edf..43321c737 100644 --- a/SignalMessaging/attachments/AttachmentApprovalViewController.swift +++ b/SignalMessaging/attachments/AttachmentApprovalViewController.swift @@ -166,6 +166,15 @@ public class AttachmentApprovalViewController: OWSViewController { } func sendPressed(sender: UIButton) { + + // FIXME + // this is just a temporary hack to provide some UI + // until we have a proper progress indicator + let activityIndicatorView = UIActivityIndicatorView() + view.addSubview(activityIndicatorView) + activityIndicatorView.autoCenterInSuperview() + activityIndicatorView.startAnimating() + self.delegate?.didApproveAttachment() } } diff --git a/SignalMessaging/attachments/SendExternalFileViewController.m b/SignalMessaging/attachments/SendExternalFileViewController.m deleted file mode 100644 index afa2ab702..000000000 --- a/SignalMessaging/attachments/SendExternalFileViewController.m +++ /dev/null @@ -1,212 +0,0 @@ -// -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. -// - -#import "SendExternalFileViewController.h" -#import "Environment.h" -#import "NSString+OWS.h" -#import "SignalApp.h" -#import "ThreadUtil.h" -#import "UIColor+OWS.h" -#import "UIFont+OWS.h" -#import "UIView+OWS.h" -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface SendExternalFileViewController () - -@property (nonatomic, readonly) OWSContactsManager *contactsManager; -@property (nonatomic, readonly) OWSMessageSender *messageSender; -@property (nonatomic) TSThread *thread; - -@end - -#pragma mark - - -@implementation SendExternalFileViewController - -- (instancetype)init -{ - if (self = [super init]) { - self.delegate = self; - } - return self; -} - -- (void)loadView -{ - [super loadView]; - - _contactsManager = [Environment current].contactsManager; - _messageSender = [Environment current].messageSender; - - self.title = NSLocalizedString(@"SEND_EXTERNAL_FILE_VIEW_TITLE", @"Title for the 'send external file' view."); -} - -- (BOOL)canSelectBlockedContact -{ - return NO; -} - -- (nullable UIView *)createHeaderWithSearchBar:(UISearchBar *)searchBar -{ - OWSAssert(searchBar) - - const CGFloat imageSize - = ScaleFromIPhone5To7Plus(40, 50); - const CGFloat imageLabelSpacing = ScaleFromIPhone5To7Plus(5, 8); - const CGFloat titleVSpacing = ScaleFromIPhone5To7Plus(10, 15); - const CGFloat contentVMargin = 20; - - UIView *header = [UIView new]; - header.backgroundColor = [UIColor whiteColor]; - - UIView *titleLabel = [self createTitleLabel]; - [titleLabel sizeToFit]; - [header addSubview:titleLabel]; - [titleLabel autoHCenterInSuperview]; - [titleLabel autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:contentVMargin]; - - UIView *fileView = [UIView new]; - [header addSubview:fileView]; - [fileView autoHCenterInSuperview]; - [fileView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:titleLabel withOffset:titleVSpacing]; - - UIImage *image = [UIImage imageNamed:@"file-thin-black-filled-large"]; - OWSAssert(image); - UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; - imageView.layer.minificationFilter = kCAFilterTrilinear; - imageView.layer.magnificationFilter = kCAFilterTrilinear; - imageView.layer.shadowColor = [UIColor blackColor].CGColor; - imageView.layer.shadowRadius = 2.f; - imageView.layer.shadowOpacity = 0.2f; - imageView.layer.shadowOffset = CGSizeMake(0.75f, 0.75f); - [fileView addSubview:imageView]; - [imageView autoSetDimension:ALDimensionWidth toSize:imageSize]; - [imageView autoSetDimension:ALDimensionHeight toSize:imageSize]; - [imageView autoPinLeadingToSuperview]; - [imageView autoPinEdgeToSuperviewEdge:ALEdgeTop]; - [imageView autoPinEdgeToSuperviewEdge:ALEdgeBottom]; - - UIView *fileNameLabel = [self createFileNameLabel]; - [fileView addSubview:fileNameLabel]; - [fileNameLabel autoAlignAxis:ALAxisHorizontal toSameAxisOfView:imageView]; - [fileNameLabel autoPinLeadingToTrailingOfView:imageView margin:imageLabelSpacing]; - [fileNameLabel autoPinTrailingToSuperview]; - - [header addSubview:searchBar]; - [searchBar autoPinWidthToSuperview]; - [searchBar autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:fileView withOffset:contentVMargin]; - [searchBar autoPinEdgeToSuperviewEdge:ALEdgeBottom]; - - // UITableViewController.tableHeaderView must have its height set. - header.frame = CGRectMake(0, - 0, - 0, - (contentVMargin * 2 + titleLabel.frame.size.height + titleVSpacing + imageSize + searchBar.frame.size.height)); - - return header; -} - -- (NSString *)formattedFileName -{ - // AppDelegate already verifies that this attachment has a valid filename. - // - // TODO: If we reuse this VC, for example to offer a "forward attachment to other thread", - // feature, this assumption would no longer apply. - OWSAssert(self.attachment); - NSString *filename = [self.attachment.sourceFilename ows_stripped]; - OWSAssert(filename.length > 0); - const NSUInteger kMaxFilenameLength = 20; - if (filename.length > kMaxFilenameLength) { - // Truncate the filename if necessary. - // - // TODO: Use l10n-safe truncation. - filename = [[[filename substringToIndex:kMaxFilenameLength / 2] stringByAppendingString:@"…"] - stringByAppendingString:[filename substringFromIndex:filename.length - kMaxFilenameLength / 2]]; - } - return filename; -} - -- (UIView *)createFileNameLabel -{ - UILabel *label = [UILabel new]; - label.text = [self formattedFileName]; - label.textColor = [UIColor ows_materialBlueColor]; - label.font = [UIFont ows_regularFontWithSize:ScaleFromIPhone5To7Plus(16.f, 20.f)]; - return label; -} - - -- (UIView *)createTitleLabel -{ - UILabel *label = [UILabel new]; - label.text - = NSLocalizedString(@"SEND_EXTERNAL_FILE_HEADER_TITLE", @"Header title for the 'send external file' view."); - label.textColor = [UIColor blackColor]; - label.font = [UIFont ows_mediumFontWithSize:ScaleFromIPhone5To7Plus(18.f, 20.f)]; - return label; -} - -#pragma mark - SelectThreadViewControllerDelegate - -- (void)threadWasSelected:(TSThread *)thread -{ - OWSAssert(self.attachment); - OWSAssert(thread); - self.thread = thread; - - __weak typeof(self) weakSelf = self; - - // FIXME SHARINGEXTENSION - // Handling safety number changes brings in a lot of machinery. - // How do we want to handle this? - // e.g. fingerprint scanning, etc. in the SAE or just redirect the user to the main app? - // BOOL didShowSNAlert = - // [SafetyNumberConfirmationAlert presentAlertIfNecessaryWithRecipientIds:thread.recipientIdentifiers - // confirmationText:[SafetyNumberStrings - // confirmSendButton] - // contactsManager:self.contactsManager - // completion:^(BOOL didConfirm) { - // if (didConfirm) { - // [weakSelf threadWasSelected:thread]; - // } - // }]; - // if (didShowSNAlert) { - // return; - // } - - AttachmentApprovalViewController *approvalVC = - [[AttachmentApprovalViewController alloc] initWithAttachment:self.attachment delegate:self]; - - [self.navigationController pushViewController:approvalVC animated:YES]; - - // FIXME This implemenation was for the "import with signal" functionality, - // and is now broken. We need to be sure to remove the "import with signal" functionality. - // [SignalApp.sharedApp presentConversationForThread:thread]; -} - -#pragma mark - AttachmentApprovalViewControllerDelegate - -- (void)didApproveAttachment -{ - [ThreadUtil addThreadToProfileWhitelistIfEmptyContactThread:self.thread]; - [ThreadUtil sendMessageWithAttachment:self.attachment inThread:self.thread messageSender:self.messageSender]; - - // FIXME SHARINGEXTENSION - // Show loading screen and dismiss entire share extension once entirely complete - [self.navigationController popViewControllerAnimated:YES]; -} - -- (void)didCancelAttachment -{ - [self.navigationController popViewControllerAnimated:YES]; -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/SignalMessaging/attachments/ShareViewDelegate.swift b/SignalMessaging/attachments/ShareViewDelegate.swift new file mode 100644 index 000000000..084597152 --- /dev/null +++ b/SignalMessaging/attachments/ShareViewDelegate.swift @@ -0,0 +1,12 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +import Foundation +// All Observer methods will be invoked from the main thread. +@objc +public protocol ShareViewDelegate: class { + func shareViewWasCompleted() + func shareViewWasCancelled() + func shareViewFailed(error: Error) +} diff --git a/SignalMessaging/attachments/SendExternalFileViewController.h b/SignalMessaging/attachments/SharingThreadPickerViewController.h similarity index 56% rename from SignalMessaging/attachments/SendExternalFileViewController.h rename to SignalMessaging/attachments/SharingThreadPickerViewController.h index 45f58c895..f221beaea 100644 --- a/SignalMessaging/attachments/SendExternalFileViewController.h +++ b/SignalMessaging/attachments/SharingThreadPickerViewController.h @@ -7,11 +7,14 @@ NS_ASSUME_NONNULL_BEGIN @class SignalAttachment; +@protocol ShareViewDelegate; -@interface SendExternalFileViewController : SelectThreadViewController +@interface SharingThreadPickerViewController : SelectThreadViewController @property (nonatomic) SignalAttachment *attachment; +- (instancetype)initWithShareViewDelegate:(id)shareViewDelegate; + @end NS_ASSUME_NONNULL_END diff --git a/SignalMessaging/attachments/SharingThreadPickerViewController.m b/SignalMessaging/attachments/SharingThreadPickerViewController.m new file mode 100644 index 000000000..d14781b62 --- /dev/null +++ b/SignalMessaging/attachments/SharingThreadPickerViewController.m @@ -0,0 +1,173 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import "SharingThreadPickerViewController.h" +#import "Environment.h" +#import "NSString+OWS.h" +#import "SignalApp.h" +#import "ThreadUtil.h" +#import "UIColor+OWS.h" +#import "UIFont+OWS.h" +#import "UIView+OWS.h" +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface SharingThreadPickerViewController () + +@property (nonatomic, readonly) OWSContactsManager *contactsManager; +@property (nonatomic, readonly) OWSMessageSender *messageSender; +@property (nonatomic) TSThread *thread; +@property (nonatomic, readonly, weak) id shareViewDelegate; + +@end + +#pragma mark - + +@implementation SharingThreadPickerViewController + +- (instancetype)initWithShareViewDelegate:(id)shareViewDelegate; +{ + self = [super init]; + if (!self) { + return self; + } + + _shareViewDelegate = shareViewDelegate; + self.selectThreadViewDelegate = self; + + return self; +} + +- (void)loadView +{ + [super loadView]; + + _contactsManager = [Environment current].contactsManager; + _messageSender = [Environment current].messageSender; + + self.title = NSLocalizedString(@"SEND_EXTERNAL_FILE_VIEW_TITLE", @"Title for the 'send external file' view."); +} + +- (BOOL)canSelectBlockedContact +{ + return NO; +} + +- (nullable UIView *)createHeaderWithSearchBar:(UISearchBar *)searchBar +{ + OWSAssert(searchBar) + + const CGFloat contentVMargin + = 0; + + UIView *header = [UIView new]; + header.backgroundColor = [UIColor whiteColor]; + + UIButton *cancelShareButton = [UIButton buttonWithType:UIButtonTypeSystem]; + [header addSubview:cancelShareButton]; + + [cancelShareButton setTitle:[CommonStrings cancelButton] forState:UIControlStateNormal]; + cancelShareButton.userInteractionEnabled = YES; + + [cancelShareButton autoPinEdgeToSuperviewMargin:ALEdgeLeading]; + [cancelShareButton autoPinEdgeToSuperviewMargin:ALEdgeBottom]; + [cancelShareButton setCompressionResistanceHigh]; + [cancelShareButton setContentHuggingHigh]; + + [cancelShareButton addTarget:self + action:@selector(didTapCancelShareButton) + forControlEvents:UIControlEventTouchUpInside]; + + [header addSubview:searchBar]; + [searchBar autoPinEdge:ALEdgeLeading toEdge:ALEdgeTrailing ofView:cancelShareButton withOffset:6]; + [searchBar autoPinEdgeToSuperviewEdge:ALEdgeTrailing]; + [searchBar autoPinEdgeToSuperviewEdge:ALEdgeTop]; + [searchBar autoPinEdgeToSuperviewEdge:ALEdgeBottom]; + + UIView *borderView = [UIView new]; + [header addSubview:borderView]; + + borderView.backgroundColor = [UIColor colorWithRGBHex:0xbbbbbb]; + [borderView autoSetDimension:ALDimensionHeight toSize:0.5]; + [borderView autoPinWidthToSuperview]; + [borderView autoPinEdgeToSuperviewEdge:ALEdgeBottom]; + + // UITableViewController.tableHeaderView must have its height set. + header.frame = CGRectMake(0, 0, 0, (contentVMargin * 2 + searchBar.frame.size.height)); + + return header; +} + +#pragma mark - SelectThreadViewControllerDelegate + +- (void)threadWasSelected:(TSThread *)thread +{ + OWSAssert(self.attachment); + OWSAssert(thread); + self.thread = thread; + + __weak typeof(self) weakSelf = self; + + // FIXME SHARINGEXTENSION + // Handling safety number changes brings in a lot of machinery. + // How do we want to handle this? + // e.g. fingerprint scanning, etc. in the SAE or just redirect the user to the main app? + // BOOL didShowSNAlert = + // [SafetyNumberConfirmationAlert presentAlertIfNecessaryWithRecipientIds:thread.recipientIdentifiers + // confirmationText:[SafetyNumberStrings + // confirmSendButton] + // contactsManager:self.contactsManager + // completion:^(BOOL didConfirm) { + // if (didConfirm) { + // [weakSelf threadWasSelected:thread]; + // } + // }]; + // if (didShowSNAlert) { + // return; + // } + + AttachmentApprovalViewController *approvalVC = + [[AttachmentApprovalViewController alloc] initWithAttachment:self.attachment delegate:self]; + + [self.navigationController pushViewController:approvalVC animated:YES]; +} + +- (void)didTapCancelShareButton +{ + DDLogDebug(@"%@ tapped cancel share button", self.logTag); + [self cancelShareExperience]; +} + +- (void)cancelShareExperience +{ + [self.shareViewDelegate shareViewWasCancelled]; +} + +#pragma mark - AttachmentApprovalViewControllerDelegate + +- (void)didApproveAttachment +{ + [ThreadUtil addThreadToProfileWhitelistIfEmptyContactThread:self.thread]; + [ThreadUtil sendMessageWithAttachment:self.attachment inThread:self.thread messageSender:self.messageSender]; + + // This is just a temporary hack while testing to hopefully not dismiss too early. + // FIXME Show progress dialog + // FIXME don't dismiss until sending is complete + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self.shareViewDelegate shareViewWasCompleted]; + }); +} + +- (void)didCancelAttachment +{ + [self cancelShareExperience]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/SignalMessaging/contacts/SelectThreadViewController.h b/SignalMessaging/contacts/SelectThreadViewController.h index 0cee930fd..f2b11da7f 100644 --- a/SignalMessaging/contacts/SelectThreadViewController.h +++ b/SignalMessaging/contacts/SelectThreadViewController.h @@ -24,7 +24,7 @@ NS_ASSUME_NONNULL_BEGIN // entering a phone number or picking from your contacts. @interface SelectThreadViewController : OWSViewController -@property (nonatomic, weak) id delegate; +@property (nonatomic, weak) id selectThreadViewDelegate; @end diff --git a/SignalMessaging/contacts/SelectThreadViewController.m b/SignalMessaging/contacts/SelectThreadViewController.m index ded70992d..1b05f3c6a 100644 --- a/SignalMessaging/contacts/SelectThreadViewController.m +++ b/SignalMessaging/contacts/SelectThreadViewController.m @@ -71,7 +71,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)createViews { - OWSAssert(self.delegate); + OWSAssert(self.selectThreadViewDelegate); // Search UISearchBar *searchBar = [UISearchBar new]; @@ -82,7 +82,7 @@ NS_ASSUME_NONNULL_BEGIN searchBar.backgroundColor = [UIColor whiteColor]; [searchBar sizeToFit]; - UIView *header = [self.delegate createHeaderWithSearchBar:searchBar]; + UIView *header = [self.selectThreadViewDelegate createHeaderWithSearchBar:searchBar]; // Table _tableViewController = [OWSTableViewController new]; @@ -150,7 +150,7 @@ NS_ASSUME_NONNULL_BEGIN } customRowHeight:[ContactTableViewCell rowHeight] actionBlock:^{ - [weakSelf.delegate threadWasSelected:thread]; + [weakSelf.selectThreadViewDelegate threadWasSelected:thread]; }]]; } @@ -204,11 +204,12 @@ NS_ASSUME_NONNULL_BEGIN - (void)signalAccountWasSelected:(SignalAccount *)signalAccount { OWSAssert(signalAccount); - OWSAssert(self.delegate); + OWSAssert(self.selectThreadViewDelegate); ContactsViewHelper *helper = self.contactsViewHelper; - if ([helper isRecipientIdBlocked:signalAccount.recipientId] && ![self.delegate canSelectBlockedContact]) { + if ([helper isRecipientIdBlocked:signalAccount.recipientId] + && ![self.selectThreadViewDelegate canSelectBlockedContact]) { __weak SelectThreadViewController *weakSelf = self; [BlockListUIUtils showUnblockSignalAccountActionSheet:signalAccount @@ -230,7 +231,7 @@ NS_ASSUME_NONNULL_BEGIN }]; OWSAssert(thread); - [self.delegate threadWasSelected:thread]; + [self.selectThreadViewDelegate threadWasSelected:thread]; } #pragma mark - Filter diff --git a/SignalShareExtension/SAEFailedViewController.swift b/SignalShareExtension/SAEFailedViewController.swift index 8841fe755..418f709d1 100644 --- a/SignalShareExtension/SAEFailedViewController.swift +++ b/SignalShareExtension/SAEFailedViewController.swift @@ -8,7 +8,7 @@ import PureLayout // All Observer methods will be invoked from the main thread. protocol SAEFailedViewDelegate: class { - func shareExtensionWasCancelled() + func shareViewWasCancelled() } class SAEFailedViewController: UIViewController { @@ -92,6 +92,6 @@ class SAEFailedViewController: UIViewController { owsFail("\(self.logTag) missing delegate") return } - delegate.shareExtensionWasCancelled() + delegate.shareViewWasCancelled() } } diff --git a/SignalShareExtension/SAELoadViewController.swift b/SignalShareExtension/SAELoadViewController.swift index 07d9e0f7d..9857d5e93 100644 --- a/SignalShareExtension/SAELoadViewController.swift +++ b/SignalShareExtension/SAELoadViewController.swift @@ -6,20 +6,15 @@ import UIKit import SignalMessaging import PureLayout -// All Observer methods will be invoked from the main thread. -protocol SAELoadViewDelegate: class { - func shareExtensionWasCancelled() -} - class SAELoadViewController: UIViewController { - weak var delegate: SAELoadViewDelegate? + weak var delegate: ShareViewDelegate? var activityIndicator: UIActivityIndicatorView? // MARK: Initializers and Factory Methods - init(delegate: SAELoadViewDelegate) { + init(delegate: ShareViewDelegate) { self.delegate = delegate super.init(nibName: nil, bundle: nil) } @@ -81,6 +76,6 @@ class SAELoadViewController: UIViewController { owsFail("\(self.logTag) missing delegate") return } - delegate.shareExtensionWasCancelled() + delegate.shareViewWasCancelled() } } diff --git a/SignalShareExtension/ShareViewController.swift b/SignalShareExtension/ShareViewController.swift index 44617b070..10d2c0bbe 100644 --- a/SignalShareExtension/ShareViewController.swift +++ b/SignalShareExtension/ShareViewController.swift @@ -10,7 +10,7 @@ import SignalServiceKit import PromiseKit @objc -public class ShareViewController: UINavigationController, SAELoadViewDelegate, SAEFailedViewDelegate { +public class ShareViewController: UINavigationController, ShareViewDelegate, SAEFailedViewDelegate { private var hasInitialRootViewController = false private var isReadyForAppExtensions = false @@ -335,19 +335,27 @@ public class ShareViewController: UINavigationController, SAELoadViewDelegate, S Logger.flush() } - // MARK: SAELoadViewDelegate, SAEFailedViewDelegate + // MARK: ShareViewDelegate, SAEFailedViewDelegate - public func shareExtensionWasCancelled() { + public func shareViewWasCompleted() { self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil) } + public func shareViewWasCancelled() { + self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil) + } + + public func shareViewFailed(error: Error) { + self.extensionContext!.cancelRequest(withError: error) + } + // MARK: Helpers private func presentConversationPicker() { // pause any animation revealing the "loading" screen self.view.layer.removeAllAnimations() self.buildAttachment().then { attachment -> Void in - let conversationPicker = SendExternalFileViewController() + let conversationPicker = SharingThreadPickerViewController(shareViewDelegate: self) let navigationController = UINavigationController(rootViewController: conversationPicker) navigationController.isNavigationBarHidden = true conversationPicker.attachment = attachment @@ -358,7 +366,7 @@ public class ShareViewController: UINavigationController, SAELoadViewDelegate, S OWSAlerts.showAlert(withTitle: alertTitle, message: error.localizedDescription, buttonTitle: CommonStrings.cancelButton) { _ in - self.shareExtensionWasCancelled() + self.shareViewWasCancelled() } owsFail("\(self.logTag) building attachment failed with error: \(error)") }.retainUntilComplete()