Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Signal/ConversationView/CVItemViewModelImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,10 @@ extension CVItemViewModelImpl {
AttachmentSharing.showShareUI(for: attachments, sender: sender)
}

var isSticker: Bool {
return messageCellType == .stickerMessage
}

var canForwardMessage: Bool {
guard !isViewOnce else {
return false
Expand Down
2 changes: 1 addition & 1 deletion Signal/ConversationView/CellViews/CVMediaView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ public class CVMediaView: ManualLayoutViewWithLayer {
mediaView.backgroundColor = isBorderless ? .clear : Theme.washColor

if !addProgressIfNecessary() {
if reusableMediaView.isVideo {
if reusableMediaView.needsPlayButton {
addVideoPlayButton()
}
}
Expand Down
19 changes: 19 additions & 0 deletions Signal/ConversationView/CellViews/ReusableMediaView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,21 @@ public class ReusableMediaView: NSObject {
mediaViewAdapter is MediaViewAdapterVideo
}

var needsPlayButton: Bool {
mediaViewAdapter is MediaViewAdapterVideo
|| (
UIAccessibility.isReduceMotionEnabled
&& (
mediaViewAdapter is MediaViewAdapterLoopingVideo
|| (
mediaViewAdapter is MediaViewAdapterSticker
&& mediaViewAdapter.shouldBeRenderedByYY
)
|| mediaViewAdapter is MediaViewAdapterAnimated
)
)
}

// MARK: - LoadState

// Thread-safe access to load state.
Expand Down Expand Up @@ -332,6 +347,7 @@ class MediaViewAdapterAnimated: MediaViewAdapterSwift {
return
}
imageView.image = image
imageView.autoPlayAnimatedImage = !UIAccessibility.isReduceMotionEnabled
}

func unloadMedia() {
Expand Down Expand Up @@ -568,6 +584,9 @@ public class MediaViewAdapterSticker: NSObject, MediaViewAdapterSwift {
owsFailDebug("Media has unexpected type: \(type(of: media))")
return
}
if let yyView = imageView as? CVAnimatedImageView {
yyView.autoPlayAnimatedImage = !UIAccessibility.isReduceMotionEnabled
}
imageView.image = image
} else {
guard let image = media as? UIImage else {
Expand Down
69 changes: 65 additions & 4 deletions Signal/ConversationView/Components/CVComponentSticker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,18 @@ public class CVComponentSticker: CVComponentBase, CVComponent {

// MARK: - Events

private func toggleStickerAnimation(_ view: CVComponentView) {
if let stickerView = view as? CVComponentViewSticker, let rmv = stickerView.reusableMediaView, let yyView = rmv.mediaView as? CVAnimatedImageView {
if yyView.isAnimating {
yyView.stopAnimating()
stickerView.togglePlayButton()
} else {
stickerView.togglePlayButton()
yyView.startAnimating()
}
}
}

public override func handleTap(sender: UIGestureRecognizer,
componentDelegate: CVComponentDelegate,
componentView: CVComponentView,
Expand All @@ -168,7 +180,15 @@ public class CVComponentSticker: CVComponentBase, CVComponent {
// Not yet downloaded.
return false
}
componentDelegate.didTapStickerPack(stickerMetadata.packInfo)
var isAnimated = false
if let stickerComponent = componentView as? CVComponentViewSticker {
isAnimated = stickerComponent.isAnimated
}
if UIAccessibility.isReduceMotionEnabled && isAnimated {
toggleStickerAnimation(componentView)
} else {
componentDelegate.didTapStickerPack(stickerMetadata.packInfo)
}
return true
}

Expand All @@ -179,6 +199,7 @@ public class CVComponentSticker: CVComponentBase, CVComponent {
public class CVComponentViewSticker: NSObject, CVComponentView {

fileprivate let stackView = ManualStackView(name: "sticker.container")
fileprivate var playButtonView: UIView? = nil

fileprivate var reusableMediaView: ReusableMediaView?

Expand All @@ -188,11 +209,21 @@ public class CVComponentSticker: CVComponentBase, CVComponent {
stackView
}

public var isAnimated: Bool {
get {
reusableMediaView?.needsPlayButton != nil && (reusableMediaView?.needsPlayButton)! || false
}
}

public func setIsCellVisible(_ isCellVisible: Bool) {
if isCellVisible {
if let reusableMediaView = reusableMediaView,
reusableMediaView.owner == self {
reusableMediaView.load()
if let reusableMediaView = reusableMediaView {
if reusableMediaView.owner == self {
reusableMediaView.load()
}
if reusableMediaView.needsPlayButton {
addPlayButton()
}
}
} else {
if let reusableMediaView = reusableMediaView,
Expand All @@ -202,6 +233,36 @@ public class CVComponentSticker: CVComponentBase, CVComponent {
}
}

private func addPlayButton() {
if playButtonView != nil {
return
}
let playButtonWidth: CGFloat = 44
let playIconWidth: CGFloat = 20

let playButton = UIView.transparentContainer()
playButtonView = playButton
stackView.addSubviewToCenterOnSuperview(playButton, size: CGSize(square: playButtonWidth))

let playCircleView = OWSLayerView.circleView()
playCircleView.backgroundColor = UIColor.ows_black.withAlphaComponent(0.7)
playCircleView.isUserInteractionEnabled = false
playButton.addSubview(playCircleView)
stackView.layoutSubviewToFillSuperviewEdges(playCircleView)

let playIconView = CVImageView()
playIconView.setTemplateImageName("play-fill-32", tintColor: UIColor.ows_white)
playIconView.isUserInteractionEnabled = false
stackView.addSubviewToCenterOnSuperview(playIconView,
size: CGSize(square: playIconWidth))
}

fileprivate func togglePlayButton() {
if let playButton = playButtonView {
playButton.isHidden = !playButton.isHidden
}
}

public func reset() {
stackView.reset()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ extension ConversationViewController: ContextMenuInteractionDelegate {
.showPaymentDetails,
.speak,
.stopSpeaking,
.showStickerPack,
.info,
.delete
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,4 +278,10 @@ extension ConversationViewController: MessageActionsDelegate {
let paymentsDetailViewController = PaymentsDetailViewController(paymentItem: paymentHistoryItem)
navigationController?.pushViewController(paymentsDetailViewController, animated: true)
}

func messageActionsShowStickerPack(_ itemViewModel: CVItemViewModelImpl) {
if let stickerMetadata = itemViewModel.stickerMetadata {
didTapStickerPack(stickerMetadata.packInfo)
}
}
}
19 changes: 19 additions & 0 deletions Signal/ConversationView/MessageActions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ protocol MessageActionsDelegate: AnyObject {
func messageActionsStopSpeakingItem(_ itemViewModel: CVItemViewModelImpl)
func messageActionsEditItem(_ itemViewModel: CVItemViewModelImpl)
func messageActionsShowPaymentDetails(_ itemViewModel: CVItemViewModelImpl)
func messageActionsShowStickerPack(_ itemViewModel: CVItemViewModelImpl)
}

// MARK: -
Expand Down Expand Up @@ -151,6 +152,19 @@ struct MessageActionBuilder {
}
)
}

static func showStickerPack(itemViewModel: CVItemViewModelImpl, delegate: MessageActionsDelegate) -> MessageAction {
MessageAction(
.showStickerPack,
accessibilityLabel: OWSLocalizedString("MESSAGE_ACTION_SHOW_STICKER_PACK", comment: "Action sheet accessibility label"),
accessibilityIdentifier: UIView.accessibilityIdentifier(containerName: "message_action", name: "show_sticker_pack"),
contextMenuTitle: OWSLocalizedString("CONTEXT_MENU_SHOW_STICKER_PACK", comment: "Context menu button title"),
contextMenuAttributes: [],
block: { [weak delegate] _ in
delegate?.messageActionsShowStickerPack(itemViewModel)
}
)
}
}

class MessageActions: NSObject {
Expand Down Expand Up @@ -229,6 +243,11 @@ class MessageActions: NSObject {
actions.append(editAction)
}

if itemViewModel.isSticker {
let showPackAction = MessageActionBuilder.showStickerPack(itemViewModel: itemViewModel, delegate: delegate)
actions.append(showPackAction)
}

let selectAction = MessageActionBuilder.selectMessage(itemViewModel: itemViewModel, delegate: delegate)
actions.append(selectAction)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ class MediaItemViewController: OWSViewController, VideoPlaybackStatusProvider {
guard mediaView == nil else { return }

let view: UIView
if attachmentStream.contentType.isVideo, galleryItem.renderingFlag == .shouldLoop {
if attachmentStream.contentType.isVideo, galleryItem.renderingFlag == .shouldLoop, !UIAccessibility.isReduceMotionEnabled {
if attachmentStream.contentType.isVideo, let loopingVideoPlayerView = buildLoopingVideoPlayerView() {
loopingVideoPlayerView.delegate = self
view = loopingVideoPlayerView
Expand Down Expand Up @@ -331,7 +331,7 @@ class MediaItemViewController: OWSViewController, VideoPlaybackStatusProvider {
private var hasAutoPlayedVideo = false

private var isVideo: Bool {
galleryItem.isVideo
galleryItem.isVideo || (UIAccessibility.isReduceMotionEnabled && galleryItem.isAnimated)
}

private func playVideo() {
Expand Down
3 changes: 3 additions & 0 deletions Signal/src/ViewControllers/MessageActionsToolbar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public class MessageAction: NSObject {
case stopSpeaking
case edit
case showPaymentDetails
case showStickerPack
}

let actionType: MessageActionType
Expand Down Expand Up @@ -70,6 +71,8 @@ public class MessageAction: NSObject {
return .contextMenuEdit
case .showPaymentDetails:
return .settingsPayments
case .showStickerPack:
return .contextMenuSticker
}
}()
return Theme.iconImage(icon)
Expand Down
6 changes: 6 additions & 0 deletions Signal/translations/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -1471,6 +1471,9 @@
/* Context menu button title */
"CONTEXT_MENU_SHARE_MEDIA" = "Share";

/* Context menu button title */
"CONTEXT_MENU_SHOW_STICKER_PACK" = "Show Sticker Pack";

/* Context menu button title */
"CONTEXT_MENU_SPEAK_MESSAGE" = "Speak";

Expand Down Expand Up @@ -4414,6 +4417,9 @@
/* Action sheet button title */
"MESSAGE_ACTION_SHARE_MEDIA" = "Share Media";

/* Aciton sheet button title */
"MESSAGE_ACTION_SHOW_STICKER_PACK" = "Show Sticker Pack";

/* Action sheet accessibility label */
"MESSAGE_ACTION_SPEAK_MESSAGE" = "Speak Message";

Expand Down
3 changes: 3 additions & 0 deletions SignalUI/Appearance/Theme+Icons.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ public enum ThemeIcon: UInt {
case contextMenuVoiceCall
case contextMenuVideoCall
case contextMenuMessage
case contextMenuSticker

case composeNewGroupLarge
case composeFindByUsernameLarge
Expand Down Expand Up @@ -447,6 +448,8 @@ public extension Theme {
return "video-light"
case .contextMenuMessage:
return "chat-light"
case .contextMenuSticker:
return "sticker"

// Empty chat list
case .composeNewGroupLarge:
Expand Down
1 change: 1 addition & 0 deletions SignalUI/Stickers/StickerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public class StickerView {
let yyView = YYAnimatedImageView()
yyView.alwaysInfiniteLoop = true
yyView.contentMode = .scaleAspectFit
yyView.autoPlayAnimatedImage = !UIAccessibility.isReduceMotionEnabled
yyView.image = stickerImage
stickerView = yyView
}
Expand Down
4 changes: 3 additions & 1 deletion SignalUI/Views/LoopingVideoView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,9 @@ public class LoopingVideoView: UIView {
if let asset = video?.asset {
let playerItem = AVPlayerItem(asset: asset, automaticallyLoadedAssetKeys: ["tracks"])
self.player.replaceCurrentItem(with: playerItem)
self.player.play()
if !UIAccessibility.isReduceMotionEnabled {
self.player.play()
}
self.invalidateIntrinsicContentSize()
self.delegate?.loopingVideoViewChangedPlayerItem()
}
Expand Down