diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj
index ba6958e90..1b858cb17 100644
--- a/Signal.xcodeproj/project.pbxproj
+++ b/Signal.xcodeproj/project.pbxproj
@@ -1665,6 +1665,9 @@
com.apple.VPNLite = {
enabled = 0;
};
+ com.apple.iCloud = {
+ enabled = 1;
+ };
};
};
D221A0A9169C9E5F00537ABF = {
diff --git a/Signal/Signal.entitlements b/Signal/Signal.entitlements
index 903def2af..dfec421ea 100644
--- a/Signal/Signal.entitlements
+++ b/Signal/Signal.entitlements
@@ -4,5 +4,17 @@
aps-environment
development
+ com.apple.developer.icloud-container-identifiers
+
+ iCloud.$(CFBundleIdentifier)
+
+ com.apple.developer.icloud-services
+
+ CloudDocuments
+
+ com.apple.developer.ubiquity-container-identifiers
+
+ iCloud.$(CFBundleIdentifier)
+
diff --git a/Signal/src/ViewControllers/MessagesViewController.m b/Signal/src/ViewControllers/MessagesViewController.m
index dcecdee09..72d7aa9fb 100644
--- a/Signal/src/ViewControllers/MessagesViewController.m
+++ b/Signal/src/ViewControllers/MessagesViewController.m
@@ -186,7 +186,10 @@ typedef enum : NSUInteger {
#pragma mark -
-@interface MessagesViewController () {
+@interface MessagesViewController () {
UIImage *tappedImage;
BOOL isGroupConversation;
}
@@ -2182,6 +2185,87 @@ typedef enum : NSUInteger {
[self presentViewController:actionSheetController animated:YES completion:nil];
}
+
+#pragma mark - Attachment Picking: Documents
+
+- (void)showAttachmentDocumentPicker
+{
+ NSString *allItems = (__bridge NSString *)kUTTypeData;
+ NSArray *documentTypes = @[ allItems ];
+ // UIDocumentPickerModeImport copies to a temp file within our container.
+ // It uses more memory than "open" but let's us avoid working with security scoped URLs.
+ UIDocumentPickerMode pickerMode = UIDocumentPickerModeImport;
+ UIDocumentMenuViewController *menuController =
+ [[UIDocumentMenuViewController alloc] initWithDocumentTypes:documentTypes inMode:pickerMode];
+ menuController.delegate = self;
+ [self presentViewController:menuController animated:YES completion:nil];
+}
+
+#pragma mark UIDocumentMenuDelegate
+
+- (void)documentMenu:(UIDocumentMenuViewController *)documentMenu
+ didPickDocumentPicker:(UIDocumentPickerViewController *)documentPicker
+{
+ documentPicker.delegate = self;
+ [self presentViewController:documentPicker animated:YES completion:nil];
+}
+
+#pragma mark UIDocumentPickerDelegate
+- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url
+{
+ DDLogDebug(@"%@ Picked document at url: %@", self.tag, url);
+ NSData *attachmentData = [NSData dataWithContentsOfURL:url];
+
+ NSString *type;
+ NSError *error;
+ [url getResourceValue:&type forKey:NSURLTypeIdentifierKey error:&error];
+ if (error) {
+ DDLogError(@"%@ Determining type of picked document at url: %@ failed with error: %@", self.tag, url, error);
+ OWSAssert(NO);
+ }
+
+ if (!type) {
+ DDLogDebug(@"%@ falling back to default filetype for picked document at url: %@", self.tag, url);
+ OWSAssert(NO);
+ type = (__bridge NSString *)kUTTypeData;
+ }
+
+ NSString *filename = url.lastPathComponent;
+ if (!filename) {
+ DDLogDebug(@"%@ Unable to determine filename from url: %@", self.tag, url);
+ OWSAssert(NO);
+ filename = NSLocalizedString(
+ @"ATTACHMENT_DEFAULT_FILENAME", @"Generic filename for an attachment with no known name");
+ }
+
+ if (!attachmentData || attachmentData.length == 0) {
+ DDLogError(@"%@ attachment data was unexpectedly empty for picked document url: %@", self.tag, url);
+ OWSAssert(NO);
+ UIAlertController *alertController = [UIAlertController
+ alertControllerWithTitle:NSLocalizedString(@"ATTACHMENT_PICKER_DOCUMENTS_FAILED_ALERT_TITLE",
+ @"Alert title when picking a document fails for an unknown reason")
+ message:nil
+ preferredStyle:UIAlertControllerStyleAlert];
+
+ UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"DISMISS_BUTTON_TEXT", nil)
+ style:UIAlertActionStyleCancel
+ handler:nil];
+ [alertController addAction:dismissAction];
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self presentViewController:alertController animated:YES completion:nil];
+ });
+ return;
+ }
+
+ OWSAssert(attachmentData);
+ OWSAssert(type);
+ OWSAssert(filename);
+ SignalAttachment *attachment =
+ [[SignalAttachment alloc] initWithData:attachmentData dataUTI:type filename:filename];
+ [ThreadUtil sendMessageWithAttachment:attachment inThread:self.thread messageSender:self.messageSender];
+}
+
#pragma mark - UIImagePickerController
/*
@@ -2680,7 +2764,16 @@ typedef enum : NSUInteger {
[self chooseFromLibrary];
}];
[actionSheetController addAction:chooseMediaAction];
-
+
+ UIAlertAction *chooseDocumentAction =
+ [UIAlertAction actionWithTitle:NSLocalizedString(@"MEDIA_FROM_DOCUMENT_PICKER_BUTTON",
+ @"action sheet button title when choosing attachment type")
+ style:UIAlertActionStyleDefault
+ handler:^(UIAlertAction *_Nonnull action) {
+ [self showAttachmentDocumentPicker];
+ }];
+ [actionSheetController addAction:chooseDocumentAction];
+
[self presentViewController:actionSheetController animated:true completion:nil];
}
diff --git a/Signal/src/views/AttachmentPointerView.swift b/Signal/src/views/AttachmentPointerView.swift
index f5c03ed6f..931797f29 100644
--- a/Signal/src/views/AttachmentPointerView.swift
+++ b/Signal/src/views/AttachmentPointerView.swift
@@ -14,7 +14,7 @@ class AttachmentPointerView: UIView {
let isIncoming: Bool
let filename: String
let attachmentPointer: TSAttachmentPointer
- let genericFilename = NSLocalizedString("ATTACHMENT_DOWNLOADING_DEFAULT_ATTACHMENT_LABEL", comment: "Generic name label when downloading an attachment with no known name.")
+ let genericFilename = NSLocalizedString("ATTACHMENT_DEFAULT_FILENAME", comment: "Generic filename for an attachment with no known name")
var progress: CGFloat = 0 {
didSet {
diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings
index 142ef2af8..0cdff1500 100644
--- a/Signal/translations/en.lproj/Localizable.strings
+++ b/Signal/translations/en.lproj/Localizable.strings
@@ -61,8 +61,8 @@
/* Label for 'send' button in the 'attachment approval' dialog. */
"ATTACHMENT_APPROVAL_SEND_BUTTON" = "Send";
-/* Generic name label when downloading an attachment with no known name. */
-"ATTACHMENT_DOWNLOADING_DEFAULT_ATTACHMENT_LABEL" = "Attachment";
+/* Generic filename for an attachment with no known name */
+"ATTACHMENT_DEFAULT_FILENAME" = "Attachment";
/* Status label when an attachment download has failed. */
"ATTACHMENT_DOWNLOADING_STATUS_FAILED" = "Failed. Tap to retry.";
@@ -100,6 +100,9 @@
/* Accessibility label for attaching photos */
"ATTACHMENT_LABEL" = "Attachment";
+/* Alert title when picking a document fails for an unknown reason */
+"ATTACHMENT_PICKER_DOCUMENTS_FAILED_ALERT_TITLE" = "Failed to choose document.";
+
/* An explanation of the consequences of blocking another user. */
"BLOCK_BEHAVIOR_EXPLANATION" = "Blocked users will not be able to call you or send you messages.";
@@ -589,6 +592,9 @@
/* media picker option to take photo or video */
"MEDIA_FROM_CAMERA_BUTTON" = "Camera";
+/* action sheet button title when choosing attachment type */
+"MEDIA_FROM_DOCUMENT_PICKER_BUTTON" = "Document";
+
/* media picker option to choose from library */
"MEDIA_FROM_LIBRARY_BUTTON" = "Photo Library";