mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-29 00:50:40 +00:00
Fixed volume indicator color in landscape Added dark outline for volume indicator in landscape to separate from underlying content
303 lines
14 KiB
Swift
303 lines
14 KiB
Swift
import Foundation
|
|
import AsyncDisplayKit
|
|
import SwiftSignalKit
|
|
|
|
private struct MappedStatusBar {
|
|
let style: StatusBarStyle
|
|
let frame: CGRect
|
|
let statusBar: StatusBar?
|
|
}
|
|
|
|
private struct MappedStatusBarSurface {
|
|
let statusBars: [MappedStatusBar]
|
|
let surface: StatusBarSurface
|
|
}
|
|
|
|
private func mapStatusBar(_ statusBar: StatusBar, forceInCall: Bool) -> MappedStatusBar {
|
|
let frame = CGRect(origin: statusBar.view.convert(CGPoint(), to: nil), size: statusBar.frame.size)
|
|
let resolvedStyle: StatusBarStyle
|
|
switch statusBar.statusBarStyle {
|
|
case .Black, .White:
|
|
if forceInCall {
|
|
resolvedStyle = .White
|
|
} else {
|
|
resolvedStyle = statusBar.statusBarStyle
|
|
}
|
|
default:
|
|
resolvedStyle = statusBar.statusBarStyle
|
|
}
|
|
return MappedStatusBar(style: resolvedStyle, frame: frame, statusBar: statusBar)
|
|
}
|
|
|
|
private func mappedSurface(_ surface: StatusBarSurface, forceInCall: Bool) -> MappedStatusBarSurface {
|
|
var statusBars: [MappedStatusBar] = []
|
|
for statusBar in surface.statusBars {
|
|
if statusBar.statusBarStyle != .Ignore {
|
|
statusBars.append(mapStatusBar(statusBar, forceInCall: forceInCall))
|
|
}
|
|
}
|
|
return MappedStatusBarSurface(statusBars: statusBars, surface: surface)
|
|
}
|
|
|
|
private func optimizeMappedSurface(statusBarSize: CGSize, surface: MappedStatusBarSurface, forceInCall: Bool) -> MappedStatusBarSurface {
|
|
if surface.statusBars.count > 1 {
|
|
for i in 1 ..< surface.statusBars.count {
|
|
if (!forceInCall && surface.statusBars[i].style != surface.statusBars[i - 1].style) || abs(surface.statusBars[i].frame.origin.y - surface.statusBars[i - 1].frame.origin.y) > CGFloat.ulpOfOne {
|
|
return surface
|
|
}
|
|
if let lhsStatusBar = surface.statusBars[i - 1].statusBar, let rhsStatusBar = surface.statusBars[i].statusBar , !lhsStatusBar.alpha.isEqual(to: rhsStatusBar.alpha) {
|
|
return surface
|
|
}
|
|
}
|
|
let size = statusBarSize
|
|
return MappedStatusBarSurface(statusBars: [MappedStatusBar(style: forceInCall ? .White : surface.statusBars[0].style, frame: CGRect(origin: CGPoint(x: 0.0, y: surface.statusBars[0].frame.origin.y), size: size), statusBar: nil)], surface: surface.surface)
|
|
} else {
|
|
return surface
|
|
}
|
|
}
|
|
|
|
private func displayHiddenAnimation() -> CAAnimation {
|
|
let animation = CABasicAnimation(keyPath: "transform.translation.y")
|
|
animation.fromValue = NSNumber(value: Float(-40.0))
|
|
animation.toValue = NSNumber(value: Float(-40.0))
|
|
animation.fillMode = kCAFillModeBoth
|
|
animation.duration = 1.0
|
|
animation.speed = 0.0
|
|
animation.isAdditive = true
|
|
animation.isRemovedOnCompletion = false
|
|
|
|
return animation
|
|
}
|
|
|
|
class StatusBarManager {
|
|
private var host: StatusBarHost
|
|
private let volumeControlStatusBar: VolumeControlStatusBar
|
|
private let volumeControlStatusBarNode: VolumeControlStatusBarNode
|
|
|
|
private var surfaces: [StatusBarSurface] = []
|
|
private var validParams: (withSafeInsets: Bool, forceInCallStatusBarText: String?, forceHiddenBySystemWindows: Bool)?
|
|
|
|
var inCallNavigate: (() -> Void)?
|
|
|
|
private var volumeTimer: SwiftSignalKit.Timer?
|
|
|
|
init(host: StatusBarHost, volumeControlStatusBar: VolumeControlStatusBar, volumeControlStatusBarNode: VolumeControlStatusBarNode) {
|
|
self.host = host
|
|
self.volumeControlStatusBar = volumeControlStatusBar
|
|
self.volumeControlStatusBarNode = volumeControlStatusBarNode
|
|
self.volumeControlStatusBarNode.isHidden = true
|
|
|
|
self.volumeControlStatusBar.valueChanged = { [weak self] previous, updated in
|
|
if let strongSelf = self {
|
|
strongSelf.startVolumeTimer()
|
|
strongSelf.volumeControlStatusBarNode.updateValue(from: CGFloat(previous), to: CGFloat(updated))
|
|
}
|
|
}
|
|
}
|
|
|
|
private func startVolumeTimer() {
|
|
self.volumeTimer?.invalidate()
|
|
let timer = SwiftSignalKit.Timer(timeout: 2.0, repeat: false, completion: { [weak self] in
|
|
self?.endVolumeTimer()
|
|
}, queue: Queue.mainQueue())
|
|
self.volumeTimer = timer
|
|
timer.start()
|
|
if self.volumeControlStatusBarNode.isHidden {
|
|
self.volumeControlStatusBarNode.isHidden = false
|
|
self.volumeControlStatusBarNode.alpha = 1.0
|
|
self.volumeControlStatusBarNode.allowsGroupOpacity = true
|
|
self.volumeControlStatusBarNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, delay: 0.18, completion: { [weak self] _ in
|
|
self?.volumeControlStatusBarNode.allowsGroupOpacity = false
|
|
})
|
|
}
|
|
if let (withSafeInsets, forceInCallStatusBarText, forceHiddenBySystemWindows) = self.validParams {
|
|
self.updateSurfaces(self.surfaces, withSafeInsets: withSafeInsets, forceInCallStatusBarText: forceInCallStatusBarText, forceHiddenBySystemWindows: forceHiddenBySystemWindows, animated: false, alphaTransition: .animated(duration: 0.2, curve: .easeInOut))
|
|
}
|
|
}
|
|
|
|
private func endVolumeTimer() {
|
|
self.volumeControlStatusBarNode.alpha = 0.0
|
|
self.volumeControlStatusBarNode.allowsGroupOpacity = true
|
|
self.volumeControlStatusBarNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, completion: { [weak self] completed in
|
|
if let strongSelf = self, completed {
|
|
strongSelf.volumeControlStatusBarNode.isHidden = true
|
|
strongSelf.volumeControlStatusBarNode.allowsGroupOpacity = false
|
|
}
|
|
})
|
|
self.volumeTimer = nil
|
|
if let (withSafeInsets, forceInCallStatusBarText, forceHiddenBySystemWindows) = self.validParams {
|
|
self.updateSurfaces(self.surfaces, withSafeInsets: withSafeInsets, forceInCallStatusBarText: forceInCallStatusBarText, forceHiddenBySystemWindows: forceHiddenBySystemWindows, animated: false, alphaTransition: .animated(duration: 0.2, curve: .easeInOut))
|
|
}
|
|
}
|
|
|
|
func updateState(surfaces: [StatusBarSurface], withSafeInsets: Bool, forceInCallStatusBarText: String?, forceHiddenBySystemWindows: Bool, animated: Bool) {
|
|
let previousSurfaces = self.surfaces
|
|
self.surfaces = surfaces
|
|
self.updateSurfaces(previousSurfaces, withSafeInsets: withSafeInsets, forceInCallStatusBarText: forceInCallStatusBarText, forceHiddenBySystemWindows: forceHiddenBySystemWindows, animated: animated, alphaTransition: .immediate)
|
|
}
|
|
|
|
private func updateSurfaces(_ previousSurfaces: [StatusBarSurface], withSafeInsets: Bool, forceInCallStatusBarText: String?, forceHiddenBySystemWindows: Bool, animated: Bool, alphaTransition: ContainedViewLayoutTransition) {
|
|
let statusBarFrame = self.host.statusBarFrame
|
|
guard let statusBarView = self.host.statusBarView else {
|
|
return
|
|
}
|
|
|
|
self.validParams = (withSafeInsets, forceInCallStatusBarText, forceHiddenBySystemWindows)
|
|
|
|
if self.host.statusBarWindow?.isUserInteractionEnabled != (forceInCallStatusBarText == nil) {
|
|
self.host.statusBarWindow?.isUserInteractionEnabled = (forceInCallStatusBarText == nil)
|
|
}
|
|
|
|
var mappedSurfaces: [MappedStatusBarSurface] = []
|
|
var mapIndex = 0
|
|
var doNotOptimize = false
|
|
for surface in self.surfaces {
|
|
inner: for statusBar in surface.statusBars {
|
|
if statusBar.statusBarStyle == .Hide {
|
|
doNotOptimize = true
|
|
break inner
|
|
}
|
|
}
|
|
|
|
let mapped = mappedSurface(surface, forceInCall: forceInCallStatusBarText != nil)
|
|
|
|
if doNotOptimize {
|
|
mappedSurfaces.append(mapped)
|
|
} else {
|
|
mappedSurfaces.append(optimizeMappedSurface(statusBarSize: statusBarFrame.size, surface: mapped, forceInCall: forceInCallStatusBarText != nil))
|
|
}
|
|
mapIndex += 1
|
|
}
|
|
|
|
var reduceSurfaces = true
|
|
var reduceSurfacesStatusBarStyleAndAlpha: (StatusBarStyle, CGFloat)?
|
|
var reduceIndex = 0
|
|
outer: for surface in mappedSurfaces {
|
|
for mappedStatusBar in surface.statusBars {
|
|
if reduceIndex == 0 && mappedStatusBar.style == .Hide {
|
|
reduceSurfaces = false
|
|
break outer
|
|
}
|
|
if mappedStatusBar.frame.origin.equalTo(CGPoint()) {
|
|
let statusBarAlpha = mappedStatusBar.statusBar?.alpha ?? 1.0
|
|
if let reduceSurfacesStatusBarStyleAndAlpha = reduceSurfacesStatusBarStyleAndAlpha {
|
|
if mappedStatusBar.style != reduceSurfacesStatusBarStyleAndAlpha.0 {
|
|
reduceSurfaces = false
|
|
break outer
|
|
}
|
|
if !statusBarAlpha.isEqual(to: reduceSurfacesStatusBarStyleAndAlpha.1) {
|
|
reduceSurfaces = false
|
|
break outer
|
|
}
|
|
} else {
|
|
reduceSurfacesStatusBarStyleAndAlpha = (mappedStatusBar.style, statusBarAlpha)
|
|
}
|
|
}
|
|
}
|
|
reduceIndex += 1
|
|
}
|
|
|
|
if reduceSurfaces {
|
|
outer: for surface in mappedSurfaces {
|
|
for mappedStatusBar in surface.statusBars {
|
|
if mappedStatusBar.frame.origin.equalTo(CGPoint()) {
|
|
if let statusBar = mappedStatusBar.statusBar , !statusBar.layer.hasPositionOrOpacityAnimations() {
|
|
mappedSurfaces = [MappedStatusBarSurface(statusBars: [mappedStatusBar], surface: surface.surface)]
|
|
break outer
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var visibleStatusBars: [StatusBar] = []
|
|
|
|
var globalStatusBar: (StatusBarStyle, CGFloat, CGFloat)?
|
|
|
|
var coveredIdentity = false
|
|
var statusBarIndex = 0
|
|
for i in 0 ..< mappedSurfaces.count {
|
|
for mappedStatusBar in mappedSurfaces[i].statusBars {
|
|
if let statusBar = mappedStatusBar.statusBar {
|
|
if mappedStatusBar.frame.origin.equalTo(CGPoint()) && !statusBar.layer.hasPositionOrOpacityAnimations() && !statusBar.offsetNode.layer.hasPositionAnimations() {
|
|
if !coveredIdentity {
|
|
if statusBar.statusBarStyle != .Hide {
|
|
if statusBar.offsetNode.frame.origin.equalTo(CGPoint()) {
|
|
coveredIdentity = CGFloat(1.0).isLessThanOrEqualTo(statusBar.alpha)
|
|
}
|
|
if statusBarIndex == 0 && globalStatusBar == nil {
|
|
globalStatusBar = (mappedStatusBar.style, statusBar.alpha, statusBar.offsetNode.frame.origin.y)
|
|
} else {
|
|
visibleStatusBars.append(statusBar)
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
visibleStatusBars.append(statusBar)
|
|
}
|
|
} else {
|
|
if !coveredIdentity {
|
|
coveredIdentity = true
|
|
if statusBarIndex == 0 && globalStatusBar == nil {
|
|
globalStatusBar = (mappedStatusBar.style, 1.0, 0.0)
|
|
}
|
|
}
|
|
}
|
|
statusBarIndex += 1
|
|
}
|
|
}
|
|
|
|
for surface in previousSurfaces {
|
|
for statusBar in surface.statusBars {
|
|
if !visibleStatusBars.contains(where: {$0 === statusBar}) {
|
|
statusBar.updateState(statusBar: nil, withSafeInsets: withSafeInsets, inCallText: forceInCallStatusBarText, animated: animated)
|
|
}
|
|
}
|
|
}
|
|
|
|
for surface in self.surfaces {
|
|
for statusBar in surface.statusBars {
|
|
statusBar.inCallNavigate = self.inCallNavigate
|
|
if !visibleStatusBars.contains(where: {$0 === statusBar}) {
|
|
statusBar.updateState(statusBar: nil, withSafeInsets: withSafeInsets, inCallText: forceInCallStatusBarText, animated: animated)
|
|
}
|
|
}
|
|
}
|
|
|
|
for statusBar in visibleStatusBars {
|
|
statusBar.updateState(statusBar: statusBarView, withSafeInsets: withSafeInsets, inCallText: forceInCallStatusBarText, animated: animated)
|
|
}
|
|
|
|
if self.volumeTimer != nil {
|
|
globalStatusBar?.1 = 0.0
|
|
}
|
|
var isDark = true
|
|
if let globalStatusBar = globalStatusBar {
|
|
isDark = globalStatusBar.0.systemStyle == UIStatusBarStyle.lightContent
|
|
}
|
|
self.volumeControlStatusBarNode.isDark = isDark
|
|
|
|
if let globalStatusBar = globalStatusBar, !forceHiddenBySystemWindows {
|
|
let statusBarStyle: UIStatusBarStyle
|
|
if forceInCallStatusBarText != nil {
|
|
statusBarStyle = .lightContent
|
|
} else {
|
|
statusBarStyle = globalStatusBar.0 == .Black ? .default : .lightContent
|
|
}
|
|
if self.host.statusBarStyle != statusBarStyle {
|
|
self.host.statusBarStyle = statusBarStyle
|
|
}
|
|
if let statusBarWindow = self.host.statusBarWindow {
|
|
alphaTransition.updateAlpha(layer: statusBarView.layer, alpha: globalStatusBar.1)
|
|
var statusBarBounds = statusBarWindow.bounds
|
|
if !statusBarBounds.origin.y.isEqual(to: globalStatusBar.2) {
|
|
statusBarBounds.origin.y = globalStatusBar.2
|
|
statusBarWindow.bounds = statusBarBounds
|
|
}
|
|
}
|
|
} else {
|
|
statusBarView.alpha = 0.0
|
|
}
|
|
}
|
|
}
|