diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m index b94760363..387631db5 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSMessageCell.m @@ -132,8 +132,6 @@ NS_ASSUME_NONNULL_BEGIN [self.bubbleImageView autoPinToSuperviewEdges]; self.textView = [UITextView new]; - // Honor dynamic type in the message bodies. - self.textView.font = [self textMessageFont]; self.textView.backgroundColor = [UIColor clearColor]; self.textView.opaque = NO; self.textView.editable = NO; @@ -182,6 +180,23 @@ NS_ASSUME_NONNULL_BEGIN - (UIFont *)textMessageFont { + OWSAssert(DisplayableText.kMaxJumbomojiCount == 5); + + if (self.displayableText.jumbomojiCount) { + switch (self.displayableText.jumbomojiCount.integerValue) { + case 1: + return [UIFont ows_regularFontWithSize:35.f]; + case 2: + return [UIFont ows_regularFontWithSize:30.f]; + case 3: + case 4: + case 5: + return [UIFont ows_regularFontWithSize:25.f]; + default: + break; + } + } + return [UIFont ows_dynamicTypeBodyFont]; } @@ -627,6 +642,7 @@ NS_ASSUME_NONNULL_BEGIN self.textView.text = self.displayableText.displayText; UIColor *textColor = [self textColor]; self.textView.textColor = textColor; + // Honor dynamic type in the message bodies. self.textView.font = [self textMessageFont]; // Don't link outgoing messages that haven't been sent yet, as @@ -848,6 +864,7 @@ NS_ASSUME_NONNULL_BEGIN const int maxTextWidth = (int)floor(maxMessageWidth - (leftMargin + rightMargin)); self.textView.text = self.displayableText.displayText; + // Honor dynamic type in the message bodies. self.textView.font = [self textMessageFont]; CGSize textSize = [self.textView sizeThatFits:CGSizeMake(maxTextWidth, CGFLOAT_MAX)]; CGFloat tapForMoreHeight = (self.displayableText.isTextTruncated ? [self tapForMoreHeight] : 0.f); diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index a3f54ef18..afc20b617 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -323,8 +323,12 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) displayText = @""; } - displayableText = - [[DisplayableText alloc] initWithFullText:fullText displayText:displayText isTextTruncated:isTextTruncated]; + NSNumber *_Nullable jumbomojiCount = [DisplayableText jumbomojiCountTo:fullText]; + + displayableText = [[DisplayableText alloc] initWithFullText:fullText + displayText:displayText + isTextTruncated:isTextTruncated + jumbomojiCount:jumbomojiCount]; [[self displayableTextCache] setObject:displayableText forKey:interactionId]; } diff --git a/Signal/src/util/DisplayableText.swift b/Signal/src/util/DisplayableText.swift index 87a558b6b..bbc37ce83 100644 --- a/Signal/src/util/DisplayableText.swift +++ b/Signal/src/util/DisplayableText.swift @@ -11,13 +11,83 @@ import Foundation let fullText: String let displayText: String let isTextTruncated: Bool + let jumbomojiCount: NSNumber? + + static let kMaxJumbomojiCount: UInt = 5 // MARK: Initializers - init(fullText: String, displayText: String, isTextTruncated: Bool) { + init(fullText: String, displayText: String, isTextTruncated: Bool, jumbomojiCount: NSNumber?) { self.fullText = fullText self.displayText = displayText self.isTextTruncated = isTextTruncated + self.jumbomojiCount = jumbomojiCount + } + + // MARK: Emoji + + private class func canDetectEmoji() -> Bool { + if #available(iOS 10.0, *) { + return true + } else { + return false + } + } + + // If the string is... + // + // * Non-empty + // * Only contains emoji + // * Contains <= maxEmojiCount emoji + // + // ...return the number of emoji in the string. Otherwise return nil. + // + // On iOS 9 and earler, always returns nil. + @objc public class func jumbomojiCount(to string: String) -> NSNumber? { + if string == "" { + return nil + } + if string.characters.count > Int(kMaxJumbomojiCount) { + return nil + } + if !canDetectEmoji() { + return nil + } + var didFail = false + var emojiCount: UInt = 0 + let attributes = [NSFontAttributeName: UIFont.systemFont(ofSize:12)] + let attributedString = NSMutableAttributedString(string: string, attributes: attributes) + let range = NSRange(location: 0, length: string.characters.count) + attributedString.fixAttributes(in: range) + attributedString.enumerateAttribute(NSFontAttributeName, + in: range, + options: [], + using: {(_ value: Any?, range: NSRange, stop: UnsafeMutablePointer) -> Void in + guard emojiCount < kMaxJumbomojiCount else { + didFail = true + stop.pointee = true + return + } + guard let rangeFont = value as? UIFont else { + didFail = true + stop.pointee = true + return + } + guard rangeFont.fontName == ".AppleColorEmojiUI" else { + didFail = true + stop.pointee = true + return + } + if rangeFont.fontName == ".AppleColorEmojiUI" { + Logger.verbose("Detected Emoji at location: \(range.location), for length: \(range.length)") + emojiCount += UInt(range.length) + } + }) + + guard !didFail else { + return nil + } + return NSNumber(value: emojiCount) } // MARK: Filter Methods