diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift index 0eb3f6033e..529f22e001 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift @@ -323,6 +323,7 @@ private final class CameraScreenComponent: CombinedComponent { } self.volumeButtonsListener = VolumeButtonsListener( + sharedContext: self.context.sharedContext, shouldBeActive: self.volumeButtonsListenerShouldBeActive.get(), upPressed: { [weak self] in if let self { diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift index e3c27064bc..f244081f25 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift @@ -357,7 +357,14 @@ private final class StoryContainerScreenComponent: Component { } final class View: UIView, UIGestureRecognizerDelegate { - private var component: StoryContainerScreenComponent? + private var component: StoryContainerScreenComponent? { + didSet { + if self.component != nil { + self.isComponentReadyPromise.set(true) + } + } + } + private let isComponentReadyPromise = ValuePromise(false, ignoreRepeated: true) private weak var state: EmptyComponentState? private var environment: ViewControllerComponentContainer.Environment? @@ -700,9 +707,13 @@ private final class StoryContainerScreenComponent: Component { self.volumeButtonsListenerShouldBeActiveDisposable = (combineLatest(queue: .mainQueue(), self.contentWantsVolumeButtonMonitoring.get(), self.isMuteSwitchOnPromise.get(), - self.audioModePromise.get() + self.audioModePromise.get(), + self.isComponentReadyPromise.get() ) - |> map { contentWantsVolumeButtonMonitoring, isMuteSwitchOn, audioMode -> Bool in + |> map { contentWantsVolumeButtonMonitoring, isMuteSwitchOn, audioMode, isComponentReady -> Bool in + if !isComponentReady { + return false + } if !contentWantsVolumeButtonMonitoring { return false } @@ -1105,53 +1116,55 @@ private final class StoryContainerScreenComponent: Component { } private func updateVolumeButtonMonitoring() { - if self.volumeButtonsListener == nil { - let buttonAction = { [weak self] in - guard let self else { - return - } - guard let slice = self.stateValue?.slice else { - return - } - var isSilentVideo = false - if case let .file(file) = slice.item.storyItem.media { - for attribute in file.attributes { - if case let .Video(_, _, flags, _) = attribute { - if flags.contains(.isSilent) { - isSilentVideo = true - } + guard self.volumeButtonsListener == nil, let component = self.component else { + return + } + let buttonAction = { [weak self] in + guard let self else { + return + } + guard let slice = self.stateValue?.slice else { + return + } + var isSilentVideo = false + if case let .file(file) = slice.item.storyItem.media { + for attribute in file.attributes { + if case let .Video(_, _, flags, _) = attribute { + if flags.contains(.isSilent) { + isSilentVideo = true } } } + } + + if isSilentVideo { + if let slice = self.stateValue?.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let currentItemView = itemSetView.view.view as? StoryItemSetContainerComponent.View { + currentItemView.displayMutedVideoTooltip() + } + } else { + switch self.audioMode { + case .off, .ambient: + break + case .on: + return + } + self.audioMode = .on + + for (_, itemSetView) in self.visibleItemSetViews { + if let componentView = itemSetView.view.view as? StoryItemSetContainerComponent.View { + componentView.leaveAmbientMode() + } + } - if isSilentVideo { - if let slice = self.stateValue?.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let currentItemView = itemSetView.view.view as? StoryItemSetContainerComponent.View { - currentItemView.displayMutedVideoTooltip() - } - } else { - switch self.audioMode { - case .off, .ambient: - break - case .on: - return - } - self.audioMode = .on - - for (_, itemSetView) in self.visibleItemSetViews { - if let componentView = itemSetView.view.view as? StoryItemSetContainerComponent.View { - componentView.leaveAmbientMode() - } - } - - self.state?.updated(transition: .immediate) - } + self.state?.updated(transition: .immediate) } - self.volumeButtonsListener = VolumeButtonsListener( - shouldBeActive: self.volumeButtonsListenerShouldBeActive.get(), - upPressed: buttonAction, - downPressed: buttonAction - ) } + self.volumeButtonsListener = VolumeButtonsListener( + sharedContext: component.context.sharedContext, + shouldBeActive: self.volumeButtonsListenerShouldBeActive.get(), + upPressed: buttonAction, + downPressed: buttonAction + ) } private var previousBackNavigationTime: Double? diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 587e39857d..88e54c6cb9 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -11790,6 +11790,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } self.volumeButtonsListener = VolumeButtonsListener( + sharedContext: self.context.sharedContext, shouldBeActive: shouldBeActive, upPressed: buttonAction, downPressed: buttonAction diff --git a/submodules/Utils/VolumeButtons/BUILD b/submodules/Utils/VolumeButtons/BUILD index faffc51c8d..5abc27fbf2 100644 --- a/submodules/Utils/VolumeButtons/BUILD +++ b/submodules/Utils/VolumeButtons/BUILD @@ -12,6 +12,7 @@ swift_library( deps = [ "//submodules/SSignalKit/SwiftSignalKit", "//submodules/LegacyComponents", + "//submodules/AccountContext", ], visibility = [ "//visibility:public", diff --git a/submodules/Utils/VolumeButtons/Sources/VolumeButtons.swift b/submodules/Utils/VolumeButtons/Sources/VolumeButtons.swift index 846d9d0de4..0a9752b568 100644 --- a/submodules/Utils/VolumeButtons/Sources/VolumeButtons.swift +++ b/submodules/Utils/VolumeButtons/Sources/VolumeButtons.swift @@ -1,10 +1,81 @@ import Foundation import UIKit +import AVKit import SwiftSignalKit import MediaPlayer - import LegacyComponents +import AccountContext +private protocol VolumeButtonHandlerImpl { +} + +private final class LegacyHandlerImpl: VolumeButtonHandlerImpl { + private let handler: PGCameraVolumeButtonHandler + + init( + context: SharedAccountContext, + performAction: @escaping (VolumeButtonsListener.Action) -> Void + ) { + self.handler = PGCameraVolumeButtonHandler(upButtonPressedBlock: { + performAction(.up) + }, upButtonReleasedBlock: { + performAction(.upRelease) + }, downButtonPressedBlock: { + performAction(.down) + }, downButtonReleasedBlock: { + performAction(.downRelease) + }) + self.handler.enabled = true + } + + deinit { + self.handler.enabled = false + } +} + +@available(iOS 17.2, *) +private final class AVCaptureEventHandlerImpl: VolumeButtonHandlerImpl { + private let interaction: AVCaptureEventInteraction + + init( + context: SharedAccountContext, + performAction: @escaping (VolumeButtonsListener.Action) -> Void + ) { + self.interaction = AVCaptureEventInteraction( + primary: { event in + switch event.phase { + case .began: + performAction(.down) + case .ended: + performAction(.downRelease) + case .cancelled: + performAction(.downRelease) + @unknown default: + break + } + }, + secondary: { event in + switch event.phase { + case .began: + performAction(.up) + case .ended: + performAction(.upRelease) + case .cancelled: + performAction(.upRelease) + @unknown default: + break + } + } + ) + self.interaction.isEnabled = true + context.mainWindow?.viewController?.view.addInteraction(self.interaction) +// hostView.eventView.addInteraction(self.interaction) + } + + deinit { + self.interaction.isEnabled = false + } +} public class VolumeButtonsListener { private final class ListenerReference { @@ -17,38 +88,27 @@ public class VolumeButtonsListener { } } - private enum Action { + fileprivate enum Action { case up case upRelease case down case downRelease } - + private final class SharedContext: NSObject { - private var handler: PGCameraVolumeButtonHandler? + private var handler: VolumeButtonHandlerImpl? + private weak var sharedAccountContext: SharedAccountContext? private var nextListenerId: Int = 0 private var listeners: [ListenerReference] = [] override init() { super.init() - - /*self.disposable = (shouldBeActive - |> deliverOnMainQueue).start(next: { [weak self] value in - guard let strongSelf = self else { - return - } - strongSelf.handler.enabled = value - })*/ - } - - deinit { - if let handler = self.handler { - handler.enabled = false - } } func add(listener: VolumeButtonsListener) -> Int { + self.sharedAccountContext = listener.sharedAccountContext + let id = self.nextListenerId self.nextListenerId += 1 @@ -69,7 +129,7 @@ public class VolumeButtonsListener { } } - private func performAction(action: Action) { + private func performAction(_ action: Action) { for i in (0 ..< self.listeners.count).reversed() { if let listener = self.listeners[i].listener, listener.isActive { switch action { @@ -100,28 +160,30 @@ public class VolumeButtonsListener { } if isActive { - if self.handler == nil { - self.handler = PGCameraVolumeButtonHandler(upButtonPressedBlock: { [weak self] in - self?.performAction(action: .up) - }, upButtonReleasedBlock: { [weak self] in - self?.performAction(action: .upRelease) - }, downButtonPressedBlock: { [weak self] in - self?.performAction(action: .down) - }, downButtonReleasedBlock: { [weak self] in - self?.performAction(action: .downRelease) - }) + if let sharedAccountContext = self.sharedAccountContext { + let performAction: (VolumeButtonsListener.Action) -> Void = { [weak self] action in + self?.performAction(action) + } + if #available(iOS 17.2, *) { + self.handler = AVCaptureEventHandlerImpl( + context: sharedAccountContext, + performAction: performAction + ) + } else { + self.handler = LegacyHandlerImpl( + context: sharedAccountContext, + performAction: performAction + ) + } } - self.handler?.enabled = true } else { - if let handler = self.handler { - self.handler = nil - - handler.enabled = false - } + self.handler = nil } } } + fileprivate let sharedAccountContext: SharedAccountContext + private static var sharedContext: SharedContext = { return SharedContext() }() @@ -137,12 +199,14 @@ public class VolumeButtonsListener { private var disposable: Disposable? public init( + sharedContext: SharedAccountContext, shouldBeActive: Signal, upPressed: @escaping () -> Void, upReleased: @escaping () -> Void = {}, downPressed: @escaping () -> Void, downReleased: @escaping () -> Void = {} ) { + self.sharedAccountContext = sharedContext self.upPressed = upPressed self.upReleased = upReleased self.downPressed = downPressed