diff --git a/submodules/AttachmentUI/Sources/AttachmentContainer.swift b/submodules/AttachmentUI/Sources/AttachmentContainer.swift index 62d803b41b..154406918a 100644 --- a/submodules/AttachmentUI/Sources/AttachmentContainer.swift +++ b/submodules/AttachmentUI/Sources/AttachmentContainer.swift @@ -96,6 +96,14 @@ final class AttachmentContainer: ASDisplayNode, UIGestureRecognizerDelegate { self.wrappingNode.view.addGestureRecognizer(panRecognizer) } + func cancelPanGesture() { + if let panGestureRecognizer = self.panGestureRecognizer, panGestureRecognizer.isEnabled { + self.panGestureArguments = nil + panGestureRecognizer.isEnabled = false + panGestureRecognizer.isEnabled = true + } + } + override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { if let (layout, _, _) = self.validLayout { if case .regular = layout.metrics.widthClass { diff --git a/submodules/AttachmentUI/Sources/AttachmentController.swift b/submodules/AttachmentUI/Sources/AttachmentController.swift index 9c7bed9a4c..b691749384 100644 --- a/submodules/AttachmentUI/Sources/AttachmentController.swift +++ b/submodules/AttachmentUI/Sources/AttachmentController.swift @@ -24,6 +24,7 @@ public protocol AttachmentContainable: ViewController { var requestAttachmentMenuExpansion: () -> Void { get set } var updateNavigationStack: (@escaping ([AttachmentContainable]) -> [AttachmentContainable]) -> Void { get set } var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void { get set } + var cancelPanGesture: () -> Void { get set } func resetForReuse() func prepareForReuse() @@ -283,6 +284,11 @@ public class AttachmentController: ViewController { strongSelf.panel.updateBackgroundAlpha(alpha, transition: transition) } } + controller.cancelPanGesture = { [weak self] in + if let strongSelf = self { + strongSelf.container.cancelPanGesture() + } + } let previousController = strongSelf.currentControllers.last let animateTransition = previousType != nil strongSelf.currentControllers = [controller] diff --git a/submodules/ComposePollUI/Sources/CreatePollController.swift b/submodules/ComposePollUI/Sources/CreatePollController.swift index f13ee4b9a3..cf94154189 100644 --- a/submodules/ComposePollUI/Sources/CreatePollController.swift +++ b/submodules/ComposePollUI/Sources/CreatePollController.swift @@ -520,6 +520,7 @@ private class CreatePollControllerImpl: ItemListController, AttachmentContainabl public var requestAttachmentMenuExpansion: () -> Void = {} public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> [AttachmentContainable]) -> Void = { _ in } public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in } + public var cancelPanGesture: () -> Void = { } } public func createPollController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peer: EnginePeer, isQuiz: Bool? = nil, completion: @escaping (ComposedPoll) -> Void) -> AttachmentContainable { diff --git a/submodules/LegacyUI/Sources/LegacyController.swift b/submodules/LegacyUI/Sources/LegacyController.swift index 1a672efcde..f8b851b42a 100644 --- a/submodules/LegacyUI/Sources/LegacyController.swift +++ b/submodules/LegacyUI/Sources/LegacyController.swift @@ -408,6 +408,7 @@ open class LegacyController: ViewController, PresentableController, AttachmentCo open var requestAttachmentMenuExpansion: () -> Void = {} open var updateNavigationStack: (@escaping ([AttachmentContainable]) -> [AttachmentContainable]) -> Void = { _ in } open var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in } + open var cancelPanGesture: () -> Void = { } public init(presentation: LegacyControllerPresentation, theme: PresentationTheme? = nil, strings: PresentationStrings? = nil, initialLayout: ContainerViewLayout? = nil) { self.sizeClass.set(SSignal.single(UIUserInterfaceSizeClass.compact.rawValue as NSNumber)) diff --git a/submodules/LocationUI/Sources/LocationPickerController.swift b/submodules/LocationUI/Sources/LocationPickerController.swift index b6e1baa559..ce9f7f7bb5 100644 --- a/submodules/LocationUI/Sources/LocationPickerController.swift +++ b/submodules/LocationUI/Sources/LocationPickerController.swift @@ -74,6 +74,7 @@ public final class LocationPickerController: ViewController, AttachmentContainab public var requestAttachmentMenuExpansion: () -> Void = {} public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> [AttachmentContainable]) -> Void = { _ in } public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in } + public var cancelPanGesture: () -> Void = { } public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, mode: LocationPickerMode, completion: @escaping (TelegramMediaMap, String?) -> Void) { self.context = context diff --git a/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift b/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift index 9f5d063a3b..7eb425fbc5 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift @@ -105,7 +105,7 @@ final class MediaPickerGridItemNode: GridItemNode { return self.asset?.localIdentifier ?? "" } - private var asset: PHAsset? { + var asset: PHAsset? { if let (fetchResult, index) = self.currentState { return fetchResult[index] } else { @@ -113,7 +113,7 @@ final class MediaPickerGridItemNode: GridItemNode { } } - func updateSelectionState() { + func updateSelectionState(animated: Bool = false) { if self.checkNode == nil, let _ = self.interaction?.selectionState, let theme = self.theme { let checkNode = InteractiveCheckNode(theme: CheckNodeTheme(theme: theme, style: .overlay)) checkNode.valueChanged = { [weak self] value in @@ -136,7 +136,7 @@ final class MediaPickerGridItemNode: GridItemNode { self.checkNode?.content = .counter(Int(index)) } } - self.checkNode?.setSelected(selected, animated: false) + self.checkNode?.setSelected(selected, animated: animated) } } diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index b9d5ce757e..24dfcfc388 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -101,8 +101,9 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { public var requestAttachmentMenuExpansion: () -> Void = { } public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> [AttachmentContainable]) -> Void = { _ in } public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in } + public var cancelPanGesture: () -> Void = { } - private class Node: ViewControllerTracingNode { + private class Node: ViewControllerTracingNode, UIGestureRecognizerDelegate { enum DisplayMode { case all case selected @@ -164,9 +165,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.gridNode = GridNode() super.init() - -// self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor - + self.addSubnode(self.containerNode) self.containerNode.addSubnode(self.backgroundNode) self.containerNode.addSubnode(self.gridNode) @@ -226,10 +225,12 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { }) if let selectionState = self.controller?.interaction?.selectionState { - func selectionChangedSignal(selectionState: TGMediaSelectionContext) -> Signal { + func selectionChangedSignal(selectionState: TGMediaSelectionContext) -> Signal { return Signal { subscriber in let disposable = selectionState.selectionChangedSignal()?.start(next: { next in - subscriber.putNext(Void()) + if let next = next as? TGMediaSelectionChange { + subscriber.putNext(next.animated) + } }, completed: {}) return ActionDisposable { disposable?.dispose() @@ -238,9 +239,9 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { } self.selectionChangedDisposable = (selectionChangedSignal(selectionState: selectionState) - |> deliverOnMainQueue).start(next: { [weak self] _ in + |> deliverOnMainQueue).start(next: { [weak self] animated in if let strongSelf = self { - strongSelf.updateSelectionState() + strongSelf.updateSelectionState(animated: animated) } }) } @@ -273,6 +274,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.itemsDimensionsUpdatedDisposable?.dispose() } + private var selectionGesture: MediaPickerGridSelectionGesture? override func didLoad() { super.didLoad() @@ -295,8 +297,32 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { } else { self.containerNode.clipsToBounds = true } - -// self.controller?.navigationBar?.updateBackgroundAlpha(0.0, transition: .immediate) + + self.selectionGesture = MediaPickerGridSelectionGesture(target: nil, action: nil, gridNode: self.gridNode) + self.selectionGesture?.delegate = self + self.selectionGesture?.began = { [weak self] in + self?.controller?.cancelPanGesture() + } + self.selectionGesture?.itemAt = { [weak self] point in + if let strongSelf = self, let itemNode = strongSelf.gridNode.itemNodeAtPoint(point) as? MediaPickerGridItemNode, let asset = itemNode.asset.flatMap({ TGMediaAsset(phAsset: $0) }) { + return (asset, strongSelf.controller?.interaction?.selectionState?.isItemSelected(asset) ?? false) + } else { + return nil + } + } + self.selectionGesture?.updateSelection = { [weak self] asset, selected in + if let strongSelf = self { + strongSelf.controller?.interaction?.selectionState?.setItem(asset, selected: selected, animated: true, sender: nil) + } + } + } + + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + if otherGestureRecognizer.view is UIScrollView || otherGestureRecognizer is UIPanGestureRecognizer { + return true + } else { + return false + } } fileprivate func dismissInput() { @@ -363,10 +389,10 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { self.updateNavigation(transition: .immediate) } - private func updateSelectionState() { + private func updateSelectionState(animated: Bool = false) { self.gridNode.forEachItemNode { itemNode in if let itemNode = itemNode as? MediaPickerGridItemNode { - itemNode.updateSelectionState() + itemNode.updateSelectionState(animated: animated) } } self.selectionNode?.updateSelectionState() @@ -1165,3 +1191,98 @@ private final class MediaPickerContextReferenceContentSource: ContextReferenceCo return ContextControllerReferenceViewInfo(referenceNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds) } } + +private class MediaPickerGridSelectionGesture: UIPanGestureRecognizer { + var itemAt: (CGPoint) -> (TGMediaSelectableItem, Bool)? = { _ in return nil } + var updateSelection: (TGMediaSelectableItem, Bool) -> Void = { _, _ in} + var began: () -> Void = {} + + private weak var gridNode: GridNode? + + private var processing = false + private var selecting = false + + private var initialLocation: CGPoint? + + init(target: Any?, action: Selector?, gridNode: GridNode) { + self.gridNode = gridNode + + super.init(target: target, action: action) + + gridNode.view.addGestureRecognizer(self) + } + + override func touchesBegan(_ touches: Set, with event: UIEvent) { + super.touchesBegan(touches, with: event) + + guard let touch = touches.first, let gridNode = self.gridNode else { + return + } + + let location = touch.location(in: gridNode.view) + self.initialLocation = location + } + + override func touchesMoved(_ touches: Set, with event: UIEvent) { + super.touchesMoved(touches, with: event) + + guard let touch = touches.first, let gridNode = self.gridNode, let initialLocation = self.initialLocation else { + self.state = .failed + return + } + + let location = touch.location(in: gridNode.view) + let translation = CGPoint(x: location.x - initialLocation.x, y: location.y - initialLocation.y) + + var additionalLocation: CGPoint? + if !self.processing { + if abs(translation.y) > 5.0 { + self.state = .failed + } else if abs(translation.x) > 4.0 { + self.processing = true + self.gridNode?.scrollView.isScrollEnabled = false + self.began() + + if let (_, selected) = self.itemAt(location) { + self.selecting = !selected + } + + additionalLocation = self.initialLocation + } + } + + if self.processing { + if let additionalLocation = additionalLocation { + if let (item, selected) = self.itemAt(additionalLocation), selected != self.selecting { + self.updateSelection(item, self.selecting) + } + } + + if let (item, selected) = self.itemAt(location), selected != self.selecting { + self.updateSelection(item, self.selecting) + } + } + } + + override func touchesEnded(_ touches: Set, with event: UIEvent) { + super.touchesEnded(touches, with: event) + + self.state = .failed + self.reset() + } + + override func touchesCancelled(_ touches: Set, with event: UIEvent) { + super.touchesCancelled(touches, with: event) + + self.state = .failed + self.reset() + } + + override func reset() { + super.reset() + + self.processing = false + self.initialLocation = nil + self.gridNode?.scrollView.isScrollEnabled = true + } +} diff --git a/submodules/TelegramUI/Sources/AttachmentFileController.swift b/submodules/TelegramUI/Sources/AttachmentFileController.swift index d44e94053e..a903ebcffe 100644 --- a/submodules/TelegramUI/Sources/AttachmentFileController.swift +++ b/submodules/TelegramUI/Sources/AttachmentFileController.swift @@ -167,6 +167,7 @@ private class AttachmentFileControllerImpl: ItemListController, AttachmentContai public var requestAttachmentMenuExpansion: () -> Void = {} public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> [AttachmentContainable]) -> Void = { _ in } public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in } + public var cancelPanGesture: () -> Void = { } var resetForReuseImpl: () -> Void = {} public func resetForReuse() { diff --git a/submodules/TelegramUI/Sources/ContactSelectionController.swift b/submodules/TelegramUI/Sources/ContactSelectionController.swift index 54d8b79e1e..801591f4d7 100644 --- a/submodules/TelegramUI/Sources/ContactSelectionController.swift +++ b/submodules/TelegramUI/Sources/ContactSelectionController.swift @@ -78,6 +78,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController var requestAttachmentMenuExpansion: () -> Void = {} var updateNavigationStack: (@escaping ([AttachmentContainable]) -> [AttachmentContainable]) -> Void = { _ in } var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in } + var cancelPanGesture: () -> Void = { } init(_ params: ContactSelectionControllerParams) { self.context = params.context diff --git a/third-party/webrtc/BUILD b/third-party/webrtc/BUILD index c504453075..e4fb9b3fc4 100644 --- a/third-party/webrtc/BUILD +++ b/third-party/webrtc/BUILD @@ -1902,6 +1902,7 @@ webrtc_sources = [ "modules/audio_coding/neteq/timestamp_scaler.h", "modules/audio_device/audio_device_buffer.h", "modules/audio_device/audio_device_generic.h", + "modules/audio_device/audio_device_impl.h", "modules/audio_device/dummy/audio_device_dummy.h", "modules/audio_device/dummy/file_audio_device.h", "modules/audio_device/dummy/file_audio_device_factory.h", @@ -2973,6 +2974,7 @@ ios_objc_sources = [ ] ios_sources = [ + "objc/native/api/audio_device_module.h", "objc/native/src/audio/audio_session_observer.h", "objc/native/src/audio/helpers.h", "objc/native/src/audio/helpers.mm",