mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
216 lines
7.3 KiB
Swift
216 lines
7.3 KiB
Swift
import Foundation
|
|
import AsyncDisplayKit
|
|
import SwiftSignalKit
|
|
import Display
|
|
|
|
private let textFont = Font.regular(13.0)
|
|
|
|
public enum MediaPlayerTimeTextNodeMode {
|
|
case normal
|
|
case reversed
|
|
}
|
|
|
|
private struct MediaPlayerTimeTextNodeState: Equatable {
|
|
let hours: Int32?
|
|
let minutes: Int32?
|
|
let seconds: Int32?
|
|
|
|
init() {
|
|
self.hours = nil
|
|
self.minutes = nil
|
|
self.seconds = nil
|
|
}
|
|
|
|
init(hours: Int32, minutes: Int32, seconds: Int32) {
|
|
self.hours = hours
|
|
self.minutes = minutes
|
|
self.seconds = seconds
|
|
}
|
|
|
|
static func ==(lhs: MediaPlayerTimeTextNodeState, rhs: MediaPlayerTimeTextNodeState) -> Bool {
|
|
if lhs.hours != rhs.hours || lhs.minutes != rhs.minutes || lhs.seconds != rhs.seconds {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
private extension MediaPlayerTimeTextNodeState {
|
|
var string: String {
|
|
if let hours = self.hours, let minutes = self.minutes, let seconds = self.seconds {
|
|
if hours != 0 {
|
|
return String(format: "%d:%02d:%02d", hours, minutes, seconds)
|
|
} else {
|
|
return String(format: "%d:%02d", minutes, seconds)
|
|
}
|
|
} else {
|
|
return "-:--"
|
|
}
|
|
}
|
|
}
|
|
|
|
private final class MediaPlayerTimeTextNodeParameters: NSObject {
|
|
let state: MediaPlayerTimeTextNodeState
|
|
let alignment: NSTextAlignment
|
|
let mode: MediaPlayerTimeTextNodeMode
|
|
let textColor: UIColor
|
|
|
|
init(state: MediaPlayerTimeTextNodeState, alignment: NSTextAlignment, mode: MediaPlayerTimeTextNodeMode, textColor: UIColor) {
|
|
self.state = state
|
|
self.alignment = alignment
|
|
self.mode = mode
|
|
self.textColor = textColor
|
|
|
|
super.init()
|
|
}
|
|
}
|
|
|
|
public final class MediaPlayerTimeTextNode: ASDisplayNode {
|
|
public var alignment: NSTextAlignment = .left
|
|
public var mode: MediaPlayerTimeTextNodeMode = .normal
|
|
|
|
public var keepPreviousValueOnEmptyState = false
|
|
|
|
public var textColor: UIColor {
|
|
didSet {
|
|
self.updateTimestamp()
|
|
}
|
|
}
|
|
public var defaultDuration: Double? {
|
|
didSet {
|
|
self.updateTimestamp()
|
|
}
|
|
}
|
|
|
|
private var updateTimer: SwiftSignalKit.Timer?
|
|
|
|
private var statusValue: MediaPlayerStatus? {
|
|
didSet {
|
|
if self.statusValue != oldValue {
|
|
if let statusValue = statusValue, case .playing = statusValue.status {
|
|
self.ensureHasTimer()
|
|
} else {
|
|
self.stopTimer()
|
|
}
|
|
self.updateTimestamp()
|
|
}
|
|
}
|
|
}
|
|
|
|
private var state = MediaPlayerTimeTextNodeState() {
|
|
didSet {
|
|
if self.state != oldValue {
|
|
self.setNeedsDisplay()
|
|
}
|
|
}
|
|
}
|
|
|
|
private var statusDisposable: Disposable?
|
|
private var statusValuePromise = Promise<MediaPlayerStatus>()
|
|
|
|
public var status: Signal<MediaPlayerStatus, NoError>? {
|
|
didSet {
|
|
if let status = self.status {
|
|
self.statusValuePromise.set(status)
|
|
} else {
|
|
self.statusValuePromise.set(.never())
|
|
}
|
|
}
|
|
}
|
|
|
|
public init(textColor: UIColor) {
|
|
self.textColor = textColor
|
|
|
|
super.init()
|
|
|
|
self.isOpaque = false
|
|
|
|
self.statusDisposable = (self.statusValuePromise.get()
|
|
|> deliverOnMainQueue).start(next: { [weak self] status in
|
|
if let strongSelf = self {
|
|
strongSelf.statusValue = status
|
|
}
|
|
})
|
|
}
|
|
|
|
deinit {
|
|
self.statusDisposable?.dispose()
|
|
self.updateTimer?.invalidate()
|
|
}
|
|
|
|
private func ensureHasTimer() {
|
|
if self.updateTimer == nil {
|
|
let timer = SwiftSignalKit.Timer(timeout: 0.5, repeat: true, completion: { [weak self] in
|
|
self?.updateTimestamp()
|
|
}, queue: Queue.mainQueue())
|
|
self.updateTimer = timer
|
|
timer.start()
|
|
}
|
|
}
|
|
|
|
private func stopTimer() {
|
|
self.updateTimer?.invalidate()
|
|
self.updateTimer = nil
|
|
}
|
|
|
|
func updateTimestamp() {
|
|
if ((self.statusValue?.duration ?? 0.0) < 0.1) && self.state.seconds != nil && self.keepPreviousValueOnEmptyState {
|
|
return
|
|
}
|
|
|
|
if let statusValue = self.statusValue, Double(0.0).isLess(than: statusValue.duration) {
|
|
let timestampSeconds: Double
|
|
if !statusValue.generationTimestamp.isZero {
|
|
timestampSeconds = statusValue.timestamp + (CACurrentMediaTime() - statusValue.generationTimestamp)
|
|
} else {
|
|
timestampSeconds = statusValue.timestamp
|
|
}
|
|
switch self.mode {
|
|
case .normal:
|
|
let timestamp = Int32(timestampSeconds)
|
|
self.state = MediaPlayerTimeTextNodeState(hours: timestamp / (60 * 60), minutes: timestamp % (60 * 60) / 60, seconds: timestamp % 60)
|
|
case .reversed:
|
|
let timestamp = abs(Int32(timestampSeconds - statusValue.duration))
|
|
self.state = MediaPlayerTimeTextNodeState(hours: timestamp / (60 * 60), minutes: timestamp % (60 * 60) / 60, seconds: timestamp % 60)
|
|
}
|
|
} else if let defaultDuration = self.defaultDuration {
|
|
let timestamp = Int32(defaultDuration)
|
|
self.state = MediaPlayerTimeTextNodeState(hours: timestamp / (60 * 60), minutes: timestamp % (60 * 60) / 60, seconds: timestamp % 60)
|
|
} else {
|
|
self.state = MediaPlayerTimeTextNodeState()
|
|
}
|
|
}
|
|
|
|
private let digitsSet = CharacterSet(charactersIn: "0123456789")
|
|
private func widthForString(_ string: String) -> CGFloat {
|
|
let convertedString = string.components(separatedBy: digitsSet).joined(separator: "8")
|
|
let text = NSAttributedString(string: convertedString, font: textFont, textColor: .black)
|
|
let size = text.boundingRect(with: CGSize(width: 200.0, height: 100.0), options: NSStringDrawingOptions.usesLineFragmentOrigin, context: nil).size
|
|
return size.width
|
|
}
|
|
|
|
override public func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
|
|
return MediaPlayerTimeTextNodeParameters(state: self.state, alignment: self.alignment, mode: self.mode, textColor: self.textColor)
|
|
}
|
|
|
|
@objc override public class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {
|
|
let context = UIGraphicsGetCurrentContext()!
|
|
|
|
if !isRasterizing {
|
|
context.setBlendMode(.copy)
|
|
context.setFillColor(UIColor.clear.cgColor)
|
|
context.fill(bounds)
|
|
}
|
|
|
|
if let parameters = parameters as? MediaPlayerTimeTextNodeParameters {
|
|
let string = NSAttributedString(string: parameters.state.string, font: textFont, textColor: parameters.textColor)
|
|
let size = string.boundingRect(with: CGSize(width: 200.0, height: 100.0), options: NSStringDrawingOptions.usesLineFragmentOrigin, context: nil).size
|
|
if parameters.alignment == .left {
|
|
string.draw(at: CGPoint())
|
|
} else {
|
|
string.draw(at: CGPoint(x: bounds.size.width - size.width, y: 0.0))
|
|
}
|
|
}
|
|
}
|
|
}
|