DocumentPicker.m 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2015-2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. #import "DocumentPicker.h"
  21. #import "UTIConverter.h"
  22. #import "ModalPresenter.h"
  23. #import "BundleUtil.h"
  24. #import "MBProgressHUD.h"
  25. #import "FeatureMaskChecker.h"
  26. #import "ContactUtil.h"
  27. #import "FileMessageSender.h"
  28. #import <Contacts/Contacts.h>
  29. #import <ContactsUI/ContactsUI.h>
  30. #import <CocoaLumberjack/CocoaLumberjack.h>
  31. @interface DocumentPicker () <UIDocumentPickerDelegate, UIDocumentMenuDelegate, UploadProgressDelegate, CNContactPickerDelegate>
  32. @property UIViewController *presentingViewController;
  33. @property Conversation *conversation;
  34. @end
  35. @implementation DocumentPicker
  36. static DocumentPicker *pickerStrongReference;
  37. + (instancetype)documentPickerForViewController:(UIViewController *)presentingViewController conversation:(Conversation *)conversation {
  38. DocumentPicker *picker = [[DocumentPicker alloc] init];
  39. picker.presentingViewController = presentingViewController;
  40. picker.conversation = conversation;
  41. return picker;
  42. }
  43. - (void)show {
  44. NSSet *conversations = [NSSet setWithObject:_conversation];
  45. FeatureMaskChecker *featureMaskChecker = [[FeatureMaskChecker alloc] init];
  46. [featureMaskChecker checkFileTransferFor:conversations presentAlarmOn:_presentingViewController onSuccess:^{
  47. [self showPicker];
  48. } onFailure:^{
  49. ;//nop
  50. }];
  51. }
  52. - (void)showPicker {
  53. NSArray *types = @[UTTYPE_ITEM, UTTYPE_DATA, UTTYPE_CONTENT, UTTYPE_ARCHIVE, UTTYPE_CONTACT, UTTYPE_MESSAGE];
  54. UIDocumentMenuViewController *controller = [[UIDocumentMenuViewController alloc] initWithDocumentTypes:types inMode:UIDocumentPickerModeImport];
  55. controller.delegate = self;
  56. NSString *title = [BundleUtil localizedStringForKey:@"contacts"];
  57. UIImage *image = [BundleUtil imageNamed:@"ThumbBusinessContact.png"];
  58. [controller addOptionWithTitle:title image:image order:UIDocumentMenuOrderFirst handler:^{
  59. [self checkPermissionAndShowContactPicker];
  60. }];
  61. pickerStrongReference = self;
  62. [self showController:controller];
  63. }
  64. - (void)showController:(UIViewController *)controller {
  65. if (CGRectIsNull(_popoverSourceRect)) {
  66. [ModalPresenter present:controller on:_presentingViewController];
  67. } else {
  68. [ModalPresenter present:controller on:_presentingViewController fromRect:_popoverSourceRect inView:_presentingViewController.view];
  69. }
  70. }
  71. - (void)checkPermissionAndShowContactPicker {
  72. if ([CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts] == CNAuthorizationStatusAuthorized) {
  73. [self showContactPicker];
  74. }
  75. else if ([CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts] == CNAuthorizationStatusDenied) {
  76. [self showContactAlert];
  77. }
  78. else {
  79. CNContactStore *cnAddressBook = [CNContactStore new];
  80. [cnAddressBook requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
  81. if (granted) {
  82. [self showContactPicker];
  83. } else {
  84. [self showContactAlert];
  85. }
  86. }];
  87. }
  88. }
  89. - (void)showContactPicker {
  90. CNContactPickerViewController *picker = [[CNContactPickerViewController alloc] init];
  91. picker.delegate = self;
  92. picker.modalPresentationStyle = UIModalPresentationFormSheet;
  93. [self showController:picker];
  94. }
  95. - (void)showContactAlert {
  96. NSString *title = NSLocalizedString(@"no_contacts_permission_title", nil);
  97. NSString *message = NSLocalizedString(@"no_contacts_permission_message", nil);
  98. [self showAlertWithTitle:title message:message closeOnOk:YES];
  99. }
  100. - (void)sendItem:(URLSenderItem *)item {
  101. NSString *messageFormat = NSLocalizedString(@"send_file_message", nil);
  102. NSString *message = [NSString stringWithFormat:messageFormat, [item getName], _conversation.displayName];
  103. NSString *title = NSLocalizedString(@"send_file_title", nil);
  104. __block UITextField *captionTextField = nil;
  105. UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
  106. [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
  107. if (@available(iOS 13.0, *)) {
  108. textField.textColor = [Colors fontNormal];
  109. }
  110. textField.placeholder = [BundleUtil localizedStringForKey:@"optional_caption"];
  111. captionTextField = textField;
  112. }];
  113. NSString *ok = [BundleUtil localizedStringForKey:@"ok"];
  114. NSString *cancel = [BundleUtil localizedStringForKey:@"cancel"];
  115. UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:ok style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
  116. [MBProgressHUD showHUDAddedTo:_presentingViewController.view animated:YES];
  117. if (captionTextField.text.length > 0) {
  118. item.caption = captionTextField.text;
  119. }
  120. FileMessageSender *sender = [[FileMessageSender alloc] init];
  121. sender.uploadProgressDelegate = self;
  122. [sender sendItem:item inConversation:_conversation];
  123. }];
  124. [alertController addAction:defaultAction];
  125. UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:cancel style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
  126. pickerStrongReference = nil;
  127. }];
  128. [alertController addAction:cancelAction];
  129. [_presentingViewController presentViewController:alertController animated:YES completion:nil];
  130. }
  131. #pragma mark - UIDocumentPickerDelegate
  132. - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url {
  133. URLSenderItem *item = [URLSenderItemCreator getSenderItemFor:url];
  134. [self sendItem:item];
  135. }
  136. #pragma mark - UIDocumentMenuDelegate
  137. - (void)documentMenu:(UIDocumentMenuViewController *)documentMenu didPickDocumentPicker:(UIDocumentPickerViewController *)documentPicker {
  138. documentPicker.delegate = self;
  139. [self showController:documentPicker];
  140. }
  141. #pragma mark - UploadProgressDelegate
  142. - (BOOL)blobMessageSenderUploadShouldCancel:(BlobMessageSender *)blobMessageSender {
  143. return NO;
  144. }
  145. - (void)blobMessageSender:(BlobMessageSender *)blobMessageSender uploadProgress:(NSNumber *)progress forMessage:(BaseMessage *)message {
  146. // hide as soon as progress starts which is visible in message bubble
  147. [MBProgressHUD hideHUDForView:_presentingViewController.view animated:YES];
  148. }
  149. - (void)blobMessageSender:(BlobMessageSender *)blobMessageSender uploadFailedForMessage:(BaseMessage *)message error:(UploadError)error {
  150. [MBProgressHUD hideHUDForView:_presentingViewController.view animated:YES];
  151. NSString *errorTitle = [BundleUtil localizedStringForKey:@"error_sending_failed"];
  152. NSString *errorMessage = [FileMessageSender messageForError:error];
  153. [self showAlertWithTitle:errorTitle message:errorMessage closeOnOk:NO];
  154. }
  155. - (void)blobMessageSender:(BlobMessageSender *)blobMessageSender uploadSucceededForMessage:(BaseMessage *)message {
  156. pickerStrongReference = nil;
  157. }
  158. - (void)showAlertWithTitle:(NSString *)title message:(NSString *)message closeOnOk:(BOOL)closeOnOk {
  159. [UIAlertTemplate showAlertWithOwner:_presentingViewController title:title message:message actionOk:^(UIAlertAction * _Nonnull okAction) {
  160. pickerStrongReference = nil;
  161. }];
  162. }
  163. #pragma mark - People picker delegate
  164. -(void) contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact{
  165. NSData *vCardData = [ContactUtil vCardDataForCnContact:contact];
  166. URLSenderItem *item = [URLSenderItem itemWithData:vCardData fileName:nil type:UTTYPE_VCARD renderType:@0 sendAsFile:true];
  167. [picker dismissViewControllerAnimated:YES completion:^{
  168. [self sendItem:item];
  169. }];
  170. }
  171. - (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperty:(CNContactProperty *)contactProperty {
  172. [picker dismissViewControllerAnimated:YES completion:nil];
  173. }
  174. -(void)contactPickerDidCancel:(CNContactPickerViewController *)picker {
  175. [picker dismissViewControllerAnimated:YES completion:nil];
  176. }
  177. @end