123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941 |
- // _____ _
- // |_ _| |_ _ _ ___ ___ _ __ __ _
- // | | | ' \| '_/ -_) -_) ' \/ _` |_
- // |_| |_||_|_| \___\___|_|_|_\__,_(_)
- //
- // Threema iOS Client
- // Copyright (c) 2012-2020 Threema GmbH
- //
- // This program is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Affero General Public License, version 3,
- // as published by the Free Software Foundation.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Affero General Public License for more details.
- //
- // You should have received a copy of the GNU Affero General Public License
- // along with this program. If not, see <https://www.gnu.org/licenses/>.
- @import SafariServices;
- #import "ChatTextMessageCell.h"
- #import "TextMessage.h"
- #import "ChatDefines.h"
- #import "UserSettings.h"
- #import "UILabel+Markup.h"
- #import "NonFirstResponderActionSheet.h"
- #import "QRCodeActivity.h"
- #import "NSString+Emoji.h"
- #import "MyIdentityStore.h"
- #import "ContactStore.h"
- #import "Contact.h"
- #import "QuoteParser.h"
- #import "BundleUtil.h"
- #import "TextStyleUtils.h"
- #import "EntityManager.h"
- #import "ChatLocationMessageCell.h"
- #import "ActivityUtil.h"
- #import "Threema-Swift.h"
- #import "ChatCallMessageCell.h"
- static NSDictionary *linkAttributes = nil;
- static NSDictionary *activeLinkAttributes = nil;
- static NSDictionary *inactiveLinkAttributes = nil;
- static CGFloat sideMargin = 2.0f;
- static CGFloat quoteBarWidth = 2.0f;
- static CGFloat quoteBarSpacing = 8.0f;
- static CGFloat quoteTextSpacing = 8.0f;
- static CGFloat quoteRightSpacing = 3.0f;
- static CGFloat ZSWTappableLabelSpace = 22.0f;
- static CGFloat quoteImageSize = 60.0;
- static CGFloat quoteImageSpacing = 8.0;
- static CGFloat quoteIconSpacing = 8.0;
- static ColorTheme currentTheme;
- @implementation ChatTextMessageCell {
- EntityManager *entityManager;
- ZSWTappableLabel *textLabel;
- ZSWTappableLabel *quoteLabel;
- UIImageView *quoteIcon;
- UIImageView *quoteImagePreview;
- UIView *quoteBar;
- UIAccessibilityElement *cellElement;
- NSURL *actionUrl;
- NSString *actionPhone;
- NSInteger openButtonIndex, copyButtonIndex, callButtonIndex;
-
- NSString *origText;
- NSString *origQuotedText;
- NSString *origQuotedIdentity;
- BaseMessage *quotedMessage;
- }
- + (CGFloat)heightForMessage:(BaseMessage*)message forTableWidth:(CGFloat)tableWidth {
- CGSize size;
- CGSize maxSize = CGSizeMake([ChatMessageCell maxContentWidthForTableWidth:tableWidth], CGFLOAT_MAX);
- TextMessage *textMessage = (TextMessage*)message;
- NSString *text = [textMessage text];
- NSString *quotedText = nil;
- NSString *quotedIdentity = nil;
- UIImage *quotedImage = nil;
- UIImage *quoteIcon = nil;
- if (textMessage.quotedMessageId != nil) {
- EntityManager *entityManager = [[EntityManager alloc] init];
- BaseMessage *quoteMessage = [entityManager.entityFetcher messageWithId:textMessage.quotedMessageId conversation:textMessage.conversation];
- if (quoteMessage != nil) {
- if (quoteMessage.isOwn.boolValue) {
- quotedIdentity = [[MyIdentityStore sharedMyIdentityStore] identity];
- } else {
- if (quoteMessage.sender) {
- quotedIdentity = quoteMessage.sender.identity;
- } else {
- quotedIdentity = quoteMessage.conversation.contact.identity;
- }
- }
-
- quotedText = quoteMessage.quotePreviewText;
- if ([quoteMessage isKindOfClass:[ImageMessage class]]) {
- if (((ImageMessage *) quoteMessage).thumbnail != nil) {
- quotedImage = ((ImageMessage *) quoteMessage).thumbnail.uiImage;
- }
- }
- else if ([quoteMessage isKindOfClass:[VideoMessage class]]) {
- if (((VideoMessage *) quoteMessage).thumbnail != nil) {
- quotedImage = ((VideoMessage *) quoteMessage).thumbnail.uiImage;
- }
- }
- else if ([quoteMessage isKindOfClass:[FileMessage class]]) {
- if (((FileMessage *) quoteMessage).thumbnail != nil) {
- quotedImage = ((FileMessage *) quoteMessage).thumbnail.uiImage;
- }
- }
- else if ([quoteMessage isKindOfClass:[AudioMessage class]]) {
- quoteIcon = [BundleUtil imageNamed:@"ActionMicrophone"];
- }
- else if ([quoteMessage isKindOfClass:[BallotMessage class]]) {
- quoteIcon = [BundleUtil imageNamed:@"ActionBallot"];
- }
- else if ([quoteMessage isKindOfClass:[LocationMessage class]]) {
- quoteIcon = [BundleUtil imageNamed:@"CurrentLocation"];
- }
- } else {
- quotedIdentity = @"";
- quotedText = [BundleUtil localizedStringForKey:@"quote_not_found"];
- }
- } else {
- NSString *remainingBody = nil;
- quotedText = [QuoteParser parseQuoteFromMessage:text quotedIdentity:"edIdentity remainingBody:&remainingBody];
- if (quotedText) {
- text = remainingBody;
- }
- }
-
- if (![UserSettings sharedUserSettings].disableBigEmojis && [text isOnlyEmojisMaxCount:3]) {
- static ZSWTappableLabel *dummyLabelEmoji = nil;
-
- if (dummyLabelEmoji == nil) {
- dummyLabelEmoji = [ChatTextMessageCell makeAttributedLabelWithFrame:CGRectMake(0.0, 0.0, maxSize.width, maxSize.height)];
- }
-
- dummyLabelEmoji.font = [ChatMessageCell emojiFont];
- dummyLabelEmoji.attributedText = [[NSAttributedString alloc] initWithString:text attributes:@{NSFontAttributeName: [ChatMessageCell emojiFont]}];
-
- size = [dummyLabelEmoji sizeThatFits:maxSize];
- } else {
- static ZSWTappableLabel *dummyLabel = nil;
-
- if (dummyLabel == nil) {
- dummyLabel = [ChatTextMessageCell makeAttributedLabelWithFrame:CGRectMake(0.0, 0.0, maxSize.width, maxSize.height)];
- }
-
- dummyLabel.font = [ChatMessageCell textFont];
- NSAttributedString *attributed = [TextStyleUtils makeAttributedStringFromString:text withFont:[ChatMessageCell textFont] textColor:[Colors fontNormal] isOwn:true application:[UIApplication sharedApplication]];
- NSMutableAttributedString *formattedAttributeString = [[NSMutableAttributedString alloc] initWithAttributedString:[dummyLabel applyMarkupFor:attributed]];
- dummyLabel.attributedText = [TextStyleUtils makeMentionsAttributedStringForAttributedString:formattedAttributeString textFont:[ChatMessageCell textFont] atColor:[dummyLabel.textColor colorWithAlphaComponent:0.4] messageInfo:message.isOwn.intValue application:[UIApplication sharedApplication]];
-
- size = [dummyLabel sizeThatFits:maxSize];
- }
-
- // Add quote?
- if (quotedText.length > 0 || textMessage.quotedMessageId != nil) {
- static ZSWTappableLabel *dummyLabelQuote = nil;
- CGSize maxSizeQuote;
- if (quotedImage != nil) {
- maxSizeQuote = CGSizeMake(maxSize.width - quoteBarWidth - quoteBarSpacing - quoteRightSpacing - quoteImageSize - quoteImageSpacing, CGFLOAT_MAX);
- } else {
- if (quoteIcon != nil) {
- maxSizeQuote = CGSizeMake(maxSize.width - quoteBarWidth - quoteBarSpacing - quoteRightSpacing - dummyLabelQuote.font.pointSize - quoteIconSpacing, CGFLOAT_MAX);
- } else {
- maxSizeQuote = CGSizeMake(maxSize.width - quoteBarWidth - quoteBarSpacing - quoteRightSpacing, CGFLOAT_MAX);
- }
- }
-
- if (dummyLabelQuote == nil) {
- dummyLabelQuote = [ChatTextMessageCell makeAttributedLabelWithFrame:CGRectMake(0.0, 0.0, maxSizeQuote.width, maxSizeQuote.height)];
- }
-
- dummyLabelQuote.font = [ChatMessageCell quoteFont];
- NSMutableAttributedString *quoteAttributed = [[NSMutableAttributedString alloc] initWithAttributedString:[ChatTextMessageCell makeQuoteAttributedStringForIdentity:quotedIdentity quotedText:quotedText inLabel:dummyLabelQuote]];
- dummyLabelQuote.attributedText = [TextStyleUtils makeMentionsAttributedStringForAttributedString:quoteAttributed textFont:[ChatMessageCell quoteFont] atColor:[[Colors fontQuoteText] colorWithAlphaComponent:0.4] messageInfo:message.isOwn.intValue application:[UIApplication sharedApplication]];
-
- if (dummyLabelQuote.attributedText.length > 200) {
- NSMutableAttributedString *trimmedString = [[NSMutableAttributedString alloc] initWithAttributedString:[dummyLabelQuote.attributedText attributedSubstringFromRange:NSMakeRange(0, 200)]];
- NSAttributedString *ellipses = [[NSAttributedString alloc] initWithString:@"..." attributes:@{ NSForegroundColorAttributeName: [Colors fontQuoteText], NSFontAttributeName: [ChatMessageCell quoteFont]}];
- [trimmedString appendAttributedString:ellipses];
- dummyLabelQuote.attributedText = trimmedString;
- }
-
- CGSize quoteSize = [dummyLabelQuote sizeThatFits:maxSizeQuote];
- if (quotedImage != nil && quoteSize.height < quoteImageSize + quoteImageSpacing) {
- quoteSize.height = quoteImageSize + quoteImageSpacing;
- }
- size.height += quoteSize.height + quoteTextSpacing;
- size.width = MAX(size.width, quoteSize.width + quoteRightSpacing);
- }
- return size.height;
- }
- - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier transparent:(BOOL)transparent
- {
- self = [super initWithStyle:style reuseIdentifier:reuseIdentifier transparent:transparent];
- if (self) {
-
- // Create message text label
- textLabel = [ChatTextMessageCell makeAttributedLabelWithFrame:self.bounds];
- textLabel.tapDelegate = self;
- textLabel.longPressDelegate = self;
-
- [self.chatVc registerForPreviewingWithDelegate:self sourceView:textLabel];
-
- [self.contentView addSubview:textLabel];
- entityManager = [[EntityManager alloc] init];
- }
- return self;
- }
- - (void)setupColors {
- [super setupColors];
-
- textLabel.textColor = [Colors fontNormal];
-
- [self updateLinkColors];
- [self updateQuoteLabel];
- }
- - (void)updateLinkColors {
- if (currentTheme != [Colors getTheme]) {
- currentTheme = [Colors getTheme];
- textLabel.attributedText = [textLabel applyMarkupFor:[TextStyleUtils makeAttributedStringFromString:origText withFont:textLabel.font textColor:nil isOwn:self.message.isOwn.boolValue application:[UIApplication sharedApplication]]];
- }
- }
- - (void)layoutSubviews {
- CGFloat messageTextWidth;
- if (@available(iOS 11.0, *)) {
- messageTextWidth = [ChatMessageCell maxContentWidthForTableWidth:self.safeAreaLayoutGuide.layoutFrame.size.width];
- } else {
- messageTextWidth = [ChatMessageCell maxContentWidthForTableWidth:self.frame.size.width];
- }
-
- CGSize textSize = [textLabel sizeThatFits:CGSizeMake(messageTextWidth, CGFLOAT_MAX)];
-
- CGSize quoteSize = CGSizeMake(0, 0);
-
- CGSize bubbleSize = textSize;
- if (quoteLabel != nil && quoteLabel.hidden == NO) {
- if (quoteImagePreview.hidden == false) {
- quoteSize = [quoteLabel sizeThatFits:CGSizeMake(messageTextWidth - quoteBarWidth - quoteBarSpacing - quoteRightSpacing - quoteImageSize - quoteImageSpacing, CGFLOAT_MAX)];
- if (quoteSize.height < quoteImageSize + quoteBarSpacing) {
- quoteSize.height = quoteImageSize + quoteBarSpacing;
- }
- bubbleSize.height += quoteSize.height + quoteTextSpacing;
- bubbleSize.width = MAX(quoteSize.width + quoteImageSize + quoteImageSpacing + quoteRightSpacing, textSize.width);
- } else {
- if (quoteIcon.hidden == false) {
- quoteSize = [quoteLabel sizeThatFits:CGSizeMake(messageTextWidth - quoteBarWidth - quoteBarSpacing - quoteRightSpacing - [quoteLabel.font pointSize] + quoteIconSpacing, CGFLOAT_MAX)];
- bubbleSize.height += quoteSize.height + quoteTextSpacing;
- bubbleSize.width = MAX(quoteSize.width + quoteRightSpacing + [quoteLabel.font pointSize] + quoteIconSpacing, textSize.width + [quoteLabel.font pointSize] + quoteIconSpacing);
- } else {
- quoteSize = [quoteLabel sizeThatFits:CGSizeMake(messageTextWidth - quoteBarWidth - quoteBarSpacing - quoteRightSpacing, CGFLOAT_MAX)];
- bubbleSize.height += quoteSize.height + quoteTextSpacing;
- bubbleSize.width = MAX(quoteSize.width + quoteRightSpacing, textSize.width);
- }
- }
- }
-
- [self setBubbleContentSize:bubbleSize];
-
- [super layoutSubviews];
-
- CGFloat x;
- if (self.message.isOwn.boolValue) {
- x = self.contentView.frame.size.width-bubbleSize.width-21.0f-sideMargin;
- } else {
- x = 20.0f + self.contentLeftOffset;
- }
-
- CGFloat y = 7.0f;
-
- if (quoteLabel != nil && quoteLabel.hidden == NO) {
- if (quoteIcon.hidden == false) {
- quoteLabel.frame = CGRectMake(x + quoteBarWidth + quoteBarSpacing + [quoteLabel.font pointSize] + quoteIconSpacing, y, quoteSize.width, quoteSize.height);
- quoteIcon.frame = CGRectMake(x + quoteBarWidth + quoteBarSpacing, y + (quoteLabel.frame.size.height / 2) - ([quoteLabel.font pointSize] / 2), [quoteLabel.font pointSize], [quoteLabel.font pointSize]);
- } else {
- quoteLabel.frame = CGRectMake(x + quoteBarWidth + quoteBarSpacing, y, quoteSize.width, quoteSize.height);
- }
-
- quoteBar.frame = CGRectMake(x, y, quoteBarWidth, quoteSize.height);
- quoteImagePreview.frame = CGRectMake(quoteLabel.frame.origin.x + quoteLabel.frame.size.width + quoteImageSpacing, quoteBarSpacing, quoteImageSize, quoteImageSize);
- y += quoteLabel.frame.size.height;
- y += quoteTextSpacing;
- }
-
- textLabel.frame = CGRectMake(ceil(x), ceil(y - (ZSWTappableLabelSpace/2)), ceil(textSize.width), ceil(textSize.height + ZSWTappableLabelSpace));
- }
- - (NSString *)accessibilityLabelForContent {
- if (((NSString *)quoteLabel.text).length > 0) {
- NSMutableString *accessibilityText = [[NSMutableString alloc] initWithString:textLabel.text];
- [accessibilityText appendString:@"\n"];
- [accessibilityText appendString:NSLocalizedString(@"in_reply_to", nil)];
- [accessibilityText appendString:@"\n"];
- [accessibilityText appendString:quoteLabel.text];
- return accessibilityText;
- } else {
- return textLabel.text;
- }
- }
- - (void)setMessage:(BaseMessage *)newMessage {
- [super setMessage:newMessage];
-
- TextMessage *textMessage = (TextMessage*)self.message;
- origText = textMessage.text;
-
- [self updateQuoteLabel];
-
- if (![UserSettings sharedUserSettings].disableBigEmojis && [origText isOnlyEmojisMaxCount:3]) {
- textLabel.font = [ChatMessageCell emojiFont];
- textLabel.attributedText = [[NSAttributedString alloc] initWithString:origText attributes:@{NSFontAttributeName: [ChatMessageCell emojiFont]}];
- } else {
-
- textLabel.font = [ChatMessageCell textFont];
-
- NSAttributedString *attributed = [TextStyleUtils makeAttributedStringFromString:origText withFont:textLabel.font textColor:[Colors fontNormal] isOwn:self.message.isOwn.boolValue application:[UIApplication sharedApplication]];
- NSMutableAttributedString *formattedAttributeString = [[NSMutableAttributedString alloc] initWithAttributedString:[textLabel applyMarkupFor:attributed]];
- textLabel.attributedText = [TextStyleUtils makeMentionsAttributedStringForAttributedString:formattedAttributeString textFont:textLabel.font atColor:[textLabel.textColor colorWithAlphaComponent:0.4] messageInfo:textMessage.isOwn.intValue application:[UIApplication sharedApplication]];
- }
-
- if (self.message.isOwn.boolValue) {
- textLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
- } else {
- textLabel.autoresizingMask = UIViewAutoresizingFlexibleRightMargin;
- }
-
- cellElement = nil;
-
- [self setNeedsLayout];
- }
- - (void)updateQuoteLabel {
- TextMessage *textMessage = (TextMessage*)self.message;
-
- if (quoteLabel == nil) {
- quoteLabel = [ChatTextMessageCell makeAttributedLabelWithFrame:self.bounds];
- quoteLabel.font = [ChatMessageCell quoteFont];
- quoteLabel.tapDelegate = self;
- quoteLabel.longPressDelegate = self;
-
- [self.contentView addSubview:quoteLabel];
-
- quoteBar = [[UIView alloc] init];
-
- [self.contentView addSubview:quoteBar];
- }
-
- if (quoteImagePreview == nil) {
- quoteImagePreview = [[UIImageView alloc] initWithFrame:self.bounds];
- quoteImagePreview.contentMode = UIViewContentModeScaleAspectFill;
- quoteImagePreview.clipsToBounds = true;
- [self.contentView addSubview:quoteImagePreview];
- }
-
- if (quoteIcon == nil) {
- quoteIcon = [[UIImageView alloc] initWithFrame:self.bounds];
- quoteIcon.contentMode = UIViewContentModeScaleAspectFill;
- quoteIcon.clipsToBounds = true;
- [self.contentView addSubview:quoteIcon];
- }
-
- if (textMessage.quotedMessageId != nil) {
- BaseMessage *quoteMessage = [entityManager.entityFetcher messageWithId:textMessage.quotedMessageId conversation:textMessage.conversation];
-
- quoteBar.backgroundColor = [Colors quoteBar];
- NSString *quotedText = nil;
- if (quoteMessage != nil) {
- quotedMessage = quoteMessage;
- if (quoteMessage.isOwn.boolValue) {
- origQuotedIdentity = [[MyIdentityStore sharedMyIdentityStore] identity];
- } else {
- if (quoteMessage.sender) {
- origQuotedIdentity = quoteMessage.sender.identity;
- } else {
- origQuotedIdentity = quoteMessage.conversation.contact.identity;
- }
- }
- quotedText = quoteMessage.quotePreviewText;
- } else {
- quotedMessage = nil;
- origQuotedIdentity = @"";
- quotedText = [BundleUtil localizedStringForKey:@"quote_not_found"];
- origQuotedText = nil;
- }
-
- NSMutableAttributedString *quoteAttributed = [[NSMutableAttributedString alloc] initWithAttributedString:[ChatTextMessageCell makeQuoteAttributedStringForIdentity:origQuotedIdentity quotedText:quotedText inLabel:quoteLabel]];
- quoteLabel.attributedText = [TextStyleUtils makeMentionsAttributedStringForAttributedString:quoteAttributed textFont:quoteLabel.font atColor:[[Colors fontNormal] colorWithAlphaComponent:0.4] messageInfo:self.message.isOwn.intValue application:[UIApplication sharedApplication]];
-
- if (quoteLabel.attributedText.length > 200) {
- NSMutableAttributedString *trimmedString = [[NSMutableAttributedString alloc] initWithAttributedString:[quoteLabel.attributedText attributedSubstringFromRange:NSMakeRange(0, 200)]];
- NSAttributedString *ellipses = [[NSAttributedString alloc] initWithString:@"..." attributes:@{ NSForegroundColorAttributeName: [Colors fontQuoteText], NSFontAttributeName: [ChatMessageCell quoteFont]}];
- [trimmedString appendAttributedString:ellipses];
- quoteLabel.attributedText = trimmedString;
- }
-
- quoteLabel.hidden = NO;
- quoteBar.hidden = NO;
- quoteImagePreview.hidden = true;
- quoteImagePreview.image = nil;
- quoteIcon.hidden = true;
- quoteIcon.image = nil;
-
- if ([quotedMessage isKindOfClass:[ImageMessage class]]) {
- if (((ImageMessage *) quoteMessage).thumbnail != nil) {
- quoteImagePreview.hidden = false;
- quoteImagePreview.image = ((ImageMessage *)quoteMessage).thumbnail.uiImage;
- }
- }
- else if ([quotedMessage isKindOfClass:[VideoMessage class]]) {
- if (((VideoMessage *) quoteMessage).thumbnail != nil) {
- quoteImagePreview.hidden = false;
- quoteImagePreview.image = ((VideoMessage *)quoteMessage).thumbnail.uiImage;
- }
- }
- else if ([quotedMessage isKindOfClass:[FileMessage class]]) {
- if (((FileMessage *) quoteMessage).thumbnail != nil) {
- quoteImagePreview.hidden = false;
- quoteImagePreview.image = ((FileMessage *)quoteMessage).thumbnail.uiImage;
- }
- }
- else if ([quotedMessage isKindOfClass:[AudioMessage class]]) {
- quoteIcon.hidden = false;
- quoteIcon.image = [[BundleUtil imageNamed:@"ActionMicrophone"] imageWithTint:[Colors fontQuoteText]];
- }
- else if ([quoteMessage isKindOfClass:[BallotMessage class]]) {
- quoteIcon.hidden = false;
- quoteIcon.image = [[BundleUtil imageNamed:@"ActionBallot"] imageWithTint:[Colors fontQuoteText]];
- }
- else if ([quoteMessage isKindOfClass:[LocationMessage class]]) {
- quoteIcon.hidden = false;
- quoteIcon.image = [[BundleUtil imageNamed:@"CurrentLocation"] imageWithTint:[Colors fontQuoteText]];
- }
-
- return;
- }
-
- quoteImagePreview.image = nil;
- quoteIcon.image = nil;
-
- if (origText == nil) {
- return;
- }
-
- NSString *quotedIdentity = nil;
- NSString *remainingBody = nil;
-
- NSString *quotedText = [QuoteParser parseQuoteFromMessage:origText quotedIdentity:"edIdentity remainingBody:&remainingBody];
- if (quotedText != nil) {
- origQuotedText = quotedText;
- origText = remainingBody;
- origQuotedIdentity = quotedIdentity;
-
- quoteBar.backgroundColor = [Colors quoteBar];
-
- NSMutableAttributedString *quoteAttributed = [[NSMutableAttributedString alloc] initWithAttributedString:[ChatTextMessageCell makeQuoteAttributedStringForIdentity:quotedIdentity quotedText:quotedText inLabel:quoteLabel]];
- quoteLabel.attributedText = [TextStyleUtils makeMentionsAttributedStringForAttributedString:quoteAttributed textFont:quoteLabel.font atColor:[[Colors fontNormal] colorWithAlphaComponent:0.4] messageInfo:self.message.isOwn.intValue application:[UIApplication sharedApplication]];
-
- if (quoteLabel.attributedText.length > 200) {
- NSMutableAttributedString *trimmedString = [[NSMutableAttributedString alloc] initWithAttributedString:[quoteLabel.attributedText attributedSubstringFromRange:NSMakeRange(0, 200)]];
- NSAttributedString *ellipses = [[NSAttributedString alloc] initWithString:@"..." attributes:@{ NSForegroundColorAttributeName: [Colors fontQuoteText], NSFontAttributeName: [ChatMessageCell quoteFont]}];
- [trimmedString appendAttributedString:ellipses];
- quoteLabel.attributedText = trimmedString;
- }
-
- quoteLabel.hidden = NO;
- quoteBar.hidden = NO;
- quoteImagePreview.hidden = YES;
- quoteIcon.hidden = YES;
- quoteImagePreview.hidden = YES;
- } else {
- origQuotedText = nil;
- origQuotedIdentity = nil;
- quoteLabel.text = nil;
- quoteLabel.hidden = YES;
- quoteBar.hidden = YES;
- quoteImagePreview.hidden = YES;
- quoteIcon.hidden = YES;
- }
- }
- - (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
- if (action == @selector(speakMessage:)) {
- return YES;
- } else {
- return [super canPerformAction:action withSender:sender];
- }
- }
- - (BOOL)highlightOccurencesOf:(NSString *)pattern {
- NSAttributedString *attributedString = [ChatMessageCell highlightedOccurencesOf:pattern inString:origText];
-
- if (![UserSettings sharedUserSettings].disableBigEmojis && [origText isOnlyEmojisMaxCount:3]) {
- textLabel.font = [ChatMessageCell emojiFont];
- NSAttributedString *emoji =[[NSAttributedString alloc] initWithString:origText attributes:@{NSFontAttributeName: [ChatMessageCell emojiFont]}];
- textLabel.attributedText = emoji;
- if (attributedString) {
- return YES;
- } else {
- return NO;
- }
- } else {
- if (attributedString) {
- NSMutableAttributedString *markupString = [[NSMutableAttributedString alloc] initWithAttributedString:[textLabel applyMarkupFor:attributedString]];
- textLabel.attributedText = [TextStyleUtils makeMentionsAttributedStringForAttributedString:markupString textFont:textLabel.font atColor:[textLabel.textColor colorWithAlphaComponent:0.4] messageInfo:self.message.isOwn.intValue application:[UIApplication sharedApplication]];
- return YES;
- } else {
- NSAttributedString *attributed = [TextStyleUtils makeAttributedStringFromString:origText withFont:textLabel.font textColor:[Colors fontNormal] isOwn:self.message.isOwn.boolValue application:[UIApplication sharedApplication]];
- NSMutableAttributedString *formattedAttributeString = [[NSMutableAttributedString alloc] initWithAttributedString:[textLabel applyMarkupFor:attributed]];
- textLabel.attributedText = [TextStyleUtils makeMentionsAttributedStringForAttributedString:formattedAttributeString textFont:textLabel.font atColor:[textLabel.textColor colorWithAlphaComponent:0.4] messageInfo:self.message.isOwn.intValue application:[UIApplication sharedApplication]];
- return NO;
- }
- }
- }
- - (void)copyMessage:(UIMenuController *)menuController {
- [[UIPasteboard generalPasteboard] setString:origText];
- }
- - (void)speakMessage:(UIMenuController *)menuController {
- AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:origText];
- AVSpeechSynthesizer *syn = [[AVSpeechSynthesizer alloc] init];
- [syn speakUtterance:utterance];
- }
- - (NSString *)textForQuote {
- return origText;
- }
- - (void)handleTapResult:(id)result {
- if ([result isKindOfClass:[Contact class]]) {
- [self.chatVc mentionTapped:(Contact *)result];
- } else {
- if ([result isKindOfClass:[NSString class]]) {
- if ([(NSString *)result isEqualToString:@"meContact"]) {
- [self.chatVc mentionTapped:(NSString *)result];
- } else {
- if ([(NSString *)result isEqualToString:@"searchQuote"]) {
- if (quotedMessage != nil) {
- [self.chatVc showQuotedMessage:quotedMessage];
- } else {
- if (origQuotedText != nil) {
- __block BaseMessage *foundMessage = nil;
- NSArray *messageHits = [entityManager.entityFetcher quoteMessagesContaining:origQuotedText message:self.message inConversation:self.message.conversation];
- [messageHits enumerateObjectsUsingBlock:^(BaseMessage *bm, NSUInteger idx, BOOL * _Nonnull stop) {
- if (( bm.conversation.isGroup && ( [bm.sender.identity isEqualToString:origQuotedIdentity] || ( bm.isOwn.boolValue && [[[MyIdentityStore sharedMyIdentityStore] identity] isEqualToString:origQuotedIdentity] ) ) ) || ( !bm.conversation.isGroup && ( [bm.conversation.contact.identity isEqualToString:origQuotedIdentity] || ( bm.isOwn.boolValue && [[[MyIdentityStore sharedMyIdentityStore] identity] isEqualToString:origQuotedIdentity] ) ) )) {
- if ([bm isKindOfClass:[TextMessage class]]) {
- NSString *quotedIdentity = nil;
- NSString *remainingBody = nil;
- NSString *quotedText = [QuoteParser parseQuoteFromMessage:((TextMessage *)bm).text quotedIdentity:"edIdentity remainingBody:&remainingBody];
- if (quotedText != nil) {
- NSString *remaining = [remainingBody stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
- NSString *originalText = [origQuotedText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
- if ([remaining isEqualToString:originalText]) {
- foundMessage = bm;
- *stop = YES;
- }
- } else {
- NSString *cellText = [((TextMessage *)bm).text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
- NSString *originalText = [origQuotedText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
- if ([cellText isEqualToString:originalText]) {
- foundMessage = bm;
- *stop = YES;
- }
- }
- }
- else if ([bm isKindOfClass:[ImageMessage class]]) {
- if ([[((ImageMessage *)bm).image getCaption] isEqualToString:origQuotedText]) {
- foundMessage = bm;
- *stop = YES;
- }
- }
- else if ([bm isKindOfClass:[FileMessage class]]) {
- if ([[((FileMessage *)bm) getCaption] isEqualToString:origQuotedText]) {
- foundMessage = bm;
- *stop = YES;
- }
- }
- else if ([bm isKindOfClass:[LocationMessage class]]) {
- NSString *locationText = [ChatLocationMessageCell displayTextForLocationMessage:(LocationMessage *)bm];
- if ([origQuotedText containsString:locationText]) {
- foundMessage = bm;
- *stop = YES;
- }
- }
- }
- }];
-
- if (foundMessage) {
- [self.chatVc showQuotedMessage:foundMessage];
- } else {
- [UIAlertTemplate showAlertWithOwner:self.chatVc title:@"" message:NSLocalizedString(@"quote_not_found", @"") actionOk:nil];
- }
- }
- }
- }
- }
- }
- else if ([result isKindOfClass:[NSTextCheckingResult class]]) {
- [self openLinkWithTextCheckingResult:(NSTextCheckingResult *)result];
- }
- }
- }
- - (void)handleLongPressResult:(NSTextCheckingResult *)result {
- if ([result isKindOfClass:[NSString class]]) {
- return;
- }
- if ([result isKindOfClass:[Contact class]]) {
- [self.chatVc mentionTapped:(Contact *)result];
- }
- else if (result.resultType == NSTextCheckingTypeLink) {
- actionUrl = result.URL;
- actionPhone = nil;
-
- UIAlertController *actionSheet = [NonFirstResponderActionSheet alertControllerWithTitle:[self displayStringForUrl:actionUrl] message:nil preferredStyle:UIAlertControllerStyleActionSheet];
- [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"open", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
- [IDNSafetyHelper safeOpenWithUrl:actionUrl viewController:self.chatVc];
- }]];
- [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"copy", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
- if (actionPhone != nil)
- [[UIPasteboard generalPasteboard] setString:actionPhone];
- else
- [[UIPasteboard generalPasteboard] setString:[self displayStringForUrl:actionUrl]];
- }]];
- [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"cancel", nil) style:UIAlertActionStyleDefault handler:nil]];
-
- if (SYSTEM_IS_IPAD) {
- actionSheet.popoverPresentationController.sourceView = self;
- actionSheet.popoverPresentationController.sourceRect = self.bounds;
- }
-
- [self.chatVc.chatBar resignFirstResponder];
- [self.chatVc presentViewController:actionSheet animated:YES completion:nil];
-
- } else if (result.resultType == NSTextCheckingTypePhoneNumber) {
- actionPhone = result.phoneNumber;
- actionUrl = nil;
-
- UIAlertController *actionSheet = [NonFirstResponderActionSheet alertControllerWithTitle:actionPhone message:nil preferredStyle:UIAlertControllerStyleActionSheet];
- [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"call", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
- [self callPhoneNumber:actionPhone];
- }]];
- [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"copy", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
- if (actionPhone != nil)
- [[UIPasteboard generalPasteboard] setString:actionPhone];
- else
- [[UIPasteboard generalPasteboard] setString:[self displayStringForUrl:actionUrl]];
- }]];
- [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"cancel", nil) style:UIAlertActionStyleDefault handler:nil]];
-
- if (SYSTEM_IS_IPAD) {
- actionSheet.popoverPresentationController.sourceView = self;
- actionSheet.popoverPresentationController.sourceRect = self.bounds;
- }
-
- [self.chatVc.chatBar resignFirstResponder];
- [self.chatVc presentViewController:actionSheet animated:YES completion:nil];
- }
- }
- - (NSString*)displayStringForUrl:(NSURL*)url {
- NSString *urlString = [url.absoluteString stringByReplacingOccurrencesOfString:@"mailto:" withString:@""];
- return urlString;
- }
- + (ZSWTappableLabel*)makeAttributedLabelWithFrame:(CGRect)rect {
- ZSWTappableLabel *label = [[ZSWTappableLabel alloc] initWithFrame:rect];
- label.clearsContextBeforeDrawing = NO;
- label.backgroundColor = [UIColor clearColor];
- label.numberOfLines = 0;
- label.lineBreakMode = NSLineBreakByWordWrapping;
- label.font = [ChatMessageCell textFont];
- label.contentMode = UIViewContentModeScaleToFill;
-
- return label;
- }
- + (NSAttributedString*)makeQuoteAttributedStringForIdentity:(NSString*)identity quotedText:(NSString*)quotedText inLabel:(UILabel*)label {
- NSMutableAttributedString *quoteString = [[NSMutableAttributedString alloc] init];
-
- // Resolve identity to name
- Contact *contact = [[ContactStore sharedContactStore] contactForIdentity:identity];
- NSString *identityNewline;
-
- if ([identity isEqualToString:[MyIdentityStore sharedMyIdentityStore].identity]) {
- identityNewline = [[BundleUtil localizedStringForKey:@"me"] stringByAppendingString:@"\n"];
- } else if (contact != nil) {
- identityNewline = [contact.displayName stringByAppendingString:@"\n"];
- } else {
- if ([identity length] > 0) {
- identityNewline = [identity stringByAppendingString:@"\n"];
- } else {
- identityNewline = @"";
- }
- }
-
- [quoteString appendAttributedString:[[NSAttributedString alloc] initWithString:identityNewline attributes:@{
- NSForegroundColorAttributeName: [Colors fontQuoteId],
- NSFontAttributeName: [ChatMessageCell quoteIdentityFont],
- @"ZSWTappableLabelTappableRegionAttributeName": @YES,
- @"NSTextCheckingResult": @"searchQuote"
- }]];
-
- NSMutableAttributedString *quotedTextAttr = [[NSMutableAttributedString alloc] initWithString:quotedText attributes:@{
- NSForegroundColorAttributeName: [Colors fontQuoteText],
- NSFontAttributeName: [ChatMessageCell quoteFont],
- @"ZSWTappableLabelTappableRegionAttributeName": @YES,
- @"NSTextCheckingResult": @"searchQuote"
- }];
-
- NSAttributedString *quotedTextAttrMarkup = [label applyMarkupFor:quotedTextAttr];
- [quoteString appendAttributedString:quotedTextAttrMarkup];
-
- return quoteString;
- }
- - (UIViewController *)previewViewControllerFor:(id<UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location {
- id<ZSWTappableLabelTappableRegionInfo> regionInfo = [textLabel tappableRegionInfoForPreviewingContext:previewingContext location:location];
- if (!regionInfo) {
- return nil;
- }
-
- NSTextCheckingResult *result = regionInfo.attributes[@"NSTextCheckingResult"];
- if ([result isKindOfClass:[NSTextCheckingResult class]]) {
- if (result.resultType == NSTextCheckingTypeLink && ![[result.URL absoluteString] hasPrefix:@"mailto:"]) {
- NSURL *url = result.URL;
- if ([url.scheme isEqualToString:@"http"] || [url.scheme isEqualToString:@"https"]) {
- [regionInfo configurePreviewingContext:previewingContext];
- ThreemaSafariViewController *webController = [[ThreemaSafariViewController alloc] initWithURL:result.URL];
- webController.url = result.URL;
-
- return webController;
- }
- }
- }
- return nil;
- }
- - (UIContextMenuConfiguration *)getContextMenu:(NSIndexPath *)indexPath point:(CGPoint)point API_AVAILABLE(ios(13.0)) {
- if (!self.editing) {
- CGPoint convertedPoint = [textLabel convertPoint:point fromView:self.chatVc.chatContent];
- NSDictionary *regionInfo = [textLabel checkIsPointAction:convertedPoint];
- if (regionInfo != nil) {
- NSTextCheckingResult *result = regionInfo[@"NSTextCheckingResult"];
- if ([result isKindOfClass:[NSTextCheckingResult class]]) {
- if (result.resultType == NSTextCheckingTypeLink && ![[result.URL absoluteString] hasPrefix:@"mailto:"]) {
- NSURL *url = result.URL;
- if ([url.scheme isEqualToString:@"http"] || [url.scheme isEqualToString:@"https"]) {
- ThreemaSafariViewController *webController = [[ThreemaSafariViewController alloc] initWithURL:result.URL];
- webController.url = result.URL;
- UIContextMenuConfiguration *conf = [UIContextMenuConfiguration configurationWithIdentifier:indexPath previewProvider:^UIViewController * _Nullable{
- return webController;
- } actionProvider:^UIMenu * _Nullable(NSArray<UIMenuElement *> * _Nonnull suggestedActions) {
- NSMutableArray *menuItems = [NSMutableArray array];
- UIImage *copyImage = [UIImage systemImageNamed:@"doc.on.doc.fill" compatibleWithTraitCollection:self.traitCollection];
- UIAction *action = [UIAction actionWithTitle:[BundleUtil localizedStringForKey:@"copy"] image:copyImage identifier:nil handler:^(__kindof UIAction * _Nonnull action) {
- [[UIPasteboard generalPasteboard] setString:[self displayStringForUrl:result.URL]];
- }];
- [menuItems addObject:action];
- return [UIMenu menuWithTitle:@"" image:nil identifier:UIMenuApplication options:UIMenuOptionsDisplayInline children:menuItems];
- }];
- return conf;
- }
- }
- }
- return nil;
- }
- }
-
- return [super getContextMenu:indexPath point:point];
- }
- - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
- if (touches.count == 1) {
- if (UIAccessibilityIsVoiceOverRunning()) {
- /* when VoiceOver is on, double-taps on message cells will result in a touch located
- in the center of the cell. Since this may not be within the bubble, it will not
- trigger playing/showing media. Therefore, we hand this event to the cell */
- if ([self performPlayActionForAccessibility])
- return;
- }
-
- UITouch *touch = [touches anyObject];
- CGPoint point = [touch locationInView:self.contentView];
- if (CGRectContainsPoint(quoteImagePreview.frame, point)) {
- [self handleTapResult:@"searchQuote"];
- return;
- }
- }
-
- [super touchesEnded:touches withEvent:event];
- }
- #pragma mark - ZSWTappableLabel delegate
- - (void)tappableLabel:(ZSWTappableLabel *)tappableLabel tappedAtIndex:(NSInteger)idx withAttributes:(NSDictionary *)attributes {
- [self handleTapResult:attributes[@"NSTextCheckingResult"]];
- }
- - (void)tappableLabel:(ZSWTappableLabel *)tappableLabel longPressedAtIndex:(NSInteger)idx withAttributes:(NSDictionary<NSString *,id> *)attributes {
- [self handleLongPressResult:attributes[@"NSTextCheckingResult"]];
- }
- - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
- if (self.editing) {
- // don't event forward to label
- return self;
- }
- return [super hitTest:point withEvent:event];
- }
-
- - (void)callPhoneNumber:(NSString*)phoneNumber {
- NSString *cleanString = [phoneNumber stringByReplacingOccurrencesOfString:@"\u00a0" withString:@""];
- NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"tel:%@", [cleanString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]]]];
- [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
- }
- #pragma mark - UIAccessibilityContainer
- - (BOOL)isAccessibilityElement {
- return YES;
- }
- - (NSArray *)accessibilityCustomActions {
- NSMutableArray *actions = [[NSMutableArray alloc] initWithArray:[super accessibilityCustomActions]];
- int indexCounter = 0;
- NSMutableArray *tmpArray = [NSMutableArray new];
- NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
-
- for (int i = 0; i < textLabel.accessibilityElementCount; i++) {
- UIAccessibilityElement *element = [textLabel accessibilityElementAtIndex:i];
- if (![element.accessibilityLabel isEqualToString:@"."] && ![element.accessibilityLabel isEqualToString:@"@"]) {
-
- NSTextCheckingResult *urlResult = [self checkTextResult:element.accessibilityLabel];
-
- if (urlResult) {
- UIAccessibilityCustomAction *linkAction = [[UIAccessibilityCustomAction alloc] initWithName:[NSString stringWithFormat:@"%@: %@", NSLocalizedString(@"open", @""), element.accessibilityLabel] target:self selector:@selector(openLink:)];
- [tmpArray addObject:linkAction];
- [indexSet addIndex:indexCounter];
- indexCounter ++;
- UIAccessibilityCustomAction *shareAction = [[UIAccessibilityCustomAction alloc] initWithName:[NSString stringWithFormat:@"%@: %@", NSLocalizedString(@"share", @""), element.accessibilityLabel] target:self selector:@selector(shareLink:)];
- [tmpArray addObject:shareAction];
- [indexSet addIndex:indexCounter];
- indexCounter ++;
-
- } else {
- UIAccessibilityCustomAction *mentionAction = [[UIAccessibilityCustomAction alloc] initWithName:[NSString stringWithFormat:@"%@ @%@", NSLocalizedString(@"details", @""), element.accessibilityLabel] target:self selector:@selector(openMention:)];
- [tmpArray addObject:mentionAction];
- [indexSet addIndex:indexCounter];
- indexCounter ++;
- }
- }
- }
-
- if (tmpArray.count > 0) {
- [actions insertObjects:tmpArray atIndexes:indexSet];
- }
-
- return actions;
- }
- - (BOOL)openLink:(UIAccessibilityCustomAction *)action {
- [self openLinkWithTextCheckingResult:[self checkTextResult:action.name]];
- return YES;
- }
- - (void)openLinkWithTextCheckingResult:(NSTextCheckingResult*)urlResult {
- if (urlResult.resultType == NSTextCheckingTypeLink) {
- [IDNSafetyHelper safeOpenWithUrl:urlResult.URL viewController:self.chatVc];
- } else if (urlResult.resultType == NSTextCheckingTypePhoneNumber) {
- [self callPhoneNumber:urlResult.phoneNumber];
- }
- }
- - (BOOL)shareLink:(UIAccessibilityCustomAction *)action {
- NSTextCheckingResult *urlResult = [self checkTextResult:action.name];
-
- if (urlResult.resultType == NSTextCheckingTypeLink) {
- UIActivityViewController *activityViewController = [ActivityUtil activityViewControllerWithActivityItems:@[urlResult.URL] applicationActivities:@[]];
- [self.chatVc presentActivityViewController:activityViewController animated:YES fromView:self];
- }
- else if (urlResult.resultType == NSTextCheckingTypePhoneNumber) {
- UIActivityViewController *activityViewController = [ActivityUtil activityViewControllerWithActivityItems:@[urlResult.phoneNumber] applicationActivities:@[]];
- [self.chatVc presentActivityViewController:activityViewController animated:YES fromView:self];
- }
-
- return YES;
- }
- - (BOOL)openMention:(UIAccessibilityCustomAction *)action {
- NSString *identity = [action.name stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"%@ @", NSLocalizedString(@"details", @"")] withString:@""];
- if ([identity isEqualToString:[BundleUtil localizedStringForKey:@"me"]]) {
- [self handleTapResult:@"meContact"];
- } else {
- Contact *contact = [[ContactStore sharedContactStore] contactForIdentity:identity];
- [self handleTapResult:contact];
- }
- return YES;
- }
- - (NSTextCheckingResult *)checkTextResult:(NSString *)text {
- NSTextCheckingTypes textCheckingTypes = NSTextCheckingTypeLink;
-
- static dispatch_once_t onceToken;
- static BOOL canOpenPhoneLinks;
- dispatch_once(&onceToken, ^{
- canOpenPhoneLinks = [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel:0"]];
- });
- if (canOpenPhoneLinks)
- textCheckingTypes |= NSTextCheckingTypePhoneNumber;
-
- __block NSTextCheckingResult *urlResult = nil;
-
- NSDataDetector *detector = [NSDataDetector dataDetectorWithTypes:textCheckingTypes error:NULL];
- [detector enumerateMatchesInString:text options:0 range:NSMakeRange(0, text.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
- urlResult = result;
- }];
- return urlResult;
- }
- @end
|