This commit is contained in:
Ali 2020-11-30 02:22:00 +04:00
parent 4e73749ca2
commit 454b01aab5
2 changed files with 111 additions and 11 deletions

View File

@ -800,7 +800,7 @@ public extension ContainedViewLayoutTransition {
}
}
func updateSublayerTransformScale(node: ASDisplayNode, scale: CGPoint, completion: ((Bool) -> Void)? = nil) {
func updateSublayerTransformScale(node: ASDisplayNode, scale: CGPoint, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) {
if !node.isNodeLoaded {
node.subnodeTransform = CATransform3DMakeScale(scale.x, scale.y, 1.0)
completion?(true)
@ -826,8 +826,15 @@ public extension ContainedViewLayoutTransition {
completion(true)
}
case let .animated(duration, curve):
let initialTransform: CATransform3D
if beginWithCurrentState, node.isNodeLoaded {
initialTransform = node.layer.presentation()?.sublayerTransform ?? t
} else {
initialTransform = t
}
node.layer.sublayerTransform = CATransform3DMakeScale(scale.x, scale.y, 1.0)
node.layer.animate(from: NSValue(caTransform3D: t), to: NSValue(caTransform3D: node.layer.sublayerTransform), keyPath: "sublayerTransform", timingFunction: curve.timingFunction, duration: duration, delay: 0.0, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: true, additive: false, completion: {
node.layer.animate(from: NSValue(caTransform3D: initialTransform), to: NSValue(caTransform3D: node.layer.sublayerTransform), keyPath: "sublayerTransform", timingFunction: curve.timingFunction, duration: duration, delay: 0.0, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: true, additive: false, completion: {
result in
if let completion = completion {
completion(result)

View File

@ -11,6 +11,7 @@ import AccountContext
import AppBundle
import SwiftSignalKit
import AnimatedAvatarSetNode
import AudioBlob
private let titleFont = Font.semibold(15.0)
private let subtitleFont = Font.regular(13.0)
@ -61,9 +62,12 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
private let joinButtonTitleNode: ImmediateTextNode
private let joinButtonBackgroundNode: ASImageNode
private var audioLevelView: VoiceBlobView?
private let micButton: HighlightTrackingButtonNode
private let micButtonForegroundNode: VoiceChatMicrophoneNode
private let micButtonBackgroundNode: ASImageNode
private var micButtonBackgroundNodeIsMuted: Bool?
let titleNode: ImmediateTextNode
let textNode: ImmediateTextNode
@ -77,6 +81,9 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
private let membersDisposable = MetaDisposable()
private let isMutedDisposable = MetaDisposable()
private let audioLevelDisposable = MetaDisposable()
private var callState: PresentationGroupCallState?
private var currentData: GroupCallPanelData?
private var validLayout: (CGSize, CGFloat, CGFloat)?
@ -182,16 +189,18 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
private var actionButtonPressGestureStartTime: Double = 0.0
@objc private func micButtonPressGesture(_ gestureRecognizer: UILongPressGestureRecognizer) {
guard let call = self.currentData?.groupCall else {
guard let call = self.currentData?.groupCall, let callState = self.callState else {
return
}
switch gestureRecognizer.state {
case .began:
self.actionButtonPressGestureStartTime = CACurrentMediaTime()
if callState.muteState != nil {
call.setIsMuted(action: .muted(isPushToTalkActive: true))
}
case .ended, .cancelled:
let timestamp = CACurrentMediaTime()
if timestamp - self.actionButtonPressGestureStartTime < 0.2 {
if callState.muteState != nil || timestamp - self.actionButtonPressGestureStartTime < 0.1 {
call.toggleIsMuted()
} else {
call.setIsMuted(action: .muted(isPushToTalkActive: false))
@ -214,9 +223,6 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
self.joinButtonTitleNode.attributedText = NSAttributedString(string: presentationData.strings.VoiceChat_PanelJoin.uppercased(), font: Font.semibold(15.0), textColor: presentationData.theme.chat.inputPanel.actionControlForegroundColor)
self.joinButtonBackgroundNode.image = generateStretchableFilledCircleImage(diameter: 28.0, color: presentationData.theme.chat.inputPanel.actionControlFillColor)
//TODO:localize
self.micButtonBackgroundNode.image = generateStretchableFilledCircleImage(diameter: 36.0, color: UIColor(rgb: 0x30b251))
self.titleNode.attributedText = NSAttributedString(string: presentationData.strings.VoiceChat_Title, font: Font.semibold(15.0), textColor: presentationData.theme.chat.inputPanel.primaryTextColor)
self.textNode.attributedText = NSAttributedString(string: self.textNode.attributedText?.string ?? "", font: Font.regular(13.0), textColor: presentationData.theme.chat.inputPanel.secondaryTextColor)
@ -239,6 +245,8 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
self.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: self.theme.chat.inputPanel.secondaryTextColor)
self.callState = nil
self.membersDisposable.set(nil)
self.isMutedDisposable.set(nil)
@ -272,15 +280,80 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
}
}))
self.isMutedDisposable.set((groupCall.isMuted
|> deliverOnMainQueue).start(next: { [weak self] isMuted in
self.isMutedDisposable.set((groupCall.state
|> deliverOnMainQueue).start(next: { [weak self] callState in
guard let strongSelf = self else {
return
}
strongSelf.micButtonForegroundNode.update(state: VoiceChatMicrophoneNode.State(muted: isMuted, color: UIColor.white), animated: true)
var transition: ContainedViewLayoutTransition = .immediate
if strongSelf.callState != nil {
transition = .animated(duration: 0.3, curve: .spring)
}
strongSelf.callState = callState
if let (size, leftInset, rightInset) = strongSelf.validLayout {
strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: transition)
}
}))
self.audioLevelDisposable.set((groupCall.myAudioLevel
|> deliverOnMainQueue).start(next: { [weak self] value in
guard let strongSelf = self else {
return
}
if strongSelf.audioLevelView == nil {
let blobFrame = CGRect(origin: CGPoint(), size: CGSize(width: 36.0, height: 36.0)).insetBy(dx: -12.0, dy: -12.0)
let audioLevelView = VoiceBlobView(
frame: blobFrame,
maxLevel: 0.3,
smallBlobRange: (0, 0),
mediumBlobRange: (0.7, 0.8),
bigBlobRange: (0.8, 0.9)
)
let maskRect = CGRect(origin: .zero, size: blobFrame.size)
let playbackMaskLayer = CAShapeLayer()
playbackMaskLayer.frame = maskRect
playbackMaskLayer.fillRule = .evenOdd
let maskPath = UIBezierPath()
maskPath.append(UIBezierPath(roundedRect: maskRect.insetBy(dx: 12, dy: 12), cornerRadius: 22))
maskPath.append(UIBezierPath(rect: maskRect))
playbackMaskLayer.path = maskPath.cgPath
audioLevelView.layer.mask = playbackMaskLayer
audioLevelView.setColor(UIColor(rgb: 0x30B251))
strongSelf.audioLevelView = audioLevelView
strongSelf.micButton.view.insertSubview(audioLevelView, at: 0)
}
var value = value
if value <= 0.15 {
value = 0.0
}
let level = min(1.0, max(0.0, CGFloat(value)))
let avatarScale: CGFloat
strongSelf.audioLevelView?.updateLevel(CGFloat(value) * 2.0)
if value > 0.0 {
strongSelf.audioLevelView?.startAnimating()
avatarScale = 1.03 + level * 0.1
} else {
strongSelf.audioLevelView?.stopAnimating(duration: 0.5)
avatarScale = 1.0
}
//let transition: ContainedViewLayoutTransition = .animated(duration: 0.15, curve: .spring)
//transition.updateSublayerTransformScale(node: strongSelf.avatarNode, scale: avatarScale, beginWithCurrentState: true)
}))
}
} else if data.groupCall == nil {
self.audioLevelDisposable.set(nil)
let membersText: String
let membersTextIsActive: Bool
if data.numberOfActiveSpeakers != 0 {
@ -334,6 +407,26 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode {
let animationSize = CGSize(width: 36.0, height: 36.0)
transition.updateFrame(node: self.micButtonForegroundNode, frame: CGRect(origin: CGPoint(x: floor((micButtonFrame.width - animationSize.width) / 2.0), y: floor((micButtonFrame.height - animationSize.height) / 2.0)), size: animationSize))
var isMuted = true
if let _ = self.callState?.muteState {
isMuted = true
} else {
isMuted = false
}
self.micButtonForegroundNode.update(state: VoiceChatMicrophoneNode.State(muted: isMuted, color: UIColor.white), animated: transition.isAnimated)
if isMuted != self.micButtonBackgroundNodeIsMuted {
self.micButtonBackgroundNodeIsMuted = isMuted
let updatedImage = generateStretchableFilledCircleImage(diameter: 36.0, color: isMuted ? UIColor(rgb: 0xb6b6bb) : UIColor(rgb: 0x30b251))
if let updatedImage = updatedImage, let previousImage = self.micButtonBackgroundNode.image?.cgImage, transition.isAnimated {
self.micButtonBackgroundNode.image = updatedImage
self.micButtonBackgroundNode.layer.animate(from: previousImage, to: updatedImage.cgImage!, keyPath: "contents", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.25, delay: 0.0)
} else {
self.micButtonBackgroundNode.image = updatedImage
}
}
let titleSize = self.titleNode.updateLayout(CGSize(width: size.width, height: .greatestFiniteMagnitude))
let textSize = self.textNode.updateLayout(CGSize(width: size.width, height: .greatestFiniteMagnitude))