diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 4ccced1c32..580340bd5e 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -5988,7 +5988,7 @@ Sorry for the inconvenience."; "CHAT_VOICECHAT_START" = "%1$@ has started voice chat in the group %2$@"; "CHAT_VOICECHAT_INVITE" = "%1$@ has invited %3$@ in the group %2$@"; -"CHAT_VOICECHAT_INVITE_YOU" = "%1$@ has invited you to voice chat in the group %1$@"; +"CHAT_VOICECHAT_INVITE_YOU" = "%1$@ has invited you to voice chat in the group %2$@"; "Call.VoiceChatInProgressTitle" = "Voice Chat in Progress"; "Call.VoiceChatInProgressMessageCall" = "Leave voice chat in %1$@ and start a call with %2$@?"; diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 05220ae6fe..d989df2a68 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -1361,7 +1361,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { animateOnline = true } else if let channel = renderedPeer.peer as? TelegramChannel { onlineIsVoiceChat = true - if channel.flags.contains(.hasVoiceChat) && item.interaction.searchTextHighightState == nil { + if channel.flags.contains(.hasActiveVoiceChat) && item.interaction.searchTextHighightState == nil { online = true } animateOnline = true diff --git a/submodules/Display/Source/Navigation/NavigationController.swift b/submodules/Display/Source/Navigation/NavigationController.swift index a570221aca..75aeafe26c 100644 --- a/submodules/Display/Source/Navigation/NavigationController.swift +++ b/submodules/Display/Source/Navigation/NavigationController.swift @@ -140,6 +140,7 @@ open class NavigationController: UINavigationController, ContainableController, var inCallNavigate: (() -> Void)? private var inCallStatusBar: StatusBar? + private var updateInCallStatusBarState: CallStatusBarNode? private var globalScrollToTopNode: ScrollToTopNode? private var rootContainer: RootContainer? private var rootModalFrame: NavigationModalFrame? @@ -406,6 +407,11 @@ open class NavigationController: UINavigationController, ContainableController, inCallStatusBar.callStatusBarNode?.frame = inCallStatusBarFrame layout.statusBarHeight = inCallStatusBarFrame.height inCallStatusBar.frame = inCallStatusBarFrame + + if let forceInCallStatusBar = self.updateInCallStatusBarState { + self.updateInCallStatusBarState = nil + inCallStatusBar.updateState(statusBar: nil, withSafeInsets: !layout.safeInsets.top.isZero, inCallNode: forceInCallStatusBar, animated: false) + } } if let globalOverlayContainerParent = self.globalOverlayContainerParent { @@ -1409,6 +1415,8 @@ open class NavigationController: UINavigationController, ContainableController, if let layout = self.validLayout { inCallStatusBar.updateState(statusBar: nil, withSafeInsets: !layout.safeInsets.top.isZero, inCallNode: forceInCallStatusBar, animated: false) self.containerLayoutUpdated(layout, transition: transition) + } else { + self.updateInCallStatusBarState = forceInCallStatusBar } } else if let inCallStatusBar = self.inCallStatusBar { self.inCallStatusBar = nil diff --git a/submodules/Display/Source/Navigation/NavigationModalContainer.swift b/submodules/Display/Source/Navigation/NavigationModalContainer.swift index f683399506..0b06f95718 100644 --- a/submodules/Display/Source/Navigation/NavigationModalContainer.swift +++ b/submodules/Display/Source/Navigation/NavigationModalContainer.swift @@ -333,8 +333,7 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes let containerLayout: ContainerViewLayout let containerFrame: CGRect let containerScale: CGFloat - switch layout.metrics.widthClass { - case .compact: + if layout.metrics.widthClass == .compact || self.isFlat { self.panRecognizer?.isEnabled = true self.container.clipsToBounds = true if self.isFlat { @@ -387,7 +386,7 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate, UIGes let scaledTopInset: CGFloat = topInset * (1.0 - coveredByModalTransition) + maxScaledTopInset * coveredByModalTransition containerFrame = unscaledFrame.offsetBy(dx: 0.0, dy: scaledTopInset - (unscaledFrame.midY - containerScale * unscaledFrame.height / 2.0)) } - case .regular: + } else { self.panRecognizer?.isEnabled = false if self.isFlat { self.dim.backgroundColor = .clear diff --git a/submodules/SyncCore/Sources/TelegramChannel.swift b/submodules/SyncCore/Sources/TelegramChannel.swift index 58fa47db67..e7d9234ea5 100644 --- a/submodules/SyncCore/Sources/TelegramChannel.swift +++ b/submodules/SyncCore/Sources/TelegramChannel.swift @@ -142,6 +142,7 @@ public struct TelegramChannelFlags: OptionSet { public static let isScam = TelegramChannelFlags(rawValue: 1 << 2) public static let hasGeo = TelegramChannelFlags(rawValue: 1 << 3) public static let hasVoiceChat = TelegramChannelFlags(rawValue: 1 << 4) + public static let hasActiveVoiceChat = TelegramChannelFlags(rawValue: 1 << 5) } public final class TelegramChannel: Peer { @@ -298,4 +299,8 @@ public final class TelegramChannel: Peer { public func withUpdatedDefaultBannedRights(_ defaultBannedRights: TelegramChatBannedRights?) -> TelegramChannel { return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: self.flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: defaultBannedRights) } + + public func withUpdatedFlags(_ flags: TelegramChannelFlags) -> TelegramChannel { + return TelegramChannel(id: self.id, accessHash: self.accessHash, title: self.title, username: self.username, photo: self.photo, creationDate: self.creationDate, version: self.version, participationStatus: self.participationStatus, info: self.info, flags: flags, restrictionInfo: self.restrictionInfo, adminRights: self.adminRights, bannedRights: self.bannedRights, defaultBannedRights: self.defaultBannedRights) + } } diff --git a/submodules/TelegramCallsUI/BUILD b/submodules/TelegramCallsUI/BUILD index ddd6662a52..1a1a233a5f 100644 --- a/submodules/TelegramCallsUI/BUILD +++ b/submodules/TelegramCallsUI/BUILD @@ -38,6 +38,7 @@ swift_library( "//submodules/AlertUI:AlertUI", "//submodules/DirectionalPanGesture:DirectionalPanGesture", "//submodules/PeerInfoUI:PeerInfoUI", + "//submodules/DeviceProximity:DeviceProximity", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift b/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift index 3f146a5daf..d54a4a403e 100644 --- a/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift +++ b/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift @@ -404,7 +404,7 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode { } if let (size, leftInset, rightInset) = self.validLayout { - self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .immediate) + self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .animated(duration: 0.2, curve: .easeInOut)) } if updateAudioLevels { @@ -448,6 +448,8 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode { public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { self.validLayout = (size, leftInset, rightInset) + let staticTransition: ContainedViewLayoutTransition = .immediate + let panelHeight = size.height transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(), size: size)) @@ -462,17 +464,17 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode { let joinButtonTitleSize = self.joinButtonTitleNode.updateLayout(CGSize(width: 150.0, height: .greatestFiniteMagnitude)) let joinButtonSize = CGSize(width: joinButtonTitleSize.width + 20.0, height: 28.0) let joinButtonFrame = CGRect(origin: CGPoint(x: size.width - rightInset - 7.0 - joinButtonSize.width, y: floor((panelHeight - joinButtonSize.height) / 2.0)), size: joinButtonSize) - transition.updateFrame(node: self.joinButton, frame: joinButtonFrame) - transition.updateFrame(node: self.joinButtonBackgroundNode, frame: CGRect(origin: CGPoint(), size: joinButtonFrame.size)) - transition.updateFrame(node: self.joinButtonTitleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((joinButtonFrame.width - joinButtonTitleSize.width) / 2.0), y: floorToScreenPixels((joinButtonFrame.height - joinButtonTitleSize.height) / 2.0)), size: joinButtonTitleSize)) + staticTransition.updateFrame(node: self.joinButton, frame: joinButtonFrame) + staticTransition.updateFrame(node: self.joinButtonBackgroundNode, frame: CGRect(origin: CGPoint(), size: joinButtonFrame.size)) + staticTransition.updateFrame(node: self.joinButtonTitleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((joinButtonFrame.width - joinButtonTitleSize.width) / 2.0), y: floorToScreenPixels((joinButtonFrame.height - joinButtonTitleSize.height) / 2.0)), size: joinButtonTitleSize)) let micButtonSize = CGSize(width: 36.0, height: 36.0) let micButtonFrame = CGRect(origin: CGPoint(x: size.width - rightInset - 7.0 - micButtonSize.width, y: floor((panelHeight - micButtonSize.height) / 2.0)), size: micButtonSize) - transition.updateFrame(node: self.micButton, frame: micButtonFrame) - transition.updateFrame(node: self.micButtonBackgroundNode, frame: CGRect(origin: CGPoint(), size: micButtonFrame.size)) + staticTransition.updateFrame(node: self.micButton, frame: micButtonFrame) + staticTransition.updateFrame(node: self.micButtonBackgroundNode, frame: CGRect(origin: CGPoint(), size: micButtonFrame.size)) let animationSize = CGSize(width: 36.0, height: 36.0) - transition.updateFrame(node: self.micButtonForegroundNode, frame: CGRect(origin: CGPoint(x: floor((micButtonFrame.width - animationSize.width) / 2.0), y: floor((micButtonFrame.height - animationSize.height) / 2.0)), size: animationSize)) + staticTransition.updateFrame(node: self.micButtonForegroundNode, frame: CGRect(origin: CGPoint(x: floor((micButtonFrame.width - animationSize.width) / 2.0), y: floor((micButtonFrame.height - animationSize.height) / 2.0)), size: animationSize)) var isMuted = true if let _ = self.callState?.muteState { @@ -498,11 +500,11 @@ public final class GroupCallNavigationAccessoryPanel: ASDisplayNode { let textSize = self.textNode.updateLayout(CGSize(width: size.width, height: .greatestFiniteMagnitude)) let titleFrame = CGRect(origin: CGPoint(x: leftInset + 16.0, y: 9.0), size: titleSize) - transition.updateFrame(node: self.titleNode, frame: titleFrame) - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: leftInset + 16.0, y: titleFrame.maxY + 1.0), size: textSize)) + staticTransition.updateFrame(node: self.titleNode, frame: titleFrame) + staticTransition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: leftInset + 16.0, y: titleFrame.maxY + 1.0), size: textSize)) if let image = self.muteIconNode.image { - transition.updateFrame(node: self.muteIconNode, frame: CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: titleFrame.minY + 5.0), size: image.size)) + staticTransition.updateFrame(node: self.muteIconNode, frame: CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: titleFrame.minY + 5.0), size: image.size)) } self.muteIconNode.isHidden = self.currentData?.groupCall != nil self.joinButton.isHidden = self.currentData?.groupCall != nil diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index 3d12012574..deb989e57c 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -14,6 +14,7 @@ import TelegramPresentationData import DeviceAccess import UniversalMediaPlayer import AccountContext +import DeviceProximity public final class AccountGroupCallContextImpl: AccountGroupCallContext { public final class Proxy { @@ -427,6 +428,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { public weak var sourcePanel: ASDisplayNode? + private var proximityManagerIndex: Int? + init( accountContext: AccountContext, audioSession: ManagedAudioSession, @@ -651,6 +654,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { self.myAudioLevelTimer?.invalidate() self.typingDisposable.dispose() + + if let proximityManagerIndex = self.proximityManagerIndex { + DeviceProximityManager.shared().remove(proximityManagerIndex) + } } private func updateSessionState(internalState: InternalState, audioSessionControl: ManagedAudioSessionControl?) { @@ -663,6 +670,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { if let audioSessionControl = audioSessionControl, previousControl == nil { audioSessionControl.setOutputMode(.custom(self.currentAudioOutputValue)) audioSessionControl.setup(synchronous: true) + + self.setCurrentAudioOutput(.speaker) } self.audioSessionShouldBeActive.set(true) @@ -1033,6 +1042,26 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } self.currentAudioOutputValue = output + var shouldMonitorProximity = false + switch output { + case .builtin: + shouldMonitorProximity = true + default: + break + } + + if shouldMonitorProximity { + if self.proximityManagerIndex == nil { + self.proximityManagerIndex = DeviceProximityManager.shared().add { _ in + } + } + } else { + if let proximityManagerIndex = self.proximityManagerIndex { + self.proximityManagerIndex = nil + DeviceProximityManager.shared().remove(proximityManagerIndex) + } + } + self.audioOutputStatePromise.set(.single((self.audioOutputStateValue.0, output)) |> then( .single(self.audioOutputStateValue) diff --git a/submodules/TelegramCore/Sources/ApiGroupOrChannel.swift b/submodules/TelegramCore/Sources/ApiGroupOrChannel.swift index 4b91c29b12..3e04fe1dea 100644 --- a/submodules/TelegramCore/Sources/ApiGroupOrChannel.swift +++ b/submodules/TelegramCore/Sources/ApiGroupOrChannel.swift @@ -100,6 +100,9 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? { if (flags & Int32(1 << 23)) != 0 { channelFlags.insert(.hasVoiceChat) } + if (flags & Int32(1 << 24)) != 0 { + channelFlags.insert(.hasActiveVoiceChat) + } let restrictionInfo: PeerAccessRestrictionInfo? if let restrictionReason = restrictionReason { diff --git a/submodules/TelegramCore/Sources/GroupCalls.swift b/submodules/TelegramCore/Sources/GroupCalls.swift index 55c0f02800..c9b58221cf 100644 --- a/submodules/TelegramCore/Sources/GroupCalls.swift +++ b/submodules/TelegramCore/Sources/GroupCalls.swift @@ -440,6 +440,15 @@ public func stopGroupCall(account: Account, peerId: PeerId, callId: Int64, acces return cachedData } }) + if var peer = transaction.getPeer(peerId) as? TelegramChannel { + var flags = peer.flags + flags.remove(.hasVoiceChat) + flags.remove(.hasActiveVoiceChat) + peer = peer.withUpdatedFlags(flags) + updatePeers(transaction: transaction, peers: [peer], update: { _, updated in + return updated + }) + } account.stateManager.addUpdates(result) } diff --git a/submodules/TelegramUI/Sources/ApplicationContext.swift b/submodules/TelegramUI/Sources/ApplicationContext.swift index 8a0e8a17b8..1ac1a97b48 100644 --- a/submodules/TelegramUI/Sources/ApplicationContext.swift +++ b/submodules/TelegramUI/Sources/ApplicationContext.swift @@ -729,6 +729,8 @@ final class AuthorizedApplicationContext { } })) }) + + self.rootController.setForceInCallStatusBar((self.context.sharedContext as! SharedAccountContextImpl).currentCallStatusBarNode) } deinit { diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 1b3055caf9..1849fa6c4f 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -94,7 +94,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { private var callDisposable: Disposable? private var callStateDisposable: Disposable? - private var currentCallStatusBarNode: CallStatusBarNodeImpl? + private(set) var currentCallStatusBarNode: CallStatusBarNodeImpl? private var groupCallDisposable: Disposable?