mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
080a8b780a
BIN
Telegram/Telegram-iOS/Resources/voip_group_recording_started.mp3
Normal file
BIN
Telegram/Telegram-iOS/Resources/voip_group_recording_started.mp3
Normal file
Binary file not shown.
BIN
Telegram/Telegram-iOS/Resources/voip_group_unmuted.mp3
Normal file
BIN
Telegram/Telegram-iOS/Resources/voip_group_unmuted.mp3
Normal file
Binary file not shown.
@ -286,6 +286,11 @@ public final class PresentationGroupCallMemberEvent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum PresentationGroupCallTone {
|
||||||
|
case unmuted
|
||||||
|
case recordingStarted
|
||||||
|
}
|
||||||
|
|
||||||
public protocol PresentationGroupCall: class {
|
public protocol PresentationGroupCall: class {
|
||||||
var account: Account { get }
|
var account: Account { get }
|
||||||
var accountContext: AccountContext { get }
|
var accountContext: AccountContext { get }
|
||||||
@ -321,6 +326,8 @@ public protocol PresentationGroupCall: class {
|
|||||||
func setVolume(peerId: PeerId, volume: Int32, sync: Bool)
|
func setVolume(peerId: PeerId, volume: Int32, sync: Bool)
|
||||||
func setFullSizeVideo(peerId: PeerId?)
|
func setFullSizeVideo(peerId: PeerId?)
|
||||||
func setCurrentAudioOutput(_ output: AudioSessionOutput)
|
func setCurrentAudioOutput(_ output: AudioSessionOutput)
|
||||||
|
|
||||||
|
func playTone(_ tone: PresentationGroupCallTone)
|
||||||
|
|
||||||
func updateMuteState(peerId: PeerId, isMuted: Bool) -> GroupCallParticipantsContext.Participant.MuteState?
|
func updateMuteState(peerId: PeerId, isMuted: Bool) -> GroupCallParticipantsContext.Participant.MuteState?
|
||||||
func setShouldBeRecording(_ shouldBeRecording: Bool, title: String?)
|
func setShouldBeRecording(_ shouldBeRecording: Bool, title: String?)
|
||||||
|
@ -413,7 +413,7 @@ public final class ManagedAudioSession {
|
|||||||
}, deactivate: deactivate)
|
}, deactivate: deactivate)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func push(audioSessionType: ManagedAudioSessionType, outputMode: AudioSessionOutputMode = .system, once: Bool = false, manualActivate: @escaping (ManagedAudioSessionControl) -> Void, deactivate: @escaping () -> Signal<Void, NoError>, headsetConnectionStatusChanged: @escaping (Bool) -> Void = { _ in }, availableOutputsChanged: @escaping ([AudioSessionOutput], AudioSessionOutput?) -> Void = { _, _ in }) -> Disposable {
|
public func push(audioSessionType: ManagedAudioSessionType, outputMode: AudioSessionOutputMode = .system, once: Bool = false, activateImmediately: Bool = false, manualActivate: @escaping (ManagedAudioSessionControl) -> Void, deactivate: @escaping () -> Signal<Void, NoError>, headsetConnectionStatusChanged: @escaping (Bool) -> Void = { _ in }, availableOutputsChanged: @escaping ([AudioSessionOutput], AudioSessionOutput?) -> Void = { _, _ in }) -> Disposable {
|
||||||
let id = OSAtomicIncrement32(&self.nextId)
|
let id = OSAtomicIncrement32(&self.nextId)
|
||||||
let queue = self.queue
|
let queue = self.queue
|
||||||
queue.async {
|
queue.async {
|
||||||
@ -422,7 +422,7 @@ public final class ManagedAudioSession {
|
|||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
for holder in strongSelf.holders {
|
for holder in strongSelf.holders {
|
||||||
if holder.id == id && holder.active {
|
if holder.id == id && holder.active {
|
||||||
strongSelf.setup(type: audioSessionType, outputMode: holder.outputMode, activateNow: false)
|
strongSelf.setup(type: audioSessionType, outputMode: holder.outputMode, activateNow: activateImmediately)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ private func loadToneData(name: String, addSilenceDuration: Double = 0.0) -> Dat
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PresentationCallTone {
|
enum PresentationCallTone: Equatable {
|
||||||
case ringing
|
case ringing
|
||||||
case connecting
|
case connecting
|
||||||
case busy
|
case busy
|
||||||
@ -83,6 +83,7 @@ enum PresentationCallTone {
|
|||||||
case groupJoined
|
case groupJoined
|
||||||
case groupLeft
|
case groupLeft
|
||||||
case groupConnecting
|
case groupConnecting
|
||||||
|
case custom(name: String, loopCount: Int?)
|
||||||
|
|
||||||
var loopCount: Int? {
|
var loopCount: Int? {
|
||||||
switch self {
|
switch self {
|
||||||
@ -96,6 +97,8 @@ enum PresentationCallTone {
|
|||||||
return 1
|
return 1
|
||||||
case .groupConnecting:
|
case .groupConnecting:
|
||||||
return nil
|
return nil
|
||||||
|
case let .custom(_, loopCount):
|
||||||
|
return loopCount
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -120,5 +123,7 @@ func presentationCallToneData(_ tone: PresentationCallTone) -> Data? {
|
|||||||
return loadToneData(name: "voip_group_left.mp3")
|
return loadToneData(name: "voip_group_left.mp3")
|
||||||
case .groupConnecting:
|
case .groupConnecting:
|
||||||
return loadToneData(name: "voip_group_connecting.mp3", addSilenceDuration: 2.0)
|
return loadToneData(name: "voip_group_connecting.mp3", addSilenceDuration: 2.0)
|
||||||
|
case let .custom(name, _):
|
||||||
|
return loadToneData(name: name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,8 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext {
|
|||||||
myPeerId: account.peerId,
|
myPeerId: account.peerId,
|
||||||
id: call.id,
|
id: call.id,
|
||||||
accessHash: call.accessHash,
|
accessHash: call.accessHash,
|
||||||
state: state
|
state: state,
|
||||||
|
previousServiceState: nil
|
||||||
)
|
)
|
||||||
|
|
||||||
strongSelf.participantsContext = context
|
strongSelf.participantsContext = context
|
||||||
@ -581,7 +582,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
self.audioOutputStatePromise.set(.single(([], .speaker)))
|
self.audioOutputStatePromise.set(.single(([], .speaker)))
|
||||||
}
|
}
|
||||||
|
|
||||||
self.audioSessionDisposable = audioSession.push(audioSessionType: .voiceCall, manualActivate: { [weak self] control in
|
self.audioSessionDisposable = audioSession.push(audioSessionType: .voiceCall, activateImmediately: true, manualActivate: { [weak self] control in
|
||||||
Queue.mainQueue().async {
|
Queue.mainQueue().async {
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.updateSessionState(internalState: strongSelf.internalState, audioSessionControl: control)
|
strongSelf.updateSessionState(internalState: strongSelf.internalState, audioSessionControl: control)
|
||||||
@ -842,7 +843,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let sourceContext = sourceContext, let initialState = sourceContext.immediateState {
|
if let sourceContext = sourceContext, let initialState = sourceContext.immediateState {
|
||||||
let temporaryParticipantsContext = GroupCallParticipantsContext(account: self.account, peerId: self.peerId, myPeerId: myPeerId, id: sourceContext.id, accessHash: sourceContext.accessHash, state: initialState)
|
let temporaryParticipantsContext = GroupCallParticipantsContext(account: self.account, peerId: self.peerId, myPeerId: myPeerId, id: sourceContext.id, accessHash: sourceContext.accessHash, state: initialState, previousServiceState: sourceContext.serviceState)
|
||||||
self.temporaryParticipantsContext = temporaryParticipantsContext
|
self.temporaryParticipantsContext = temporaryParticipantsContext
|
||||||
self.participantsContextStateDisposable.set((combineLatest(queue: .mainQueue(),
|
self.participantsContextStateDisposable.set((combineLatest(queue: .mainQueue(),
|
||||||
myPeer,
|
myPeer,
|
||||||
@ -1364,8 +1365,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
let myPeerId = self.joinAsPeerId
|
let myPeerId = self.joinAsPeerId
|
||||||
|
|
||||||
var initialState = initialState
|
var initialState = initialState
|
||||||
|
var serviceState: GroupCallParticipantsContext.ServiceState?
|
||||||
if let participantsContext = self.participantsContext, let immediateState = participantsContext.immediateState {
|
if let participantsContext = self.participantsContext, let immediateState = participantsContext.immediateState {
|
||||||
initialState.mergeActivity(from: immediateState, myPeerId: myPeerId, previousMyPeerId: self.ignorePreviousJoinAsPeerId?.0)
|
initialState.mergeActivity(from: immediateState, myPeerId: myPeerId, previousMyPeerId: self.ignorePreviousJoinAsPeerId?.0)
|
||||||
|
serviceState = participantsContext.serviceState
|
||||||
}
|
}
|
||||||
|
|
||||||
let participantsContext = GroupCallParticipantsContext(
|
let participantsContext = GroupCallParticipantsContext(
|
||||||
@ -1374,7 +1377,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
myPeerId: self.joinAsPeerId,
|
myPeerId: self.joinAsPeerId,
|
||||||
id: callInfo.id,
|
id: callInfo.id,
|
||||||
accessHash: callInfo.accessHash,
|
accessHash: callInfo.accessHash,
|
||||||
state: initialState
|
state: initialState,
|
||||||
|
previousServiceState: serviceState
|
||||||
)
|
)
|
||||||
self.temporaryParticipantsContext = nil
|
self.temporaryParticipantsContext = nil
|
||||||
self.participantsContext = participantsContext
|
self.participantsContext = participantsContext
|
||||||
@ -1397,6 +1401,8 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strongSelf.participantsContext?.updateAdminIds(adminIds)
|
||||||
|
|
||||||
var topParticipants: [GroupCallParticipantsContext.Participant] = []
|
var topParticipants: [GroupCallParticipantsContext.Participant] = []
|
||||||
|
|
||||||
@ -1482,6 +1488,12 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if participant.peer.id == strongSelf.joinAsPeerId {
|
if participant.peer.id == strongSelf.joinAsPeerId {
|
||||||
|
var filteredMuteState = participant.muteState
|
||||||
|
if isReconnectingAsSpeaker || strongSelf.currentConnectionMode != .rtc {
|
||||||
|
filteredMuteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: false, mutedByYou: false)
|
||||||
|
participant.muteState = filteredMuteState
|
||||||
|
}
|
||||||
|
|
||||||
let previousRaisedHand = strongSelf.stateValue.raisedHand
|
let previousRaisedHand = strongSelf.stateValue.raisedHand
|
||||||
if !(strongSelf.stateValue.muteState?.canUnmute ?? false) {
|
if !(strongSelf.stateValue.muteState?.canUnmute ?? false) {
|
||||||
strongSelf.stateValue.raisedHand = participant.raiseHandRating != nil
|
strongSelf.stateValue.raisedHand = participant.raiseHandRating != nil
|
||||||
@ -1512,15 +1524,10 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
text = presentationData.strings.VoiceChat_YouCanNowSpeak
|
text = presentationData.strings.VoiceChat_YouCanNowSpeak
|
||||||
}
|
}
|
||||||
strongSelf.accountContext.sharedContext.mainWindow?.present(UndoOverlayController(presentationData: presentationData, content: .voiceChatCanSpeak(text: text), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return true }), on: .root, blockInteraction: false, completion: {})
|
strongSelf.accountContext.sharedContext.mainWindow?.present(UndoOverlayController(presentationData: presentationData, content: .voiceChatCanSpeak(text: text), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return true }), on: .root, blockInteraction: false, completion: {})
|
||||||
|
strongSelf.playTone(.unmuted)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var filteredMuteState = participant.muteState
|
|
||||||
if isReconnectingAsSpeaker || strongSelf.currentConnectionMode != .rtc {
|
|
||||||
filteredMuteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: false, mutedByYou: false)
|
|
||||||
participant.muteState = filteredMuteState
|
|
||||||
}
|
|
||||||
|
|
||||||
if let muteState = filteredMuteState {
|
if let muteState = filteredMuteState {
|
||||||
if muteState.canUnmute {
|
if muteState.canUnmute {
|
||||||
@ -1733,6 +1740,20 @@ public final class PresentationGroupCallImpl: PresentationGroupCall {
|
|||||||
self.toneRenderer?.setAudioSessionActive(value)
|
self.toneRenderer?.setAudioSessionActive(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func playTone(_ tone: PresentationGroupCallTone) {
|
||||||
|
let name: String
|
||||||
|
switch tone {
|
||||||
|
case .unmuted:
|
||||||
|
name = "voip_group_unmuted.mp3"
|
||||||
|
case .recordingStarted:
|
||||||
|
name = "voip_group_recording_started.mp3"
|
||||||
|
}
|
||||||
|
|
||||||
|
let toneRenderer = PresentationCallToneRenderer(tone: .custom(name: name, loopCount: 1))
|
||||||
|
self.toneRenderer = toneRenderer
|
||||||
|
toneRenderer.setAudioSessionActive(self.isAudioSessionActive)
|
||||||
|
}
|
||||||
|
|
||||||
private func markAsCanBeRemoved() {
|
private func markAsCanBeRemoved() {
|
||||||
if self.markedAsCanBeRemoved {
|
if self.markedAsCanBeRemoved {
|
||||||
|
@ -1880,6 +1880,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
strongSelf.call.setShouldBeRecording(true, title: title)
|
strongSelf.call.setShouldBeRecording(true, title: title)
|
||||||
|
|
||||||
strongSelf.presentUndoOverlay(content: .voiceChatRecording(text: strongSelf.presentationData.strings.VoiceChat_RecordingStarted), action: { _ in return false })
|
strongSelf.presentUndoOverlay(content: .voiceChatRecording(text: strongSelf.presentationData.strings.VoiceChat_RecordingStarted), action: { _ in return false })
|
||||||
|
strongSelf.call.playTone(.recordingStarted)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
self?.controller?.present(controller, in: .window(.root))
|
self?.controller?.present(controller, in: .window(.root))
|
||||||
|
@ -960,9 +960,6 @@ public final class GroupCallParticipantsContext {
|
|||||||
let accountPeerId = self.account.peerId
|
let accountPeerId = self.account.peerId
|
||||||
return self.statePromise.get()
|
return self.statePromise.get()
|
||||||
|> map { state -> State in
|
|> map { state -> State in
|
||||||
if state.overlayState.isEmpty {
|
|
||||||
return state.state
|
|
||||||
}
|
|
||||||
var publicState = state.state
|
var publicState = state.state
|
||||||
var sortAgain = false
|
var sortAgain = false
|
||||||
let canSeeHands = state.state.isCreator || state.state.adminIds.contains(accountPeerId)
|
let canSeeHands = state.state.isCreator || state.state.adminIds.contains(accountPeerId)
|
||||||
@ -1015,20 +1012,26 @@ public final class GroupCallParticipantsContext {
|
|||||||
private var isLoadingMore: Bool = false
|
private var isLoadingMore: Bool = false
|
||||||
private var shouldResetStateFromServer: Bool = false
|
private var shouldResetStateFromServer: Bool = false
|
||||||
private var missingSsrcs = Set<UInt32>()
|
private var missingSsrcs = Set<UInt32>()
|
||||||
|
|
||||||
private var nextActivityRank: Int = 0
|
|
||||||
private var activityRankResetTimer: SwiftSignalKit.Timer?
|
private var activityRankResetTimer: SwiftSignalKit.Timer?
|
||||||
|
|
||||||
private let updateDefaultMuteDisposable = MetaDisposable()
|
private let updateDefaultMuteDisposable = MetaDisposable()
|
||||||
private let updateShouldBeRecordingDisposable = MetaDisposable()
|
private let updateShouldBeRecordingDisposable = MetaDisposable()
|
||||||
|
|
||||||
|
public struct ServiceState {
|
||||||
|
fileprivate var nextActivityRank: Int = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
public private(set) var serviceState: ServiceState
|
||||||
|
|
||||||
public init(account: Account, peerId: PeerId, myPeerId: PeerId, id: Int64, accessHash: Int64, state: State) {
|
public init(account: Account, peerId: PeerId, myPeerId: PeerId, id: Int64, accessHash: Int64, state: State, previousServiceState: ServiceState?) {
|
||||||
self.account = account
|
self.account = account
|
||||||
self.myPeerId = myPeerId
|
self.myPeerId = myPeerId
|
||||||
self.id = id
|
self.id = id
|
||||||
self.accessHash = accessHash
|
self.accessHash = accessHash
|
||||||
self.stateValue = InternalState(state: state, overlayState: OverlayState())
|
self.stateValue = InternalState(state: state, overlayState: OverlayState())
|
||||||
self.statePromise = ValuePromise<InternalState>(self.stateValue)
|
self.statePromise = ValuePromise<InternalState>(self.stateValue)
|
||||||
|
self.serviceState = previousServiceState ?? ServiceState()
|
||||||
|
|
||||||
self.updatesDisposable.set((self.account.stateManager.groupCallParticipantUpdates
|
self.updatesDisposable.set((self.account.stateManager.groupCallParticipantUpdates
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] updates in
|
|> deliverOnMainQueue).start(next: { [weak self] updates in
|
||||||
@ -1167,10 +1170,16 @@ public final class GroupCallParticipantsContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func takeNextActivityRank() -> Int {
|
private func takeNextActivityRank() -> Int {
|
||||||
let value = self.nextActivityRank
|
let value = self.serviceState.nextActivityRank
|
||||||
self.nextActivityRank += 1
|
self.serviceState.nextActivityRank += 1
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func updateAdminIds(_ adminIds: Set<PeerId>) {
|
||||||
|
if self.stateValue.state.adminIds != adminIds {
|
||||||
|
self.stateValue.state.adminIds = adminIds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func reportSpeakingParticipants(ids: [PeerId: UInt32]) {
|
public func reportSpeakingParticipants(ids: [PeerId: UInt32]) {
|
||||||
if !ids.isEmpty {
|
if !ids.isEmpty {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user