//
// C o p y r i g h t ( c ) 2 0 1 9 O p e n W h i s p e r S y s t e m s . A l l r i g h t s r e s e r v e d .
//
import Foundation
import MediaPlayer
import YYImage
import NVActivityIndicatorView
import SessionUIKit
public class MediaMessageView : UIView , OWSAudioPlayerDelegate {
public enum Mode : UInt {
case large
case small
case attachmentApproval
}
// MARK: P r o p e r t i e s
public let mode : Mode
public let attachment : SignalAttachment
public var audioPlayer : OWSAudioPlayer ?
private var linkPreviewInfo : ( url : String , draft : OWSLinkPreviewDraft ? ) ?
public var playbackState = AudioPlaybackState . stopped {
didSet {
AssertIsOnMainThread ( )
ensureButtonState ( )
}
}
public var audioProgressSeconds : CGFloat = 0
public var audioDurationSeconds : CGFloat = 0
public var contentView : UIView ?
// MARK: I n i t i a l i z e r s
@ available ( * , unavailable , message : " use other constructor instead. " )
required public init ? ( coder aDecoder : NSCoder ) {
notImplemented ( )
}
// C u r r e n t l y w e o n l y u s e o n e m o d e ( A t t a c h m e n t A p p r o v a l ) , s o w e c o u l d s i m p l i f y t h i s c l a s s , b u t i t ' s k i n d
// o f n i c e t h a t i t ' s w r i t t e n i n a f l e x i b l e w a y i n c a s e w e ' d w a n t t o u s e i t e l s e w h e r e a g a i n i n t h e f u t u r e .
public required init ( attachment : SignalAttachment , mode : MediaMessageView . Mode ) {
if attachment . hasError { owsFailDebug ( attachment . error . debugDescription ) }
self . attachment = attachment
self . mode = mode
super . init ( frame : CGRect . zero )
createViews ( )
backgroundColor = . red
setupLayout ( )
}
deinit {
NotificationCenter . default . removeObserver ( self )
}
// MARK: - U I
private lazy var stackView : UIStackView = {
let stackView : UIStackView = UIStackView ( )
stackView . translatesAutoresizingMaskIntoConstraints = false
stackView . axis = . vertical
stackView . alignment = . center
stackView . distribution = . equalSpacing
switch mode {
case . large , . attachmentApproval : stackView . spacing = 10
case . small : stackView . spacing = 5
}
return stackView
} ( )
private lazy var imageView : UIImageView = {
let view : UIImageView = UIImageView ( )
view . translatesAutoresizingMaskIntoConstraints = false
view . contentMode = . scaleAspectFit
view . layer . minificationFilter = . trilinear
view . layer . magnificationFilter = . trilinear
return view
} ( )
private lazy var fileTypeImageView : UIImageView = {
let view : UIImageView = UIImageView ( )
view . translatesAutoresizingMaskIntoConstraints = false
view . layer . minificationFilter = . trilinear
view . layer . magnificationFilter = . trilinear
return view
} ( )
private lazy var animatedImageView : YYAnimatedImageView = {
let view : YYAnimatedImageView = YYAnimatedImageView ( )
view . translatesAutoresizingMaskIntoConstraints = false
return view
} ( )
lazy var videoPlayButton : UIImageView = {
let imageView : UIImageView = UIImageView ( image : UIImage ( named : " CirclePlay " ) )
imageView . translatesAutoresizingMaskIntoConstraints = false
imageView . contentMode = . scaleAspectFit
return imageView
} ( )
// / N o t e : T h i s u s e s d i f f e r e n t a s s e t s f r o m t h e ` v i d e o P l a y B u t t o n ` a n d h a s a ' P a u s e ' s t a t e
private lazy var audioPlayPauseButton : UIButton = {
let button : UIButton = UIButton ( )
button . translatesAutoresizingMaskIntoConstraints = false
button . clipsToBounds = true
button . setBackgroundImage ( UIColor . white . toImage ( ) , for : . normal )
button . setBackgroundImage ( UIColor . white . darken ( by : 0.2 ) . toImage ( ) , for : . highlighted )
button . layer . cornerRadius = 30
button . addTarget ( self , action : #selector ( audioPlayPauseButtonPressed ) , for : . touchUpInside )
return button
} ( )
private lazy var titleLabel : UILabel = {
let label : UILabel = UILabel ( )
label . translatesAutoresizingMaskIntoConstraints = false
label . font = labelFont ( )
label . text = ( formattedFileName ( ) ? ? formattedFileExtension ( ) )
label . textColor = controlTintColor
label . textAlignment = . center
label . lineBreakMode = . byTruncatingMiddle
label . isHidden = ( ( label . text ? . count ? ? 0 ) = = 0 )
return label
} ( )
private lazy var fileSizeLabel : UILabel = {
let fileSize : UInt = attachment . dataLength
let label : UILabel = UILabel ( )
label . translatesAutoresizingMaskIntoConstraints = false
label . font = labelFont ( )
// F o r m a t s t r i n g f o r f i l e s i z e l a b e l i n c a l l i n t e r s t i t i a l v i e w .
// E m b e d s : { { f i l e s i z e a s ' N m b ' o r ' N k b ' } } .
label . text = String ( format : " ATTACHMENT_APPROVAL_FILE_SIZE_FORMAT " . localized ( ) , OWSFormat . formatFileSize ( UInt ( fileSize ) ) )
label . textColor = controlTintColor
label . textAlignment = . center
return label
} ( )
// MARK: - L a y o u t
private func createViews ( ) {
if attachment . isAnimatedImage {
createAnimatedPreview ( )
} else if attachment . isImage {
createImagePreview ( )
} else if attachment . isVideo {
createVideoPreview ( )
} else if attachment . isAudio {
createAudioPreview ( )
} else if attachment . isUrl {
createUrlPreview ( )
} else if attachment . isText {
// D o n o t h i n g a s w e w i l l j u s t p u t t h e t e x t i n t h e ' m e s s a g e ' i n p u t
} else {
createGenericPreview ( )
}
}
private func setupLayout ( ) {
// B o t t o m i n s e t
}
// TODO: A n y r e a s o n f o r n o t j u s t u s i n g U I S t a c k V i e w
private func wrapViewsInVerticalStack ( subviews : [ UIView ] ) -> UIView {
assert ( subviews . count > 0 )
let stackView = UIView ( )
var lastView : UIView ?
for subview in subviews {
stackView . addSubview ( subview )
subview . autoHCenterInSuperview ( )
if lastView = = nil {
subview . autoPinEdge ( toSuperviewEdge : . top )
} else {
subview . autoPinEdge ( . top , to : . bottom , of : lastView ! , withOffset : 10 )
}
lastView = subview
}
lastView ? . autoPinEdge ( toSuperviewEdge : . bottom )
return stackView
}
private func wrapViewsInHorizontalStack ( subviews : [ UIView ] ) -> UIView {
assert ( subviews . count > 0 )
let stackView = UIView ( )
var lastView : UIView ?
for subview in subviews {
stackView . addSubview ( subview )
subview . autoVCenterInSuperview ( )
if lastView = = nil {
subview . autoPinEdge ( toSuperviewEdge : . left )
} else {
subview . autoPinEdge ( . left , to : . right , of : lastView ! , withOffset : 10 )
}
lastView = subview
}
lastView ? . autoPinEdge ( toSuperviewEdge : . right )
return stackView
}
// p r i v a t e f u n c s t a c k S p a c i n g ( ) - > C G F l o a t {
// s w i t c h m o d e {
// c a s e . l a r g e , . a t t a c h m e n t A p p r o v a l :
// r e t u r n C G F l o a t ( 1 0 )
// c a s e . s m a l l :
// r e t u r n C G F l o a t ( 5 )
// }
// }
private func createAudioPreview ( ) {
guard let dataUrl = attachment . dataUrl else {
createGenericPreview ( )
return
}
audioPlayer = OWSAudioPlayer ( mediaUrl : dataUrl , audioBehavior : . playback , delegate : self )
imageView . image = UIImage ( named : " FileLarge " )
fileTypeImageView . image = UIImage ( named : " table_ic_notification_sound " )
setAudioIconToPlay ( )
self . addSubview ( stackView )
self . addSubview ( audioPlayPauseButton )
stackView . addArrangedSubview ( imageView )
stackView . addArrangedSubview ( UIView . vSpacer ( 0 ) )
stackView . addArrangedSubview ( titleLabel )
stackView . addArrangedSubview ( fileSizeLabel )
imageView . addSubview ( fileTypeImageView )
NSLayoutConstraint . activate ( [
stackView . centerYAnchor . constraint ( equalTo : centerYAnchor ) ,
stackView . widthAnchor . constraint ( equalTo : widthAnchor ) ,
stackView . heightAnchor . constraint ( lessThanOrEqualTo : heightAnchor ) ,
imageView . widthAnchor . constraint ( equalToConstant : 150 ) ,
imageView . heightAnchor . constraint ( equalToConstant : 150 ) ,
titleLabel . widthAnchor . constraint ( equalTo : stackView . widthAnchor , constant : - ( 32 * 2 ) ) ,
fileSizeLabel . widthAnchor . constraint ( equalTo : stackView . widthAnchor , constant : - ( 32 * 2 ) ) ,
fileTypeImageView . centerXAnchor . constraint ( equalTo : imageView . centerXAnchor ) ,
fileTypeImageView . centerYAnchor . constraint (
equalTo : imageView . centerYAnchor ,
constant : 25
) ,
fileTypeImageView . widthAnchor . constraint (
equalTo : fileTypeImageView . heightAnchor ,
multiplier : ( ( fileTypeImageView . image ? . size . width ? ? 1 ) / ( fileTypeImageView . image ? . size . height ? ? 1 ) )
) ,
fileTypeImageView . widthAnchor . constraint (
equalTo : imageView . widthAnchor , constant : - 75
) ,
audioPlayPauseButton . centerXAnchor . constraint ( equalTo : imageView . centerXAnchor ) ,
audioPlayPauseButton . centerYAnchor . constraint ( equalTo : imageView . centerYAnchor ) ,
audioPlayPauseButton . widthAnchor . constraint (
equalToConstant : ( audioPlayPauseButton . layer . cornerRadius * 2 )
) ,
audioPlayPauseButton . heightAnchor . constraint (
equalToConstant : ( audioPlayPauseButton . layer . cornerRadius * 2 )
)
] )
}
private func createAnimatedPreview ( ) {
guard attachment . isValidImage else {
createGenericPreview ( )
return
}
guard let dataUrl = attachment . dataUrl else {
createGenericPreview ( )
return
}
guard let image = YYImage ( contentsOfFile : dataUrl . path ) else {
createGenericPreview ( )
return
}
guard image . size . width > 0 && image . size . height > 0 else {
createGenericPreview ( )
return
}
animatedImageView . image = image
let aspectRatio : CGFloat = ( image . size . width / image . size . height )
let clampedRatio : CGFloat = CGFloatClamp ( aspectRatio , 0.05 , 95.0 )
addSubview ( animatedImageView )
// a d d S u b v i e w W i t h S c a l e A s p e c t F i t L a y o u t ( v i e w : a n i m a t e d I m a g e V i e w , a s p e c t R a t i o : a s p e c t R a t i o )
contentView = animatedImageView
NSLayoutConstraint . activate ( [
animatedImageView . centerXAnchor . constraint ( equalTo : centerXAnchor ) ,
animatedImageView . centerYAnchor . constraint ( equalTo : centerYAnchor ) ,
animatedImageView . widthAnchor . constraint (
equalTo : animatedImageView . heightAnchor ,
multiplier : clampedRatio
) ,
animatedImageView . widthAnchor . constraint ( lessThanOrEqualTo : widthAnchor ) ,
animatedImageView . heightAnchor . constraint ( lessThanOrEqualTo : heightAnchor )
] )
}
// p r i v a t e f u n c a d d S u b v i e w W i t h S c a l e A s p e c t F i t L a y o u t ( v i e w : U I V i e w , a s p e c t R a t i o : C G F l o a t ) {
// s e l f . a d d S u b v i e w ( v i e w )
// / / T h i s e m u l a t e s t h e b e h a v i o r o f c o n t e n t M o d e = . s c a l e A s p e c t F i t u s i n g
// / / i O S a u t o l a y o u t c o n s t r a i n t s .
// / /
// / / T h i s a l l o w s C o n v e r s a t i o n I n p u t T o o l b a r t o p l a c e t h e " c a n c e l " b u t t o n
// / / i n t h e u p p e r - r i g h t h a n d c o r n e r o f t h e p r e v i e w c o n t e n t .
// v i e w . a u t o C e n t e r I n S u p e r v i e w ( )
// v i e w . a u t o P i n ( t o A s p e c t R a t i o : a s p e c t R a t i o )
// v i e w . a u t o M a t c h ( . w i d t h , t o : . w i d t h , o f : s e l f , w i t h M u l t i p l i e r : 1 . 0 , r e l a t i o n : . l e s s T h a n O r E q u a l )
// v i e w . a u t o M a t c h ( . h e i g h t , t o : . h e i g h t , o f : s e l f , w i t h M u l t i p l i e r : 1 . 0 , r e l a t i o n : . l e s s T h a n O r E q u a l )
// }
private func createImagePreview ( ) {
guard attachment . isValidImage else {
createGenericPreview ( )
return
}
guard let image = attachment . image ( ) else {
createGenericPreview ( )
return
}
guard image . size . width > 0 && image . size . height > 0 else {
createGenericPreview ( )
return
}
imageView . image = image
// i m a g e V i e w . l a y e r . m i n i f i c a t i o n F i l t e r = . t r i l i n e a r
// i m a g e V i e w . l a y e r . m a g n i f i c a t i o n F i l t e r = . t r i l i n e a r
let aspectRatio = image . size . width / image . size . height
let clampedRatio : CGFloat = CGFloatClamp ( aspectRatio , 0.05 , 95.0 )
// a d d S u b v i e w W i t h S c a l e A s p e c t F i t L a y o u t ( v i e w : i m a g e V i e w , a s p e c t R a t i o : a s p e c t R a t i o )
contentView = imageView
NSLayoutConstraint . activate ( [
imageView . centerXAnchor . constraint ( equalTo : centerXAnchor ) ,
imageView . centerYAnchor . constraint ( equalTo : centerYAnchor ) ,
imageView . widthAnchor . constraint (
equalTo : imageView . heightAnchor ,
multiplier : clampedRatio
) ,
imageView . widthAnchor . constraint ( lessThanOrEqualTo : widthAnchor ) ,
imageView . heightAnchor . constraint ( lessThanOrEqualTo : heightAnchor )
] )
}
private func createVideoPreview ( ) {
guard attachment . isValidVideo else {
createGenericPreview ( )
return
}
guard let image = attachment . videoPreview ( ) else {
createGenericPreview ( )
return
}
guard image . size . width > 0 && image . size . height > 0 else {
createGenericPreview ( )
return
}
// l e t i m a g e V i e w = U I I m a g e V i e w ( i m a g e : i m a g e )
imageView . image = image
// i m a g e V i e w . l a y e r . m i n i f i c a t i o n F i l t e r = . t r i l i n e a r
// i m a g e V i e w . l a y e r . m a g n i f i c a t i o n F i l t e r = . t r i l i n e a r
self . addSubview ( imageView )
let aspectRatio = image . size . width / image . size . height
let clampedRatio : CGFloat = CGFloatClamp ( aspectRatio , 0.05 , 95.0 )
// a d d S u b v i e w W i t h S c a l e A s p e c t F i t L a y o u t ( v i e w : i m a g e V i e w , a s p e c t R a t i o : a s p e c t R a t i o )
contentView = imageView
// A t t a c h m e n t a p p r o v a l p r o v i d e s i t ' s o w n p l a y b u t t o n t o k e e p i t
// a t t h e p r o p e r z o o m s c a l e .
if mode != . attachmentApproval {
self . addSubview ( videoPlayButton )
}
NSLayoutConstraint . activate ( [
imageView . centerXAnchor . constraint ( equalTo : centerXAnchor ) ,
imageView . centerYAnchor . constraint ( equalTo : centerYAnchor ) ,
imageView . widthAnchor . constraint (
equalTo : imageView . heightAnchor ,
multiplier : clampedRatio
) ,
imageView . widthAnchor . constraint ( lessThanOrEqualTo : widthAnchor ) ,
imageView . heightAnchor . constraint ( lessThanOrEqualTo : heightAnchor )
] )
// A t t a c h m e n t a p p r o v a l p r o v i d e s i t ' s o w n p l a y b u t t o n t o k e e p i t
// a t t h e p r o p e r z o o m s c a l e .
if mode != . attachmentApproval {
self . addSubview ( videoPlayButton )
// v i d e o P l a y B u t t o n . a u t o C e n t e r I n S u p e r v i e w ( )
// v i d e o P l a y B u t t o n . a u t o S e t D i m e n s i o n ( . w i d t h , t o S i z e : 7 2 )
// v i d e o P l a y B u t t o n . a u t o S e t D i m e n s i o n ( . h e i g h t , t o S i z e : 7 2 )
NSLayoutConstraint . activate ( [
videoPlayButton . centerXAnchor . constraint ( equalTo : centerXAnchor ) ,
videoPlayButton . centerYAnchor . constraint ( equalTo : centerYAnchor ) ,
imageView . widthAnchor . constraint ( equalToConstant : 72 ) ,
imageView . heightAnchor . constraint ( equalToConstant : 72 )
] )
}
}
private func createUrlPreview ( ) {
// I f l i n k p r e v i e w s a r e n ' t e n a b l e d t h e n u s e a f a l l b a c k s t a t e
guard let linkPreviewURL : String = OWSLinkPreview . previewURL ( forRawBodyText : attachment . text ( ) ) else {
// " v c _ s h a r e _ l i n k _ p r e v i e w s _ d i s a b l e d _ t i t l e " = " L i n k P r e v i e w s D i s a b l e d " ;
// " v c _ s h a r e _ l i n k _ p r e v i e w s _ d i s a b l e d _ e x p l a n a t i o n " = " E n a b l i n g l i n k p r e v i e w s w i l l s h o w p r e v i e w s f o r U R L s y o u s s h a r e . T h i s c a n b e u s e f u l , b u t S e s s i o n w i l l n e e d t o c o n t a c t l i n k e d w e b s i t e s t o g e n e r a t e p r e v i e w s . Y o u c a n e n a b l e l i n k p r e v i e w s i n S e s s i o n ' s s e t t i n g s . " ;
// TODO: S h o w " w a r n i n g " a b o u t d i s a b l e d l i n k p r e v i e w s i n s t e a d
createGenericPreview ( )
return
}
linkPreviewInfo = ( url : linkPreviewURL , draft : nil )
var subviews = [ UIView ] ( )
let color : UIColor = isLightMode ? . black : . white
let loadingView = NVActivityIndicatorView ( frame : CGRect . zero , type : . circleStrokeSpin , color : color , padding : nil )
loadingView . set ( . width , to : 24 )
loadingView . set ( . height , to : 24 )
loadingView . startAnimating ( )
subviews . append ( loadingView )
let imageViewContainer = UIView ( )
imageViewContainer . clipsToBounds = true
imageViewContainer . contentMode = . center
imageViewContainer . alpha = 0
imageViewContainer . layer . cornerRadius = 8
subviews . append ( imageViewContainer )
let imageView = createHeroImageView ( imageName : " FileLarge " )
imageViewContainer . addSubview ( imageView )
imageView . pin ( to : imageViewContainer )
let titleLabel = UILabel ( )
titleLabel . text = linkPreviewURL
titleLabel . textColor = controlTintColor
titleLabel . font = labelFont ( )
titleLabel . textAlignment = . center
titleLabel . lineBreakMode = . byTruncatingMiddle
subviews . append ( titleLabel )
let stackView = wrapViewsInVerticalStack ( subviews : subviews )
self . addSubview ( stackView )
titleLabel . autoPinWidthToSuperview ( withMargin : 32 )
NSLayoutConstraint . activate ( [
imageView . widthAnchor . constraint ( equalToConstant : 80 ) ,
imageView . heightAnchor . constraint ( equalToConstant : 80 )
] )
// B u i l d t h e l i n k p r e v i e w
OWSLinkPreview . tryToBuildPreviewInfo ( previewUrl : linkPreviewURL ) . done { [ weak self ] draft in
// L o a d e r
loadingView . alpha = 0
loadingView . stopAnimating ( )
self ? . linkPreviewInfo = ( url : linkPreviewURL , draft : draft )
// TODO: L o o k a t r e f a c t o r i n g t h i s b e h a v i o u r t o c o n s o l i d a t e a t t a c h m e n t m u t a t i o n s
self ? . attachment . linkPreviewDraft = draft
let image : UIImage ?
if let jpegImageData : Data = draft . jpegImageData , let loadedImage : UIImage = UIImage ( data : jpegImageData ) {
image = loadedImage
imageView . contentMode = . scaleAspectFill
}
else {
image = UIImage ( named : " Link " ) ? . withTint ( isLightMode ? . black : . white )
imageView . contentMode = . center
}
// I m a g e v i e w
( imageView as ? UIImageView ) ? . image = image
imageViewContainer . alpha = 1
imageViewContainer . backgroundColor = isDarkMode ? . black : UIColor . black . withAlphaComponent ( 0.06 )
// T i t l e
if let title = draft . title {
titleLabel . font = . boldSystemFont ( ofSize : Values . smallFontSize )
titleLabel . text = title
titleLabel . textAlignment = . left
titleLabel . numberOfLines = 2
}
guard let hStackView = self ? . wrapViewsInHorizontalStack ( subviews : subviews ) else {
// TODO: F a l l b a c k
return
}
stackView . removeFromSuperview ( )
self ? . addSubview ( hStackView )
// W e w a n t t o c e n t e r t h e s t a c k V i e w i n i t ' s s u p e r v i e w w h i l e a l s o e n s u r i n g
// i t ' s s u p e r v i e w i s b i g e n o u g h t o c o n t a i n i t .
hStackView . autoPinWidthToSuperview ( withMargin : 32 )
hStackView . autoVCenterInSuperview ( )
NSLayoutConstraint . autoSetPriority ( UILayoutPriority . defaultLow ) {
hStackView . autoPinHeightToSuperview ( )
}
hStackView . autoPinEdge ( toSuperviewEdge : . top , withInset : 0 , relation : . greaterThanOrEqual )
hStackView . autoPinEdge ( toSuperviewEdge : . bottom , withInset : 0 , relation : . greaterThanOrEqual )
} . catch { _ in
// TODO: F a l l b a c k
loadingView . stopAnimating ( )
} . retainUntilComplete ( )
// W e w a n t t o c e n t e r t h e s t a c k V i e w i n i t ' s s u p e r v i e w w h i l e a l s o e n s u r i n g
// i t ' s s u p e r v i e w i s b i g e n o u g h t o c o n t a i n i t .
stackView . autoPinWidthToSuperview ( )
stackView . autoVCenterInSuperview ( )
NSLayoutConstraint . autoSetPriority ( UILayoutPriority . defaultLow ) {
stackView . autoPinHeightToSuperview ( )
}
stackView . autoPinEdge ( toSuperviewEdge : . top , withInset : 0 , relation : . greaterThanOrEqual )
stackView . autoPinEdge ( toSuperviewEdge : . bottom , withInset : 0 , relation : . greaterThanOrEqual )
}
private func createGenericPreview ( ) {
imageView . image = UIImage ( named : " FileLarge " )
stackView . backgroundColor = . green
self . addSubview ( stackView )
stackView . addArrangedSubview ( imageView )
stackView . addArrangedSubview ( UIView . vSpacer ( 0 ) )
stackView . addArrangedSubview ( titleLabel )
stackView . addArrangedSubview ( fileSizeLabel )
imageView . addSubview ( fileTypeImageView )
NSLayoutConstraint . activate ( [
stackView . centerYAnchor . constraint ( equalTo : centerYAnchor ) ,
stackView . widthAnchor . constraint ( equalTo : widthAnchor ) ,
stackView . heightAnchor . constraint ( lessThanOrEqualTo : heightAnchor ) ,
imageView . widthAnchor . constraint ( equalToConstant : 150 ) ,
imageView . heightAnchor . constraint ( equalToConstant : 150 ) ,
titleLabel . widthAnchor . constraint ( equalTo : stackView . widthAnchor , constant : - ( 32 * 2 ) ) ,
fileSizeLabel . widthAnchor . constraint ( equalTo : stackView . widthAnchor , constant : - ( 32 * 2 ) ) ,
fileTypeImageView . centerXAnchor . constraint ( equalTo : imageView . centerXAnchor ) ,
fileTypeImageView . centerYAnchor . constraint (
equalTo : imageView . centerYAnchor ,
constant : 25
) ,
fileTypeImageView . widthAnchor . constraint (
equalTo : fileTypeImageView . heightAnchor ,
multiplier : ( ( fileTypeImageView . image ? . size . width ? ? 1 ) / ( fileTypeImageView . image ? . size . height ? ? 1 ) )
) ,
fileTypeImageView . widthAnchor . constraint (
equalTo : imageView . widthAnchor , constant : - 75
)
] )
}
private func createHeroViewSize ( ) -> CGFloat {
switch mode {
case . large :
return ScaleFromIPhone5To7Plus ( 175 , 225 )
case . attachmentApproval :
return ScaleFromIPhone5 ( 100 )
case . small :
return ScaleFromIPhone5To7Plus ( 80 , 80 )
}
}
private func createHeroImageView ( imageName : String ) -> UIView {
let imageSize = createHeroViewSize ( )
let image = UIImage ( named : imageName )
assert ( image != nil )
let imageView = UIImageView ( image : image )
imageView . layer . minificationFilter = . trilinear
imageView . layer . magnificationFilter = . trilinear
imageView . layer . shadowColor = UIColor . black . cgColor
let shadowScaling = 5.0
imageView . layer . shadowRadius = CGFloat ( 2.0 * shadowScaling )
imageView . layer . shadowOpacity = 0.25
imageView . layer . shadowOffset = CGSize ( width : 0.75 * shadowScaling , height : 0.75 * shadowScaling )
imageView . autoSetDimension ( . width , toSize : imageSize )
imageView . autoSetDimension ( . height , toSize : imageSize )
return imageView
}
private func labelFont ( ) -> UIFont {
switch mode {
case . large , . attachmentApproval :
return UIFont . ows_regularFont ( withSize : ScaleFromIPhone5To7Plus ( 18 , 24 ) )
case . small :
return UIFont . ows_regularFont ( withSize : ScaleFromIPhone5To7Plus ( 14 , 14 ) )
}
}
private var controlTintColor : UIColor {
switch mode {
case . small , . large :
return Colors . accent
case . attachmentApproval :
return Colors . text
}
}
private func formattedFileExtension ( ) -> String ? {
guard let fileExtension = attachment . fileExtension else {
return nil
}
// " F o r m a t s t r i n g f o r f i l e e x t e n s i o n l a b e l i n c a l l i n t e r s t i t i a l v i e w "
return String ( format : " ATTACHMENT_APPROVAL_FILE_EXTENSION_FORMAT " . localized ( ) , fileExtension . uppercased ( ) )
}
public func formattedFileName ( ) -> String ? {
guard let sourceFilename = attachment . sourceFilename else { return nil }
let filename = sourceFilename . trimmingCharacters ( in : CharacterSet . whitespacesAndNewlines )
guard filename . count > 0 else { return nil }
return filename
}
private func createFileNameLabel ( ) -> UIView ? {
let filename = formattedFileName ( ) ? ? formattedFileExtension ( )
guard filename != nil else {
return nil
}
let label = UILabel ( )
label . text = filename
label . textColor = controlTintColor
label . font = labelFont ( )
label . textAlignment = . center
label . lineBreakMode = . byTruncatingMiddle
return label
}
private func createFileSizeLabel ( ) -> UIView {
let label = UILabel ( )
let fileSize = attachment . dataLength
label . text = String ( format : NSLocalizedString ( " ATTACHMENT_APPROVAL_FILE_SIZE_FORMAT " ,
comment : " Format string for file size label in call interstitial view. Embeds: {{file size as 'N mb' or 'N kb'}}. " ) ,
OWSFormat . formatFileSize ( UInt ( fileSize ) ) )
label . textColor = controlTintColor
label . font = labelFont ( )
label . textAlignment = . center
return label
}
// MARK: - E v e n t H a n d l e r s
@objc func audioPlayPauseButtonPressed ( sender : UIButton ) {
audioPlayer ? . togglePlayState ( )
}
// MARK: - O W S A u d i o P l a y e r D e l e g a t e
public func audioPlaybackState ( ) -> AudioPlaybackState {
return playbackState
}
public func setAudioPlaybackState ( _ value : AudioPlaybackState ) {
playbackState = value
}
public func showInvalidAudioFileAlert ( ) {
OWSAlerts . showErrorAlert ( message : NSLocalizedString ( " INVALID_AUDIO_FILE_ALERT_ERROR_MESSAGE " , comment : " Message for the alert indicating that an audio file is invalid. " ) )
}
public func audioPlayerDidFinishPlaying ( _ player : OWSAudioPlayer , successfully flag : Bool ) {
// D o n o t h i n g
}
private func ensureButtonState ( ) {
if playbackState = = . playing {
setAudioIconToPause ( )
} else {
setAudioIconToPlay ( )
}
}
public func setAudioProgress ( _ progress : CGFloat , duration : CGFloat ) {
audioProgressSeconds = progress
audioDurationSeconds = duration
}
private func setAudioIconToPlay ( ) {
// a t t a c h m e n t _ a u d i o
// l e t i m a g e = U I I m a g e ( n a m e d : " a u d i o _ p l a y _ b l a c k _ l a r g e " ) ? . w i t h R e n d e r i n g M o d e ( . a l w a y s T e m p l a t e )
// a s s e r t ( i m a g e ! = n i l )
// a u d i o P l a y B u t t o n ? . s e t I m a g e ( i m a g e , f o r : . n o r m a l )
// a u d i o P l a y B u t t o n ? . i m a g e V i e w ? . t i n t C o l o r = c o n t r o l T i n t C o l o r
// l e t i m a g e = U I I m a g e ( n a m e d : " C i r c l e P l a y " )
let image = UIImage ( named : " Play " )
audioPlayPauseButton . setImage ( image , for : . normal )
}
private func setAudioIconToPause ( ) {
// l e t i m a g e = U I I m a g e ( n a m e d : " a u d i o _ p a u s e _ b l a c k _ l a r g e " ) ? . w i t h R e n d e r i n g M o d e ( . a l w a y s T e m p l a t e )
// a s s e r t ( i m a g e ! = n i l )
// a u d i o P l a y B u t t o n ? . s e t I m a g e ( i m a g e , f o r : . n o r m a l )
// a u d i o P l a y B u t t o n ? . i m a g e V i e w ? . t i n t C o l o r = c o n t r o l T i n t C o l o r
let image = UIImage ( named : " Pause " )
audioPlayPauseButton . setImage ( image , for : . normal )
}
}