600 lines
28 KiB
Swift
600 lines
28 KiB
Swift
//
|
|
// HostCardView.m -> HostCardView.swift
|
|
// VoidLink
|
|
//
|
|
// Created by True砖家 on 2025.5.18
|
|
// Copyright © 2025 True砖家 @ Bilibili. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
|
|
@objc
|
|
protocol HostCardActionDelegate: NSObjectProtocol {
|
|
@objc(appButtonTappedForHost:)
|
|
func appButtonTapped(for host: TemporaryHost)
|
|
@objc(launchButtonTappedForHost:)
|
|
func launchButtonTapped(for host: TemporaryHost)
|
|
@objc(wakeupButtonTappedForHost:)
|
|
func wakeupButtonTapped(for host: TemporaryHost)
|
|
@objc(pairButtonTappedForHost:)
|
|
func pairButtonTapped(for host: TemporaryHost)
|
|
@objc(hostCardLongPressed:view:)
|
|
func hostCardLongPressed(_ host: TemporaryHost, view: UIView)
|
|
@objc(isStreaming)
|
|
func isStreaming() -> Bool
|
|
}
|
|
|
|
@objcMembers
|
|
class HostCardView: UIView {
|
|
var sizeFactor: CGFloat = 1
|
|
private(set) var size: CGSize = .zero
|
|
weak var delegate: HostCardActionDelegate?
|
|
|
|
private var iconBackgroundView: UIView!
|
|
private var hostIconView: UIImageView!
|
|
private var hostNameLabel: UILabel!
|
|
private var statusIcon: UIImageView!
|
|
private var statusLabel: UILabel!
|
|
private var appButton: UIButton!
|
|
private var launchButton: UIButton!
|
|
private var pairButton: UIButton!
|
|
private var wakeupButton: UIButton!
|
|
private var transparentButton: UIButton!
|
|
private var widthConstraint: NSLayoutConstraint!
|
|
private var heightConstraint: NSLayoutConstraint!
|
|
private var separatorLine: UIView!
|
|
private var cardContentpadding: CGFloat = 0
|
|
|
|
private var host: TemporaryHost?
|
|
private var hostSpinner: UIActivityIndicatorView!
|
|
private var lockIconView: UIImageView!
|
|
private var computerIconMonitorCenterYOffset: CGFloat = 0
|
|
private var buttonHeight: CGFloat = 0
|
|
private var buttonLabelFontSize: CGFloat = 0
|
|
private var iconAndButtonSpacing: CGFloat = 0
|
|
private var defaultBlue: UIColor = ThemeManager.appPrimaryColor
|
|
private var defaultGreen: UIColor = UIColor(red: 52 / 255, green: 199 / 255, blue: 89 / 255, alpha: 1)
|
|
private var backgroundLayer: CAGradientLayer!
|
|
private var longPressFired = false
|
|
|
|
private static let refreshCycle: TimeInterval = 2
|
|
private let stateUnknown = State(rawValue: 0)!
|
|
private let stateOffline = State(rawValue: 1)!
|
|
private let stateOnline = State(rawValue: 2)!
|
|
private let pairStatePaired = PairState(rawValue: 2)!
|
|
|
|
override init(frame: CGRect) {
|
|
super.init(frame: frame)
|
|
commonInit()
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
super.init(coder: coder)
|
|
setupUI()
|
|
}
|
|
|
|
@objc(initWithHost:)
|
|
convenience init(host: TemporaryHost) {
|
|
self.init(host: host, andSizeFactor: 1)
|
|
}
|
|
|
|
@objc(initWithHost:andSizeFactor:)
|
|
init(host: TemporaryHost, andSizeFactor sizeFactor: CGFloat) {
|
|
self.sizeFactor = sizeFactor
|
|
self.host = host
|
|
super.init(frame: .zero)
|
|
commonInit()
|
|
}
|
|
|
|
private func commonInit() {
|
|
buttonLabelFontSize = 15 * sizeFactor
|
|
longPressFired = false
|
|
computerIconMonitorCenterYOffset = isIPhone() ? -2.75 * sizeFactor : -3.2 * sizeFactor
|
|
iconAndButtonSpacing = 37 * sizeFactor
|
|
buttonHeight = 39 * sizeFactor
|
|
defaultBlue = ThemeManager.appPrimaryColor
|
|
defaultGreen = UIColor(red: 52 / 255, green: 199 / 255, blue: 89 / 255, alpha: 1)
|
|
|
|
let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(hostCardLongPressed(_:)))
|
|
addGestureRecognizer(longPressRecognizer)
|
|
|
|
createBackgroundLayer()
|
|
setupUI()
|
|
}
|
|
|
|
private func isIPhone() -> Bool {
|
|
UIDevice.current.userInterfaceIdiom == .phone
|
|
}
|
|
|
|
@objc func appButtonTapped() {
|
|
guard host?.pairState == pairStatePaired, host?.state == .online, self.parentViewController?.hasNoPresentedVC == true else { return }
|
|
if longPressFired {
|
|
longPressFired = false
|
|
return
|
|
}
|
|
guard let host else { return }
|
|
if let delegate {
|
|
delegate.appButtonTapped(for: host)
|
|
} else {
|
|
NSLog("Delegate not set or does not respond to appButtonTappedForHost:")
|
|
}
|
|
}
|
|
|
|
@objc func launchButtonTapped() {
|
|
guard host?.pairState == pairStatePaired, host?.state == .online, self.parentViewController?.hasNoPresentedVC == true else { return }
|
|
if longPressFired {
|
|
longPressFired = false
|
|
return
|
|
}
|
|
guard let host else { return }
|
|
if let delegate {
|
|
delegate.launchButtonTapped(for: host)
|
|
} else {
|
|
NSLog("Delegate not set or does not respond to launchButtonTappedForHost:")
|
|
}
|
|
}
|
|
|
|
@objc func wakeupButtonTapped() {
|
|
guard host?.state != .online, self.parentViewController?.hasNoPresentedVC == true else { return }
|
|
if longPressFired {
|
|
longPressFired = false
|
|
return
|
|
}
|
|
guard let host else { return }
|
|
if let delegate {
|
|
delegate.wakeupButtonTapped(for: host)
|
|
} else {
|
|
NSLog("Delegate not set or does not respond to launchButtonTappedForHost:")
|
|
}
|
|
}
|
|
|
|
@objc func pairButtonTapped() {
|
|
guard !pairButton.isHidden, self.parentViewController?.hasNoPresentedVC == true else { return }
|
|
if longPressFired {
|
|
longPressFired = false
|
|
return
|
|
}
|
|
guard let host else { return }
|
|
if let delegate {
|
|
delegate.pairButtonTapped(for: host)
|
|
} else {
|
|
NSLog("Delegate not set or does not respond to pairButtonTappedForHost:")
|
|
}
|
|
}
|
|
|
|
@objc func hostCardLongPressed(_ gesture: UILongPressGestureRecognizer) {
|
|
guard gesture.state == .began, let host else { return }
|
|
longPressFired = true
|
|
if let delegate {
|
|
delegate.hostCardLongPressed(host, view: self)
|
|
} else {
|
|
NSLog("Delegate not set or does not respond")
|
|
}
|
|
}
|
|
|
|
@objc(resizeBySizeFactor:)
|
|
func resizeBySizeFactor(_ factor: CGFloat) {
|
|
subviews.forEach { $0.removeFromSuperview() }
|
|
sizeFactor = factor
|
|
setupUI()
|
|
}
|
|
|
|
private func setupUI() {
|
|
isUserInteractionEnabled = true
|
|
backgroundColor = ThemeManager.widgetBackgroundColor
|
|
cardContentpadding = 13 * sizeFactor
|
|
clipsToBounds = true
|
|
translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
let appButtonWidth = 100 * sizeFactor
|
|
let launchButtonWidth = 120 * sizeFactor
|
|
let cardWidth = cardContentpadding * 2 + appButtonWidth + 15 * sizeFactor + launchButtonWidth
|
|
layer.cornerRadius = 2 * CGFloat(UInt16(cardWidth * 0.0603 / 2))
|
|
if #available(iOS 13.0, *) {
|
|
layer.cornerCurve = .continuous
|
|
}
|
|
|
|
heightConstraint = heightAnchor.constraint(equalToConstant: 300)
|
|
heightConstraint.isActive = true
|
|
widthConstraint = widthAnchor.constraint(equalToConstant: 300)
|
|
widthConstraint.isActive = true
|
|
|
|
iconBackgroundView = UIView(frame: CGRect(x: cardContentpadding, y: cardContentpadding, width: 80 * sizeFactor, height: 80 * sizeFactor))
|
|
iconBackgroundView.backgroundColor = defaultBlue
|
|
iconBackgroundView.layer.cornerRadius = 2 * CGFloat(UInt16(20 * sizeFactor / 2))
|
|
if #available(iOS 13.0, *) {
|
|
iconBackgroundView.layer.cornerCurve = .continuous
|
|
}
|
|
addSubview(iconBackgroundView)
|
|
|
|
hostIconView = UIImageView()
|
|
hostIconView.translatesAutoresizingMaskIntoConstraints = false
|
|
hostIconView.contentMode = .scaleAspectFit
|
|
if #available(iOS 13.0, *) {
|
|
hostIconView.image = UIImage(named: "display")?.withRenderingMode(.alwaysTemplate)
|
|
} else {
|
|
hostIconView.image = UIImage(named: "Computer")
|
|
NSLayoutConstraint.activate([
|
|
hostIconView.heightAnchor.constraint(equalToConstant: 57 * sizeFactor),
|
|
hostIconView.widthAnchor.constraint(equalToConstant: 57 * sizeFactor),
|
|
])
|
|
}
|
|
hostIconView.tintColor = .white
|
|
iconBackgroundView.addSubview(hostIconView)
|
|
NSLayoutConstraint.activate([
|
|
hostIconView.centerXAnchor.constraint(equalTo: iconBackgroundView.centerXAnchor),
|
|
hostIconView.centerYAnchor.constraint(equalTo: iconBackgroundView.centerYAnchor),
|
|
hostIconView.heightAnchor.constraint(equalToConstant: 53.9 * sizeFactor),
|
|
hostIconView.widthAnchor.constraint(equalToConstant: 53.9 * sizeFactor),
|
|
])
|
|
|
|
let oldIOS: Bool
|
|
if #available(iOS 13.0, *) {
|
|
oldIOS = false
|
|
} else {
|
|
oldIOS = true
|
|
}
|
|
|
|
hostSpinner = UIActivityIndicatorView(style: .white)
|
|
hostSpinner.isUserInteractionEnabled = false
|
|
hostSpinner.translatesAutoresizingMaskIntoConstraints = false
|
|
hostSpinner.hidesWhenStopped = true
|
|
iconBackgroundView.addSubview(hostSpinner)
|
|
NSLayoutConstraint.activate([
|
|
hostSpinner.centerXAnchor.constraint(equalTo: iconBackgroundView.centerXAnchor),
|
|
hostSpinner.centerYAnchor.constraint(equalTo: iconBackgroundView.centerYAnchor, constant: oldIOS ? -5.5 * sizeFactor : computerIconMonitorCenterYOffset),
|
|
])
|
|
hostSpinner.transform = CGAffineTransform(scaleX: sizeFactor, y: sizeFactor)
|
|
hostSpinner.stopAnimating()
|
|
|
|
lockIconView = UIImageView()
|
|
lockIconView.translatesAutoresizingMaskIntoConstraints = false
|
|
lockIconView.contentMode = .scaleAspectFit
|
|
if #available(iOS 13.0, *) {
|
|
lockIconView.image = UIImage(named: "lock.fill")?.withRenderingMode(.alwaysTemplate)
|
|
} else {
|
|
lockIconView.image = UIImage(named: "LockedOverlayIcon")
|
|
computerIconMonitorCenterYOffset = -5 * sizeFactor
|
|
}
|
|
lockIconView.tintColor = .white
|
|
iconBackgroundView.addSubview(lockIconView)
|
|
NSLayoutConstraint.activate([
|
|
lockIconView.centerXAnchor.constraint(equalTo: iconBackgroundView.centerXAnchor),
|
|
lockIconView.centerYAnchor.constraint(equalTo: iconBackgroundView.centerYAnchor, constant: computerIconMonitorCenterYOffset),
|
|
lockIconView.widthAnchor.constraint(equalToConstant: 17.05 * sizeFactor),
|
|
lockIconView.heightAnchor.constraint(equalToConstant: 17.05 * sizeFactor),
|
|
])
|
|
lockIconView.isHidden = true
|
|
|
|
let hostNameLabelCoordX = cardContentpadding + iconBackgroundView.frame.size.width + 16 * sizeFactor
|
|
hostNameLabel = UILabel(frame: CGRect(x: hostNameLabelCoordX, y: cardContentpadding + iconBackgroundView.frame.size.height * 0.02, width: cardWidth - hostNameLabelCoordX - cardContentpadding, height: 30 * sizeFactor))
|
|
hostNameLabel.numberOfLines = 1
|
|
hostNameLabel.adjustsFontSizeToFitWidth = true
|
|
hostNameLabel.minimumScaleFactor = 0.8
|
|
hostNameLabel.lineBreakMode = .byTruncatingTail
|
|
hostNameLabel.text = "RazerBlade 16"
|
|
hostNameLabel.textColor = .white
|
|
hostNameLabel.font = .boldSystemFont(ofSize: 18 * sizeFactor)
|
|
addSubview(hostNameLabel)
|
|
|
|
statusLabel = UILabel(frame: CGRect(x: 205, y: 68, width: 100, height: 24))
|
|
statusLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
statusLabel.text = LocalizationHelper.localizedString(forKey: "Online")
|
|
statusLabel.font = .systemFont(ofSize: 14 * sizeFactor, weight: .medium)
|
|
statusLabel.textColor = defaultGreen
|
|
|
|
statusIcon = UIImageView(frame: CGRect(x: 0, y: 0, width: 16 * sizeFactor, height: 16 * sizeFactor))
|
|
statusIcon.contentMode = .scaleAspectFit
|
|
statusIcon.translatesAutoresizingMaskIntoConstraints = false
|
|
statusIcon.image = UIImage(named: "wifi_green")?.withRenderingMode(.alwaysOriginal)
|
|
addSubview(statusIcon)
|
|
addSubview(statusLabel)
|
|
NSLayoutConstraint.activate([
|
|
statusLabel.leadingAnchor.constraint(equalTo: statusIcon.trailingAnchor, constant: 3 * sizeFactor),
|
|
statusLabel.topAnchor.constraint(equalTo: hostNameLabel.bottomAnchor),
|
|
statusIcon.centerYAnchor.constraint(equalTo: statusLabel.centerYAnchor),
|
|
statusIcon.leadingAnchor.constraint(equalTo: hostNameLabel.leadingAnchor),
|
|
statusIcon.widthAnchor.constraint(equalToConstant: 16 * sizeFactor),
|
|
])
|
|
|
|
appButton = UIButton(type: .system)
|
|
appButton.translatesAutoresizingMaskIntoConstraints = false
|
|
appButton.frame = CGRect(x: 20, y: 200, width: 150, height: buttonHeight)
|
|
appButton.setTitle(LocalizationHelper.localizedString(forKey: "Applications"), for: .normal)
|
|
appButton.setTitleColor(ThemeManager.textColorGray, for: .normal)
|
|
appButton.titleLabel?.font = .systemFont(ofSize: buttonLabelFontSize)
|
|
appButton.addTarget(self, action: #selector(appButtonTapped), for: .primaryActionTriggered)
|
|
addSubview(appButton)
|
|
NSLayoutConstraint.activate([
|
|
appButton.leadingAnchor.constraint(equalTo: iconBackgroundView.leadingAnchor),
|
|
appButton.topAnchor.constraint(equalTo: iconBackgroundView.bottomAnchor, constant: iconAndButtonSpacing),
|
|
appButton.widthAnchor.constraint(equalToConstant: appButtonWidth),
|
|
appButton.heightAnchor.constraint(equalToConstant: buttonHeight),
|
|
])
|
|
|
|
launchButton = UIButton(type: .system)
|
|
launchButton.translatesAutoresizingMaskIntoConstraints = false
|
|
launchButton.frame = CGRect(x: 0, y: 0, width: 150, height: 50)
|
|
launchButton.backgroundColor = defaultBlue
|
|
launchButton.layer.cornerRadius = 2 * CGFloat(UInt16(cardWidth * 0.0377 / 2))
|
|
if #available(iOS 13.0, *) {
|
|
launchButton.layer.cornerCurve = .continuous
|
|
}
|
|
launchButton.setTitle(LocalizationHelper.localizedString(forKey: " Launch"), for: .normal)
|
|
launchButton.setTitleColor(.white, for: .normal)
|
|
launchButton.titleLabel?.font = .boldSystemFont(ofSize: buttonLabelFontSize)
|
|
launchButton.tintColor = .white
|
|
launchButton.addTarget(self, action: #selector(launchButtonTapped), for: .primaryActionTriggered)
|
|
addSubview(launchButton)
|
|
if #available(iOS 13.0, *) {
|
|
let config = UIImage.SymbolConfiguration(pointSize: buttonHeight * 0.263)
|
|
launchButton.setImage(UIImage(systemName: "play.fill", withConfiguration: config), for: .normal)
|
|
}
|
|
NSLayoutConstraint.activate([
|
|
launchButton.leadingAnchor.constraint(equalTo: appButton.trailingAnchor, constant: 15 * sizeFactor),
|
|
launchButton.centerYAnchor.constraint(equalTo: appButton.centerYAnchor),
|
|
launchButton.widthAnchor.constraint(equalToConstant: launchButtonWidth),
|
|
launchButton.heightAnchor.constraint(equalToConstant: buttonHeight),
|
|
])
|
|
|
|
pairButton = UIButton(type: .system)
|
|
pairButton.translatesAutoresizingMaskIntoConstraints = false
|
|
pairButton.frame = CGRect(x: 0, y: 0, width: 150, height: 50)
|
|
pairButton.backgroundColor = ThemeManager.textTintColorWithAlpha
|
|
pairButton.layer.cornerRadius = 2 * CGFloat(UInt16(cardWidth * 0.0377 / 2))
|
|
if #available(iOS 13.0, *) {
|
|
pairButton.layer.cornerCurve = .continuous
|
|
}
|
|
pairButton.setTitle(LocalizationHelper.localizedString(forKey: " Pair with PIN"), for: .normal)
|
|
pairButton.setTitleColor(defaultBlue, for: .normal)
|
|
pairButton.titleLabel?.font = .boldSystemFont(ofSize: buttonLabelFontSize)
|
|
if #available(iOS 13.0, *) {
|
|
let config = UIImage.SymbolConfiguration(pointSize: buttonHeight / 3.4, weight: .bold)
|
|
let templateImage = UIImage(systemName: "lock.open.fill", withConfiguration: config)
|
|
let coloredImage = templateImage?.withTintColor(defaultBlue, renderingMode: .alwaysOriginal)
|
|
pairButton.setImage(coloredImage, for: .normal)
|
|
}
|
|
pairButton.addTarget(self, action: #selector(pairButtonTapped), for: .primaryActionTriggered)
|
|
addSubview(pairButton)
|
|
NSLayoutConstraint.activate([
|
|
pairButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: cardContentpadding),
|
|
pairButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -cardContentpadding),
|
|
pairButton.topAnchor.constraint(equalTo: iconBackgroundView.bottomAnchor, constant: iconAndButtonSpacing),
|
|
pairButton.heightAnchor.constraint(equalToConstant: buttonHeight),
|
|
])
|
|
|
|
wakeupButton = UIButton(type: .system)
|
|
wakeupButton.translatesAutoresizingMaskIntoConstraints = false
|
|
wakeupButton.frame = CGRect(x: 0, y: 0, width: 150, height: 50)
|
|
wakeupButton.backgroundColor = ThemeManager.textTintColorWithAlpha
|
|
wakeupButton.layer.cornerRadius = 2 * CGFloat(UInt16(cardWidth * 0.0377 / 2))
|
|
if #available(iOS 13.0, *) {
|
|
wakeupButton.layer.cornerCurve = .continuous
|
|
}
|
|
wakeupButton.setTitle(LocalizationHelper.localizedString(forKey: " Wake-on-LAN"), for: .normal)
|
|
wakeupButton.setTitleColor(defaultBlue, for: .normal)
|
|
wakeupButton.titleLabel?.font = .boldSystemFont(ofSize: buttonLabelFontSize)
|
|
wakeupButton.addTarget(self, action: #selector(wakeupButtonTapped), for: .primaryActionTriggered)
|
|
addSubview(wakeupButton)
|
|
NSLayoutConstraint.activate([
|
|
wakeupButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: cardContentpadding),
|
|
wakeupButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -cardContentpadding),
|
|
wakeupButton.topAnchor.constraint(equalTo: iconBackgroundView.bottomAnchor, constant: iconAndButtonSpacing),
|
|
wakeupButton.heightAnchor.constraint(equalToConstant: buttonHeight),
|
|
])
|
|
|
|
separatorLine = UIView()
|
|
separatorLine.backgroundColor = UIColor(white: 0.3, alpha: 5)
|
|
separatorLine.translatesAutoresizingMaskIntoConstraints = false
|
|
addSubview(separatorLine)
|
|
NSLayoutConstraint.activate([
|
|
separatorLine.leadingAnchor.constraint(equalTo: leadingAnchor, constant: cardContentpadding),
|
|
separatorLine.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -cardContentpadding),
|
|
separatorLine.centerYAnchor.constraint(equalTo: iconBackgroundView.bottomAnchor, constant: iconAndButtonSpacing / 2),
|
|
separatorLine.heightAnchor.constraint(equalToConstant: 1 / UIScreen.main.scale),
|
|
])
|
|
|
|
transparentButton = UIButton(type: .custom)
|
|
transparentButton.translatesAutoresizingMaskIntoConstraints = false
|
|
transparentButton.backgroundColor = .clear
|
|
transparentButton.setTitle("", for: .normal)
|
|
transparentButton.setImage(nil, for: .normal)
|
|
transparentButton.addTarget(self, action: #selector(appButtonTapped), for: .primaryActionTriggered)
|
|
addSubview(transparentButton)
|
|
NSLayoutConstraint.activate([
|
|
transparentButton.leadingAnchor.constraint(equalTo: leadingAnchor),
|
|
transparentButton.trailingAnchor.constraint(equalTo: trailingAnchor),
|
|
transparentButton.topAnchor.constraint(equalTo: topAnchor),
|
|
transparentButton.bottomAnchor.constraint(equalTo: separatorLine.topAnchor),
|
|
])
|
|
|
|
widthConstraint.constant = cardContentpadding * 2 + appButtonWidth + 15 * sizeFactor + launchButtonWidth
|
|
heightConstraint.constant = cardContentpadding * 2 + iconBackgroundView.frame.size.height + iconAndButtonSpacing + buttonHeight + 1
|
|
size = CGSize(width: widthConstraint.constant, height: heightConstraint.constant)
|
|
|
|
updateTheme()
|
|
}
|
|
|
|
private func updateBackgroundLayerTheme() {
|
|
let gradientColorDark = UIColor(red: 0, green: 0.319, blue: 0.64, alpha: 1)
|
|
let gradientColorLight = gradientColorDark.withAlphaComponent(0.52)
|
|
let gradientColor = ThemeManager.userInterfaceStyle() == .dark ? gradientColorDark.cgColor : gradientColorLight.cgColor
|
|
backgroundLayer.colors = [
|
|
UIColor.clear.cgColor,
|
|
UIColor.clear.cgColor,
|
|
UIColor.clear.cgColor,
|
|
gradientColor,
|
|
]
|
|
}
|
|
|
|
private func createBackgroundLayer() {
|
|
backgroundLayer = CAGradientLayer()
|
|
updateBackgroundLayerTheme()
|
|
backgroundLayer.locations = [0, 0.18, 0.5, 1]
|
|
backgroundLayer.startPoint = CGPoint(x: 0.25, y: 0.5)
|
|
backgroundLayer.endPoint = CGPoint(x: 0.75, y: 0.5)
|
|
let transform = CGAffineTransform(a: -1.01, b: -1, c: 1, d: -3.67, tx: 0.5, ty: 2.83)
|
|
backgroundLayer.transform = CATransform3DMakeAffineTransform(transform)
|
|
layer.insertSublayer(backgroundLayer, at: 0)
|
|
}
|
|
|
|
override func layoutSubviews() {
|
|
super.layoutSubviews()
|
|
backgroundLayer.bounds = bounds.insetBy(dx: -0.5 * bounds.size.width, dy: -0.5 * bounds.size.height)
|
|
backgroundLayer.position = CGPoint(x: bounds.midX, y: bounds.midY)
|
|
updateContents(for: host)
|
|
}
|
|
|
|
func tintAdjustmentModeDidChange() {
|
|
tintAdjustmentMode = .normal
|
|
}
|
|
|
|
func updateTheme() {
|
|
backgroundColor = ThemeManager.widgetBackgroundColor
|
|
hostNameLabel?.textColor = ThemeManager.textColor
|
|
appButton?.setTitleColor(ThemeManager.appPrimaryColor, for: .normal)
|
|
separatorLine?.backgroundColor = ThemeManager.hostCardSeparatorColor
|
|
|
|
backgroundLayer?.isHidden = false
|
|
updateBackgroundLayerTheme()
|
|
updateContents(for: host)
|
|
}
|
|
|
|
override func didMoveToSuperview() {
|
|
if superview != nil && host != nil {
|
|
NSLog("start update loop")
|
|
updateLoop()
|
|
}
|
|
}
|
|
|
|
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
|
}
|
|
|
|
private func isDarkTheme() -> Bool {
|
|
ThemeManager.userInterfaceStyle() == .dark
|
|
}
|
|
|
|
@objc func updateLoop() {
|
|
if superview == nil || delegate?.isStreaming() == true {
|
|
return
|
|
}
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now()+0.36) { [weak self] in
|
|
self?.updateContents(for: self?.host)
|
|
}
|
|
|
|
perform(#selector(updateLoop), with: self, afterDelay: Self.refreshCycle)
|
|
}
|
|
|
|
private func updateContents(for host: TemporaryHost?) {
|
|
guard let host else { return }
|
|
|
|
hostNameLabel.text = host.name
|
|
backgroundLayer.isHidden = !(host.state == stateOnline && host.pairState == pairStatePaired)
|
|
|
|
let hostPaired = host.pairState == pairStatePaired
|
|
let hasValidMac = host.mac != nil && host.mac != "00:00:00:00:00:00"
|
|
|
|
switch host.state {
|
|
case stateOnline:
|
|
hostSpinner.stopAnimating()
|
|
statusLabel.textColor = defaultGreen
|
|
statusLabel.text = LocalizationHelper.localizedString(forKey: "Online")
|
|
statusIcon.image = UIImage(named: "wifi_green")?.withRenderingMode(.alwaysOriginal)
|
|
statusIcon.isHidden = false
|
|
appButton.titleLabel?.font = .systemFont(ofSize: buttonLabelFontSize)
|
|
if host.pairState == pairStatePaired {
|
|
hostIconView.tintColor = .white
|
|
iconBackgroundView.backgroundColor = defaultBlue
|
|
lockIconView.isHidden = true
|
|
appButton.setTitle(LocalizationHelper.localizedString(forKey: "Applications"), for: .normal)
|
|
launchButton.setTitle(LocalizationHelper.localizedString(forKey: " Launch"), for: .normal)
|
|
appButton.isEnabled = true
|
|
launchButton.isEnabled = true
|
|
appButton.isHidden = false
|
|
launchButton.isHidden = false
|
|
pairButton.isHidden = true
|
|
wakeupButton.isHidden = true
|
|
appButton.setTitleColor(defaultBlue, for: .normal)
|
|
if #available(iOS 13.0, *) {
|
|
let config = UIImage.SymbolConfiguration(pointSize: buttonHeight * 0.263)
|
|
launchButton.setImage(UIImage(systemName: "play.fill", withConfiguration: config), for: .normal)
|
|
}
|
|
launchButton.backgroundColor = defaultBlue
|
|
launchButton.setTitleColor(.white, for: .normal)
|
|
} else {
|
|
iconBackgroundView.backgroundColor = ThemeManager.appPrimaryColorWithAlpha
|
|
let iconColor = UIColor.white.withAlphaComponent(isDarkTheme() ? 0.63 : 1)
|
|
hostIconView.tintColor = iconColor
|
|
lockIconView.tintColor = iconColor
|
|
lockIconView.isHidden = false
|
|
appButton.isHidden = true
|
|
launchButton.isHidden = true
|
|
pairButton.isHidden = false
|
|
wakeupButton.isHidden = true
|
|
}
|
|
|
|
case stateOffline:
|
|
hostSpinner.stopAnimating()
|
|
iconBackgroundView.backgroundColor = ThemeManager.offlineHostIconBackgroundColor
|
|
statusLabel.textColor = ThemeManager.textColorGray
|
|
statusLabel.text = LocalizationHelper.localizedString(forKey: "Offline")
|
|
statusIcon.tintColor = ThemeManager.textColorGray
|
|
if #available(iOS 13.0, *) {
|
|
statusIcon.image = UIImage(systemName: "exclamationmark.triangle.fill")
|
|
} else {
|
|
statusIcon.isHidden = true
|
|
}
|
|
|
|
hostIconView.tintColor = ThemeManager.lowProfileGray
|
|
lockIconView.tintColor = ThemeManager.lowProfileGray
|
|
lockIconView.isHidden = hostPaired
|
|
appButton.isHidden = true
|
|
launchButton.isHidden = true
|
|
pairButton.isHidden = true
|
|
wakeupButton.isHidden = false
|
|
|
|
if #available(iOS 13.0, *) {
|
|
let config = UIImage.SymbolConfiguration(pointSize: buttonHeight / 3.45, weight: .bold)
|
|
let templateImage = UIImage(systemName: "power", withConfiguration: config)
|
|
let color = hasValidMac ? defaultBlue : ThemeManager.textColorGray
|
|
wakeupButton.setImage(templateImage?.withTintColor(color, renderingMode: .alwaysOriginal), for: .normal)
|
|
}
|
|
|
|
wakeupButton.backgroundColor = hasValidMac ? ThemeManager.textTintColorWithAlpha : ThemeManager.textColorGray.withAlphaComponent(0.2)
|
|
wakeupButton.setTitleColor(hasValidMac ? defaultBlue : ThemeManager.textColorGray, for: .normal)
|
|
|
|
case stateUnknown:
|
|
hostSpinner.color = .white
|
|
hostSpinner.startAnimating()
|
|
iconBackgroundView.backgroundColor = ThemeManager.appPrimaryColorWithAlpha
|
|
statusLabel.textColor = ThemeManager.textColorGray
|
|
statusLabel.text = LocalizationHelper.localizedString(forKey: "Detecting...")
|
|
statusIcon.tintColor = ThemeManager.textColorGray
|
|
if #available(iOS 13.0, *) {
|
|
statusIcon.image = UIImage(systemName: "antenna.radiowaves.left.and.right")
|
|
} else {
|
|
statusIcon.isHidden = true
|
|
}
|
|
hostIconView.tintColor = defaultBlue
|
|
lockIconView.isHidden = true
|
|
appButton.isHidden = true
|
|
launchButton.isHidden = true
|
|
pairButton.isHidden = true
|
|
wakeupButton.isHidden = false
|
|
|
|
if #available(iOS 13.0, *) {
|
|
let config = UIImage.SymbolConfiguration(pointSize: buttonHeight / 3.45, weight: .bold)
|
|
let templateImage = UIImage(systemName: "power", withConfiguration: config)
|
|
let color = hasValidMac ? defaultBlue : ThemeManager.textColorGray
|
|
wakeupButton.setImage(templateImage?.withTintColor(color, renderingMode: .alwaysOriginal), for: .normal)
|
|
}
|
|
wakeupButton.backgroundColor = hasValidMac ? ThemeManager.textTintColorWithAlpha : ThemeManager.textColorGray.withAlphaComponent(0.2)
|
|
wakeupButton.setTitleColor(hasValidMac ? defaultBlue : ThemeManager.textColorGray, for: .normal)
|
|
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
}
|