parent
cd928cd9be
commit
27b515ea45
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "file-icon-large@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
|
@ -1545,11 +1545,11 @@
|
|||
<viewControllerLayoutGuide type="bottom" id="kH6-9L-pzh"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="P0X-AM-Yjw">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<rect key="frame" x="0.0" y="64" width="375" height="603"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Ukg-om-VX3" userLabel="Group Details">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="100"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="100"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ul8-NY-i4c">
|
||||
<rect key="frame" x="8" y="20" width="60" height="60"/>
|
||||
|
@ -1584,7 +1584,7 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" allowsMultipleSelection="YES" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" translatesAutoresizingMaskIntoConstraints="NO" id="cFo-AT-Srf">
|
||||
<rect key="frame" x="0.0" y="128" width="375" height="539"/>
|
||||
<rect key="frame" x="0.0" y="108" width="375" height="495"/>
|
||||
<color key="backgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.94901960780000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<view key="tableHeaderView" contentMode="scaleToFill" id="ekO-kw-iHV" userLabel="Header View">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="40"/>
|
||||
|
@ -1721,8 +1721,8 @@
|
|||
<simulatedScreenMetrics key="destination" type="retina4_7.fullscreen"/>
|
||||
</simulatedMetricsContainer>
|
||||
<inferredMetricsTieBreakers>
|
||||
<segue reference="E8S-Yc-X7E"/>
|
||||
<segue reference="I6y-pT-nEd"/>
|
||||
<segue reference="wgA-Oo-kKq"/>
|
||||
<segue reference="D0d-4f-lcI"/>
|
||||
<segue reference="G2B-Fr-Ezs"/>
|
||||
</inferredMetricsTieBreakers>
|
||||
</document>
|
||||
|
|
|
@ -18,5 +18,7 @@
|
|||
+ (UIColor *)ows_errorMessageBorderColor;
|
||||
+ (UIColor *)ows_infoMessageBorderColor;
|
||||
+ (UIColor *)backgroundColorForContact:(NSString *)contactIdentifier;
|
||||
+ (UIColor *)colorWithRGBHex:(unsigned long)value;
|
||||
+ (UIColor *)colorWithARGBHex:(unsigned long)value;
|
||||
|
||||
@end
|
||||
|
|
|
@ -107,4 +107,21 @@
|
|||
return [colors objectAtIndex:(choose % [colors count])];
|
||||
}
|
||||
|
||||
+ (UIColor *)colorWithRGBHex:(unsigned long)value
|
||||
{
|
||||
CGFloat red = ((value >> 16) & 0xff) / 255.f;
|
||||
CGFloat green = ((value >> 8) & 0xff) / 255.f;
|
||||
CGFloat blue = ((value >> 0) & 0xff) / 255.f;
|
||||
return [UIColor colorWithRed:red green:green blue:blue alpha:1.f];
|
||||
}
|
||||
|
||||
+ (UIColor *)colorWithARGBHex:(unsigned long)value
|
||||
{
|
||||
CGFloat alpha = ((value >> 24) & 0xff) / 255.f;
|
||||
CGFloat red = ((value >> 16) & 0xff) / 255.f;
|
||||
CGFloat green = ((value >> 8) & 0xff) / 255.f;
|
||||
CGFloat blue = ((value >> 0) & 0xff) / 255.f;
|
||||
return [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2496,11 +2496,15 @@ typedef enum : NSUInteger {
|
|||
[attachment hasError]) {
|
||||
// TODO: Add UI.
|
||||
} else {
|
||||
|
||||
// TODO: Add UI.
|
||||
UIViewController *viewController = [[AttachmentApprovalViewController alloc] initWithAttachment:attachment];
|
||||
// [self tryToSendMessageAttachmentWithData:imageData
|
||||
// dataUTI:dataUTI];
|
||||
__weak MessagesViewController *weakSelf = self;
|
||||
UIViewController *viewController = [[AttachmentApprovalViewController alloc] initWithAttachment:attachment
|
||||
successCompletion:^{
|
||||
[weakSelf sendMessageAttachment:attachment];
|
||||
}];
|
||||
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
|
||||
[self.navigationController presentViewController:navigationController
|
||||
animated:YES
|
||||
completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,50 +30,55 @@ enum SignalAttachmentError: String {
|
|||
// TODO: Show error on error.
|
||||
// TODO: Show progress on upload.
|
||||
class SignalAttachment: NSObject {
|
||||
|
||||
|
||||
static let TAG = "[SignalAttachment]"
|
||||
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
let data : Data!
|
||||
|
||||
|
||||
let data: Data!
|
||||
|
||||
// Attachment types are identified using UTIs.
|
||||
//
|
||||
// See: https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html
|
||||
let dataUTI : String!
|
||||
|
||||
var error : SignalAttachmentError? {
|
||||
let dataUTI: String!
|
||||
|
||||
var error: SignalAttachmentError? {
|
||||
didSet {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
|
||||
assert(oldValue == nil)
|
||||
Logger.verbose("\(SignalAttachment.TAG) Attachment has error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// To avoid redundant work of repeatedly compressing/uncompressing
|
||||
// images, we cache the UIImage associated with this attachment if
|
||||
// possible.
|
||||
public var image: UIImage?
|
||||
|
||||
// MARK: Constants
|
||||
|
||||
|
||||
/**
|
||||
* Media Size constraints from Signal-Android
|
||||
* (org/thoughtcrime/securesms/mms/PushMediaConstraints.java)
|
||||
*/
|
||||
static let kMaxFileSize_Gif = 5 * 1024 * 1024
|
||||
static let kMaxFileSize_Image = 420 * 1024
|
||||
static let kMaxFileSize_Video = 100 * 1024 * 1024
|
||||
static let kMaxFileSize_Audio = 100 * 1024 * 1024
|
||||
static let kMaxFileSizeGif = 5 * 1024 * 1024
|
||||
static let kMaxFileSizeImage = 420 * 1024
|
||||
static let kMaxFileSizeVideo = 100 * 1024 * 1024
|
||||
static let kMaxFileSizeAudio = 100 * 1024 * 1024
|
||||
// TODO: What should the max file size on "other" attachments be?
|
||||
static let kMaxFileSize_Generic = 25 * 1024 * 1024
|
||||
|
||||
static let kMaxFileSizeGeneric = 100 * 1024 * 1024
|
||||
|
||||
// MARK: Constructor
|
||||
|
||||
|
||||
// This method should not be called directly; use the factory
|
||||
// methods instead.
|
||||
internal required init(data : Data!, dataUTI : String!) {
|
||||
internal required init(data: Data!, dataUTI: String!) {
|
||||
self.data = data
|
||||
self.dataUTI = dataUTI
|
||||
super.init()
|
||||
}
|
||||
|
||||
|
||||
public func hasError() -> Bool {
|
||||
return error != nil
|
||||
}
|
||||
|
@ -87,7 +92,17 @@ class SignalAttachment: NSObject {
|
|||
}
|
||||
return mimeType?.takeRetainedValue() as? String
|
||||
}
|
||||
|
||||
|
||||
// Returns the file extension for this attachment or nil if no file extension
|
||||
// can be identified.
|
||||
public func fileExtension() -> String? {
|
||||
let fileExtension = UTTypeCopyPreferredTagWithClass(dataUTI as CFString, kUTTagClassFilenameExtension)
|
||||
guard fileExtension != nil else {
|
||||
return nil
|
||||
}
|
||||
return fileExtension?.takeRetainedValue() as? String
|
||||
}
|
||||
|
||||
// Returns the set of UTIs that correspond to valid _input_ image formats
|
||||
// for Signal attachments.
|
||||
//
|
||||
|
@ -99,10 +114,10 @@ class SignalAttachment: NSObject {
|
|||
return [
|
||||
kUTTypeJPEG as String,
|
||||
kUTTypeGIF as String,
|
||||
kUTTypePNG as String,
|
||||
kUTTypePNG as String
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
// Returns the set of UTIs that correspond to valid _output_ image formats
|
||||
// for Signal attachments.
|
||||
//
|
||||
|
@ -111,20 +126,20 @@ class SignalAttachment: NSObject {
|
|||
return [
|
||||
kUTTypeJPEG as String,
|
||||
kUTTypeGIF as String,
|
||||
kUTTypePNG as String,
|
||||
kUTTypePNG as String
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
// Returns the set of UTIs that correspond to valid video formats
|
||||
// for Signal attachments.
|
||||
//
|
||||
// TODO: We need to finalize which formats we support.
|
||||
private class func videoUTISet() -> Set<String>! {
|
||||
return [
|
||||
kUTTypeMPEG4 as String,
|
||||
kUTTypeMPEG4 as String
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
// Returns the set of UTIs that correspond to valid audio formats
|
||||
// for Signal attachments.
|
||||
//
|
||||
|
@ -132,16 +147,28 @@ class SignalAttachment: NSObject {
|
|||
private class func audioUTISet() -> Set<String>! {
|
||||
return [
|
||||
kUTTypeMP3 as String,
|
||||
kUTTypeMPEG4Audio as String,
|
||||
kUTTypeMPEG4Audio as String
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
// Returns the set of UTIs that correspond to valid input formats
|
||||
// for Signal attachments.
|
||||
public class func validInputUTISet() -> Set<String>! {
|
||||
return inputImageUTISet().union(videoUTISet().union(audioUTISet()))
|
||||
}
|
||||
|
||||
|
||||
public func isImage() -> Bool {
|
||||
return SignalAttachment.outputImageUTISet().contains(dataUTI)
|
||||
}
|
||||
|
||||
public func isVideo() -> Bool {
|
||||
return SignalAttachment.videoUTISet().contains(dataUTI)
|
||||
}
|
||||
|
||||
public func isAudio() -> Bool {
|
||||
return SignalAttachment.audioUTISet().contains(dataUTI)
|
||||
}
|
||||
|
||||
// Returns an attachment from the pasteboard, or nil if no attachment
|
||||
// can be found.
|
||||
//
|
||||
|
@ -174,39 +201,39 @@ class SignalAttachment: NSObject {
|
|||
}
|
||||
}
|
||||
// TODO: We could handle generic attachments at this point.
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
// MARK: Image Attachments
|
||||
|
||||
// Factory method for an image attachment.
|
||||
//
|
||||
// NOTE: The attachment returned by this method may not be valid.
|
||||
// Check the attachment's error property.
|
||||
public class func imageAttachment(withData imageData : Data?, dataUTI : String!) -> SignalAttachment! {
|
||||
public class func imageAttachment(withData imageData: Data?, dataUTI: String!) -> SignalAttachment! {
|
||||
assert(dataUTI.characters.count > 0)
|
||||
|
||||
assert(imageData != nil)
|
||||
guard let imageData = imageData else {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
let attachment = SignalAttachment(data : imageData, dataUTI: dataUTI)
|
||||
|
||||
|
||||
guard inputImageUTISet().contains(dataUTI) else {
|
||||
attachment.error = .invalidFileFormat
|
||||
return attachment
|
||||
}
|
||||
|
||||
|
||||
guard imageData.count > 0 else {
|
||||
assert(imageData.count > 0)
|
||||
attachment.error = .invalidData
|
||||
return attachment
|
||||
}
|
||||
|
||||
|
||||
if dataUTI == kUTTypeGIF as String {
|
||||
guard imageData.count <= kMaxFileSize_Gif else {
|
||||
guard imageData.count <= kMaxFileSizeGif else {
|
||||
attachment.error = .fileSizeTooLarge
|
||||
return attachment
|
||||
}
|
||||
|
@ -214,71 +241,91 @@ class SignalAttachment: NSObject {
|
|||
// animated.
|
||||
//
|
||||
// TODO: Consider re-encoding non-animated GIFs as JPEG?
|
||||
Logger.verbose("\(TAG) Sending raw image/gif to retain any animation")
|
||||
Logger.verbose("\(TAG) Sending raw \(attachment.mimeType()) to retain any animation")
|
||||
return attachment
|
||||
} else {
|
||||
guard let image = UIImage(data:imageData) else {
|
||||
attachment.error = .couldNotParseImage
|
||||
return attachment
|
||||
}
|
||||
|
||||
// If the proposed attachment already is a JPEG,
|
||||
// and already conforms to the file size and
|
||||
// content size limits, don't recompress it.
|
||||
//
|
||||
// TODO: Should non-JPEGs always be converted to JPEG?
|
||||
if dataUTI == kUTTypeJPEG as String {
|
||||
let imageUploadQuality = Environment.preferences().imageUploadQuality()
|
||||
let maxSize = maxSizeForImage(image: image, imageUploadQuality:imageUploadQuality)
|
||||
if (image.size.width <= maxSize &&
|
||||
image.size.height <= maxSize &&
|
||||
imageData.count <= kMaxFileSize_Image) {
|
||||
Logger.verbose("\(TAG) Sending raw image/jpeg")
|
||||
return attachment
|
||||
}
|
||||
attachment.image = image
|
||||
|
||||
if isInputImageValidOutputImage(image: image, imageData: imageData, dataUTI: dataUTI) {
|
||||
Logger.verbose("\(TAG) Sending raw \(attachment.mimeType())")
|
||||
return attachment
|
||||
}
|
||||
|
||||
Logger.verbose("\(TAG) Converting attachment to image/jpeg")
|
||||
return recompressImageAsJPEG(image : image, attachment : attachment)
|
||||
|
||||
Logger.verbose("\(TAG) Compressing attachment as image/jpeg")
|
||||
return compressImageAsJPEG(image : image, attachment : attachment)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If the proposed attachment already is a JPEG, and already conforms to the
|
||||
// file size and content size limits, don't recompress it.
|
||||
//
|
||||
// TODO: Should non-JPEGs always be converted to JPEG?
|
||||
private class func isInputImageValidOutputImage(image: UIImage?, imageData: Data?, dataUTI: String!) -> Bool {
|
||||
guard let image = image else {
|
||||
return false
|
||||
}
|
||||
guard let imageData = imageData else {
|
||||
return false
|
||||
}
|
||||
if dataUTI == kUTTypeJPEG as String {
|
||||
let imageUploadQuality = Environment.preferences().imageUploadQuality()
|
||||
let maxSize = maxSizeForImage(image: image, imageUploadQuality:imageUploadQuality)
|
||||
if image.size.width <= maxSize &&
|
||||
image.size.height <= maxSize &&
|
||||
imageData.count <= kMaxFileSizeImage {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Factory method for an image attachment.
|
||||
//
|
||||
// NOTE: The attachment returned by this method may nil or not be valid.
|
||||
// Check the attachment's error property.
|
||||
public class func imageAttachment(withImage image : UIImage?, dataUTI : String!) -> SignalAttachment! {
|
||||
public class func imageAttachment(withImage image: UIImage?, dataUTI: String!) -> SignalAttachment! {
|
||||
assert(dataUTI.characters.count > 0)
|
||||
|
||||
|
||||
guard let image = image else {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
// Make a placeholder attachment on which to hang errors if necessary.
|
||||
let attachment = SignalAttachment(data : Data(), dataUTI: dataUTI)
|
||||
|
||||
Logger.verbose("\(TAG) Converting attachment to image/jpeg")
|
||||
return recompressImageAsJPEG(image : image, attachment : attachment)
|
||||
attachment.image = image
|
||||
|
||||
Logger.verbose("\(TAG) Writing \(attachment.mimeType()) as image/jpeg")
|
||||
return compressImageAsJPEG(image : image, attachment : attachment)
|
||||
}
|
||||
|
||||
private class func recompressImageAsJPEG(image : UIImage!, attachment : SignalAttachment!) -> SignalAttachment! {
|
||||
private class func compressImageAsJPEG(image: UIImage!, attachment: SignalAttachment!) -> SignalAttachment! {
|
||||
assert(attachment.error == nil)
|
||||
|
||||
|
||||
var imageUploadQuality = Environment.preferences().imageUploadQuality()
|
||||
|
||||
|
||||
while true {
|
||||
let maxSize = maxSizeForImage(image: image, imageUploadQuality:imageUploadQuality)
|
||||
let resizedImage = imageScaled(image, toMaxSize: maxSize)
|
||||
guard let jpgImageData = UIImageJPEGRepresentation(resizedImage,
|
||||
var dstImage: UIImage! = image
|
||||
if image.size.width > maxSize ||
|
||||
image.size.height > maxSize {
|
||||
dstImage = imageScaled(image, toMaxSize: maxSize)
|
||||
}
|
||||
guard let jpgImageData = UIImageJPEGRepresentation(dstImage,
|
||||
jpegCompressionQuality(imageUploadQuality:imageUploadQuality)) else {
|
||||
attachment.error = .couldNotConvertToJpeg
|
||||
return attachment
|
||||
}
|
||||
|
||||
if jpgImageData.count <= kMaxFileSize_Image {
|
||||
return SignalAttachment(data : jpgImageData, dataUTI: kUTTypeJPEG as String)
|
||||
|
||||
if jpgImageData.count <= kMaxFileSizeImage {
|
||||
let recompressedAttachment = SignalAttachment(data : jpgImageData, dataUTI: kUTTypeJPEG as String)
|
||||
recompressedAttachment.image = dstImage
|
||||
return recompressedAttachment
|
||||
}
|
||||
|
||||
|
||||
// If the JPEG output is larger than the file size limit,
|
||||
// continue to try again by progressively reducing the
|
||||
// image upload quality.
|
||||
|
@ -295,14 +342,13 @@ class SignalAttachment: NSObject {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class func imageScaled(_ image: UIImage, toMaxSize size: CGFloat) -> UIImage {
|
||||
var scaleFactor: CGFloat
|
||||
let aspectRatio: CGFloat = image.size.height / image.size.width
|
||||
if aspectRatio > 1 {
|
||||
scaleFactor = size / image.size.width
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
scaleFactor = size / image.size.height
|
||||
}
|
||||
let newSize = CGSize(width: CGFloat(image.size.width * scaleFactor), height: CGFloat(image.size.height * scaleFactor))
|
||||
|
@ -312,7 +358,7 @@ class SignalAttachment: NSObject {
|
|||
UIGraphicsEndImageContext()
|
||||
return updatedImage!
|
||||
}
|
||||
|
||||
|
||||
private class func maxSizeForImage(image: UIImage, imageUploadQuality: TSImageQuality) -> CGFloat {
|
||||
switch imageUploadQuality {
|
||||
case .uncropped:
|
||||
|
@ -325,7 +371,7 @@ class SignalAttachment: NSObject {
|
|||
return 512
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class func jpegCompressionQuality(imageUploadQuality: TSImageQuality) -> CGFloat {
|
||||
switch imageUploadQuality {
|
||||
case .uncropped:
|
||||
|
@ -338,79 +384,79 @@ class SignalAttachment: NSObject {
|
|||
return 0.3
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Video Attachments
|
||||
|
||||
|
||||
// Factory method for video attachments.
|
||||
//
|
||||
// NOTE: The attachment returned by this method may not be valid.
|
||||
// Check the attachment's error property.
|
||||
public class func videoAttachment(withData data : Data?, dataUTI : String!) -> SignalAttachment! {
|
||||
public class func videoAttachment(withData data: Data?, dataUTI: String!) -> SignalAttachment! {
|
||||
return newAttachment(withData : data,
|
||||
dataUTI : dataUTI,
|
||||
validUTISet : videoUTISet(),
|
||||
maxFileSize : kMaxFileSize_Video)
|
||||
maxFileSize : kMaxFileSizeVideo)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Audio Attachments
|
||||
|
||||
|
||||
// Factory method for audio attachments.
|
||||
//
|
||||
// NOTE: The attachment returned by this method may not be valid.
|
||||
// Check the attachment's error property.
|
||||
public class func audioAttachment(withData data : Data?, dataUTI : String!) -> SignalAttachment! {
|
||||
public class func audioAttachment(withData data: Data?, dataUTI: String!) -> SignalAttachment! {
|
||||
return newAttachment(withData : data,
|
||||
dataUTI : dataUTI,
|
||||
validUTISet : audioUTISet(),
|
||||
maxFileSize : kMaxFileSize_Audio)
|
||||
maxFileSize : kMaxFileSizeAudio)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Generic Attachments
|
||||
|
||||
|
||||
// Factory method for generic attachments.
|
||||
//
|
||||
// NOTE: The attachment returned by this method may not be valid.
|
||||
// Check the attachment's error property.
|
||||
public class func genericAttachment(withData data : Data?, dataUTI : String!) -> SignalAttachment! {
|
||||
public class func genericAttachment(withData data: Data?, dataUTI: String!) -> SignalAttachment! {
|
||||
return newAttachment(withData : data,
|
||||
dataUTI : dataUTI,
|
||||
validUTISet : nil,
|
||||
maxFileSize : kMaxFileSize_Generic)
|
||||
maxFileSize : kMaxFileSizeGeneric)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Helper Methods
|
||||
|
||||
private class func newAttachment(withData data : Data?,
|
||||
dataUTI : String!,
|
||||
validUTISet : Set<String>?,
|
||||
maxFileSize : Int) -> SignalAttachment! {
|
||||
|
||||
private class func newAttachment(withData data: Data?,
|
||||
dataUTI: String!,
|
||||
validUTISet: Set<String>?,
|
||||
maxFileSize: Int) -> SignalAttachment! {
|
||||
assert(dataUTI.characters.count > 0)
|
||||
|
||||
|
||||
assert(data != nil)
|
||||
guard let data = data else {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
let attachment = SignalAttachment(data : data, dataUTI: dataUTI)
|
||||
|
||||
|
||||
if validUTISet != nil {
|
||||
guard validUTISet!.contains(dataUTI) else {
|
||||
attachment.error = .invalidFileFormat
|
||||
return attachment
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
guard data.count > 0 else {
|
||||
assert(data.count > 0)
|
||||
attachment.error = .invalidData
|
||||
return attachment
|
||||
}
|
||||
|
||||
|
||||
guard data.count <= maxFileSize else {
|
||||
attachment.error = .fileSizeTooLarge
|
||||
return attachment
|
||||
}
|
||||
|
||||
|
||||
// Attachment is valid
|
||||
return attachment
|
||||
}
|
||||
|
|
|
@ -52,6 +52,21 @@
|
|||
/* No comment provided by engineer. */
|
||||
"ATTACHMENT" = "Attachment";
|
||||
|
||||
/* Label for 'cancel' button in the 'attachment approval' dialog. */
|
||||
"ATTACHMENT_APPROVAL_CANCEL_BUTTON" = "Cancel";
|
||||
|
||||
/* Title for the 'attachment approval' dialog. */
|
||||
"ATTACHMENT_APPROVAL_DIALOG_TITLE" = "Attachment";
|
||||
|
||||
/* Format string for file extension label in call interstitial view */
|
||||
"ATTACHMENT_APPROVAL_FILE_EXTENSION_FORMAT" = "File type: %@";
|
||||
|
||||
/* Format string for file size label in call interstitial view */
|
||||
"ATTACHMENT_APPROVAL_FILE_SIZE_FORMAT" = "File size: %@";
|
||||
|
||||
/* Label for 'send' button in the 'attachment approval' dialog. */
|
||||
"ATTACHMENT_APPROVAL_SEND_BUTTON" = "Send";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"ATTACHMENT_DOWNLOAD_FAILED" = "Attachment download failed, tap to retry.";
|
||||
|
||||
|
|
Loading…
Reference in New Issue