Add sharing of attachments.

// FREEBIE
pull/1/head
Matthew Chen 8 years ago
parent 92a56b3a85
commit 5bd44673ea

@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
340757C21E5602D6001F15DD /* AttachmentSharing.m in Sources */ = {isa = PBXBuildFile; fileRef = 340757C11E5602D6001F15DD /* AttachmentSharing.m */; };
341BB7491DB727EE001E2975 /* JSQMediaItem+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = 341BB7481DB727EE001E2975 /* JSQMediaItem+OWS.m */; };
34535D821E256BE9008A4747 /* UIView+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = 34535D811E256BE9008A4747 /* UIView+OWS.m */; };
348F3A4F1E4A533900750D44 /* CallInterstitialViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 348F3A4E1E4A533900750D44 /* CallInterstitialViewController.swift */; };
@ -599,6 +600,8 @@
/* Begin PBXFileReference section */
1B5E7D6C9007F5E5761D79DD /* libPods-SignalTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SignalTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
340757C01E5602D6001F15DD /* AttachmentSharing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AttachmentSharing.h; sourceTree = "<group>"; };
340757C11E5602D6001F15DD /* AttachmentSharing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AttachmentSharing.m; sourceTree = "<group>"; };
341BB7471DB727EE001E2975 /* JSQMediaItem+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JSQMediaItem+OWS.h"; sourceTree = "<group>"; };
341BB7481DB727EE001E2975 /* JSQMediaItem+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "JSQMediaItem+OWS.m"; sourceTree = "<group>"; };
34535D801E256BE9008A4747 /* UIView+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+OWS.h"; sourceTree = "<group>"; };
@ -2013,18 +2016,20 @@
76EB04FE18170B33006006FC /* View Controllers */ = {
isa = PBXGroup;
children = (
FC3196311A08141D0094C78E /* Settings */,
FC3196321A08142D0094C78E /* Signals */,
FCFD25791A1543D500F4C644 /* Signup */,
B6BADBE51B88D1AC0086A80D /* LockInteractionController.h */,
B6BADBE61B88D1AC0086A80D /* LockInteractionController.m */,
340757C01E5602D6001F15DD /* AttachmentSharing.h */,
340757C11E5602D6001F15DD /* AttachmentSharing.m */,
451764261DE939F300EDB8B9 /* ContactsPicker.swift */,
E94066141DFC5B7B00B15392 /* ContactsPicker.xib */,
76EB050B18170B33006006FC /* InCallViewController.h */,
76EB050C18170B33006006FC /* InCallViewController.m */,
45514DE11DDFA183003EFF90 /* InviteFlow.swift */,
B6BADBE51B88D1AC0086A80D /* LockInteractionController.h */,
B6BADBE61B88D1AC0086A80D /* LockInteractionController.m */,
458E382F1D6682450094BD24 /* OWSQRCodeScanningViewController.h */,
458E38301D6682450094BD24 /* OWSQRCodeScanningViewController.m */,
451764261DE939F300EDB8B9 /* ContactsPicker.swift */,
45514DE11DDFA183003EFF90 /* InviteFlow.swift */,
E94066141DFC5B7B00B15392 /* ContactsPicker.xib */,
FC3196311A08141D0094C78E /* Settings */,
FC3196321A08142D0094C78E /* Signals */,
FCFD25791A1543D500F4C644 /* Signup */,
);
name = "View Controllers";
path = "view controllers";
@ -3060,6 +3065,7 @@
45C9DEB81DF4E35A0065CA84 /* WebRTCCallMessageHandler.swift in Sources */,
76EB061218170B33006006FC /* LoggingUtil.m in Sources */,
76EB060E18170B33006006FC /* DecayingSampleEstimator.m in Sources */,
340757C21E5602D6001F15DD /* AttachmentSharing.m in Sources */,
76EB05BA18170B33006006FC /* CommitPacket.m in Sources */,
76EB060218170B33006006FC /* InitiatorSessionDescriptor.m in Sources */,
76EB05FC18170B33006006FC /* CallConnectUtil_Server.m in Sources */,

@ -1,11 +1,8 @@
// TSMessageAdapter.m
//
// Signal
//
// Created by Frederic Jacobs on 24/11/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "AttachmentSharing.h"
#import "OWSCall.h"
#import "TSAttachmentPointer.h"
#import "TSAttachmentStream.h"
@ -222,14 +219,15 @@
- (BOOL)canPerformEditingAction:(SEL)action
{
// Deletes are always handled by TSMessageAdapter
if (action == @selector(delete:)) {
return YES;
}
// Delegate other actions for media items
if (self.isMediaMessage) {
if ([self attachmentStream] && action == NSSelectorFromString(@"share:")) {
return YES;
} else if (self.isMediaMessage) {
return [self.mediaItem canPerformEditingAction:action];
} else if (self.messageType == TSInfoMessageAdapter || self.messageType == TSErrorMessageAdapter) {
return NO;
@ -249,8 +247,16 @@
DDLogDebug(@"Deleting interaction with uniqueId: %@", self.interaction.uniqueId);
[self.interaction remove];
return;
} else if (action == NSSelectorFromString(@"share:")) {
TSAttachmentStream *stream = [self attachmentStream];
OWSAssert(stream);
if (stream) {
[AttachmentSharing showShareUIForAttachment:stream];
}
return;
}
// Delegate other actions for media items
if (self.isMediaMessage) {
[self.mediaItem performEditingAction:action];
@ -271,6 +277,29 @@
[self.mediaItem class]);
}
- (TSAttachmentStream *)attachmentStream
{
if (![self.interaction isKindOfClass:[TSMessage class]]) {
return nil;
}
TSMessage *message = (TSMessage *)self.interaction;
if (![message hasAttachments]) {
return nil;
}
OWSAssert(message.attachmentIds.count <= 1);
NSString *attachmentID = message.attachmentIds[0];
TSAttachment *attachment = [TSAttachment fetchObjectWithUniqueID:attachmentID];
if (![attachment isKindOfClass:[TSAttachmentStream class]]) {
return nil;
}
TSAttachmentStream *stream = (TSAttachmentStream *)attachment;
return stream;
}
- (BOOL)isMediaMessage {
return _mediaItem ? YES : NO;
}

@ -1,5 +1,6 @@
// Created by Frederic Jacobs on 17/12/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "TSPhotoAdapter.h"
#import "TSAttachmentStream.h"

@ -0,0 +1,13 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import <Foundation/Foundation.h>
@class TSAttachmentStream;
@interface AttachmentSharing : NSObject
+ (void)showShareUIForAttachment:(TSAttachmentStream *)stream;
@end

@ -0,0 +1,76 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "AttachmentSharing.h"
#import "TSAttachmentStream.h"
@implementation AttachmentSharing
+ (void)showShareUIForAttachment:(TSAttachmentStream *)stream {
OWSAssert(stream);
NSString *filePath = stream.filePath;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSError *error;
NSData *data = [NSData dataWithContentsOfFile:filePath options:0 error:&error];
if (!data || error) {
DDLogError(@"%@ %s could not read data from attachment: %@.",
self.tag,
__PRETTY_FUNCTION__,
error);
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
[AttachmentSharing showShareUIForData:data];
});
});
}
+ (void)showShareUIForData:(NSData *)data {
AssertIsOnMainThread();
OWSAssert(data);
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[data, ]
applicationActivities:@[
]];
[activityViewController setCompletionWithItemsHandler:^(UIActivityType __nullable activityType,
BOOL completed,
NSArray * __nullable returnedItems,
NSError * __nullable activityError) {
if (activityError) {
DDLogInfo(@"%@ Failed to share with activityError: %@", self.tag, activityError);
} else if (completed) {
DDLogInfo(@"%@ Did share with activityType: %@", self.tag, activityType);
}
}];
// Find the frontmost presented UIViewController from which to present the
// share view.
UIWindow *window = [UIApplication sharedApplication].keyWindow;
UIViewController *fromViewController = window.rootViewController;
while (fromViewController.presentedViewController) {
fromViewController = fromViewController.presentedViewController;
}
OWSAssert(fromViewController);
[fromViewController presentViewController:activityViewController
animated:YES
completion:nil];
}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end

@ -216,9 +216,8 @@
action:@selector(copyAttachment:)],
[[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"ATTACHMENT_VIEW_SAVE_ACTION", @"Short name for edit menu item to save contents of media message.")
action:@selector(saveAttachment:)],
// TODO: We should implement sharing.
// [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"ATTACHMENT_VIEW_SHARE_ACTION", @"Short name for edit menu item to share contents of media message.")
// action:@selector(shareAttachment:)],
[[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"ATTACHMENT_VIEW_SHARE_ACTION", @"Short name for edit menu item to share contents of media message.")
action:@selector(shareAttachment:)],
];
[UIMenuController sharedMenuController].menuItems = menuItems;
CGPoint location = [sender locationInView:self.view];
@ -241,9 +240,8 @@
OWSAssert([[self.messageItem media] isKindOfClass:[TSPhotoAdapter class]] ||
[[self.messageItem media] isKindOfClass:[TSAnimatedAdapter class]]);
id<OWSMessageEditing> messageEditing = (id<OWSMessageEditing>) self.messageItem.media;
OWSAssert([messageEditing canPerformEditingAction:selector]);
[messageEditing performEditingAction:selector];
OWSAssert([self.messageItem canPerformEditingAction:selector]);
[self.messageItem performEditingAction:selector];
}
- (void)copyAttachment:(id)sender {
@ -255,10 +253,7 @@
}
- (void)shareAttachment:(id)sender {
// TODO: We should implement sharing with UIActivityViewController.
//
// It seems that loading of the contents of the attachment is done
// with TSAttachment and TSAttachmentStream.
[self performEditingActionWithSelector:NSSelectorFromString(@"share:")];
}
#pragma mark - Presentation

@ -252,6 +252,8 @@ typedef enum : NSUInteger {
[JSQMessagesCollectionViewCell registerMenuAction:@selector(delete:)];
SEL saveSelector = NSSelectorFromString(@"save:");
[JSQMessagesCollectionViewCell registerMenuAction:saveSelector];
SEL shareSelector = NSSelectorFromString(@"share:");
[JSQMessagesCollectionViewCell registerMenuAction:shareSelector];
[self initializeCollectionViewLayout];
[self registerCustomMessageNibs];
@ -393,9 +395,14 @@ typedef enum : NSUInteger {
// Other views might change these custom menu items, so we
// need to set them every time we enter this view.
SEL saveSelector = NSSelectorFromString(@"save:");
SEL shareSelector = NSSelectorFromString(@"share:");
[UIMenuController sharedMenuController].menuItems = @[ [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"EDIT_ITEM_SAVE_ACTION",
@"Short name for edit menu item to save contents of media message.")
action:saveSelector]];
action:saveSelector],
[[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"EDIT_ITEM_SHARE_ACTION",
@"Short name for edit menu item to share contents of media message.")
action:shareSelector],
];
}
- (void)startReadTimer {

@ -169,6 +169,9 @@
/* Short name for edit menu item to save contents of media message. */
"EDIT_ITEM_SAVE_ACTION" = "Save";
/* Short name for edit menu item to share contents of media message. */
"EDIT_ITEM_SHARE_ACTION" = "Share";
/* body of email sent to contacts when inviting to install Signal. Embeds {{link to install Signal}} and {{link to WhisperSystems home page}} */
"EMAIL_INVITE_BODY" = "Hey,\n\nLately I've been using Signal to keep the conversations on my iPhone private. I'd like you to install it too, so we can be confident that only you and I can read our messages or hear our calls.\n\nSignal is available for iPhones and Android. Get it here: %@\n\nSignal works like your existing messaging app. We can send pictures and video, make calls, and start group chats. The best part is, no one else can see any of it, not even the people who make Signal!\n\nYou can read more about Open Whisper Systems, the people who make Signal, here: %@";

Loading…
Cancel
Save