AzureCommunicationUI/sdk/AzureCommunicationUICalling/Sources/Presentation/SwiftUI/Calling/Grid/Cell/ParticipantGridCellViewModel.swift (168 lines of code) (raw):

// // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // import Foundation import Combine struct ParticipantVideoViewInfoModel { let videoStreamType: VideoStreamInfoModel.MediaStreamType? let videoStreamId: String? } class ParticipantGridCellViewModel: ObservableObject, Identifiable { private let localizationProvider: LocalizationProviderProtocol private let accessibilityProvider: AccessibilityProviderProtocol let id = UUID() @Published var videoViewModel: ParticipantVideoViewInfoModel? @Published var accessibilityLabel: String = "" @Published var displayName: String? @Published var avatarDisplayName: String? @Published var isSpeaking: Bool @Published var isTypingRtt: Bool @Published var isMuted: Bool @Published var isHold: Bool @Published var participantIdentifier: String @Published var displayData = [CaptionsRttRecord]() private var isScreenSharing = false private var participantName: String private var renderDisplayName: String? private var isCameraEnabled: Bool private var participantStatus: ParticipantStatus? private var callType: CompositeCallType private var subscriptions = Set<AnyCancellable>() init(localizationProvider: LocalizationProviderProtocol, accessibilityProvider: AccessibilityProviderProtocol, participantModel: ParticipantInfoModel, isCameraEnabled: Bool, callType: CompositeCallType) { self.localizationProvider = localizationProvider self.accessibilityProvider = accessibilityProvider self.participantStatus = participantModel.status self.callType = callType let isDisplayConnecting = ParticipantGridCellViewModel.isOutgoingCallDialingInProgress( callType: callType, participantStatus: participantModel.status) if isDisplayConnecting { self.participantName = localizationProvider.getLocalizedString(LocalizationKey.callingCallMessage) self.displayName = self.participantName } else { self.participantName = participantModel.displayName self.displayName = participantModel.displayName } self.avatarDisplayName = participantModel.displayName self.isSpeaking = participantModel.isSpeaking self.isTypingRtt = participantModel.isTypingRtt self.isHold = participantModel.status == .hold self.participantIdentifier = participantModel.userIdentifier self.isMuted = participantModel.isMuted && participantModel.status == .connected self.isCameraEnabled = isCameraEnabled self.videoViewModel = getDisplayingVideoStreamModel(participantModel) self.accessibilityLabel = getAccessibilityLabel(participantModel: participantModel) } func update(participantModel: ParticipantInfoModel) { self.participantIdentifier = participantModel.userIdentifier let videoViewModel = getDisplayingVideoStreamModel(participantModel) if self.videoViewModel?.videoStreamId != videoViewModel.videoStreamId || self.videoViewModel?.videoStreamType != videoViewModel.videoStreamType { let newIsScreenSharing = videoViewModel.videoStreamType == .screenSharing if newIsScreenSharing { accessibilityProvider.postQueuedAnnouncement( localizationProvider.getLocalizedString(.screenshareStartAccessibilityLabel)) } else if self.isScreenSharing && !newIsScreenSharing { accessibilityProvider.postQueuedAnnouncement( localizationProvider.getLocalizedString(.screenshareEndAccessibilityLabel)) } self.isScreenSharing = newIsScreenSharing self.videoViewModel = ParticipantVideoViewInfoModel(videoStreamType: videoViewModel.videoStreamType, videoStreamId: videoViewModel.videoStreamId) } if self.participantName != participantModel.displayName || self.isMuted != participantModel.isMuted || self.isSpeaking != participantModel.isSpeaking || self.isCameraEnabled != participantModel.cameraVideoStreamModel?.videoStreamIdentifier.isEmpty || self.isHold != (participantModel.status == .hold) { self.accessibilityLabel = getAccessibilityLabel(participantModel: participantModel) } if self.participantStatus != participantModel.status { self.participantStatus = participantModel.status updateParticipantNameIfNeeded(with: renderDisplayName) self.isMuted = participantModel.isMuted && participantModel.status == .connected } if self.participantName != participantModel.displayName { self.participantName = participantModel.displayName updateParticipantNameIfNeeded(with: renderDisplayName) } if self.isSpeaking != participantModel.isSpeaking { self.isSpeaking = participantModel.isSpeaking } if self.isTypingRtt != participantModel.isTypingRtt { self.isTypingRtt = participantModel.isTypingRtt } if self.isMuted != participantModel.isMuted { self.isMuted = participantModel.isMuted && participantModel.status == .connected } let isOnHold = participantModel.status == .hold if self.isHold != isOnHold { self.isHold = isOnHold postParticipantStatusAccessibilityAnnouncements(isHold: self.isHold, participantModel: participantModel) } } func updateParticipantNameIfNeeded(with renderDisplayName: String?) { let isDisplayConnecting = ParticipantGridCellViewModel.isOutgoingCallDialingInProgress( callType: callType, participantStatus: participantStatus) if isDisplayConnecting { self.participantName = localizationProvider.getLocalizedString(LocalizationKey.callingCallMessage) self.displayName = self.participantName self.renderDisplayName = renderDisplayName self.avatarDisplayName = renderDisplayName return } self.renderDisplayName = renderDisplayName guard renderDisplayName != displayName else { return } let name: String if let renderDisplayName = renderDisplayName { let isRendererNameEmpty = renderDisplayName.trimmingCharacters(in: .whitespaces).isEmpty name = isRendererNameEmpty ? participantName : renderDisplayName } else { name = participantName } self.displayName = name self.avatarDisplayName = displayName } func getOnHoldString() -> String { localizationProvider.getLocalizedString(.onHold) } private func getAccessibilityLabel(participantModel: ParticipantInfoModel) -> String { let status = participantModel.status == .hold ? getOnHoldString() : localizationProvider.getLocalizedString(participantModel.isSpeaking ? .speaking : participantModel.isMuted ? .muted : .unmuted) let videoStatus = (videoViewModel?.videoStreamId?.isEmpty ?? true) ? localizationProvider.getLocalizedString(.videoOff) : localizationProvider.getLocalizedString(.videoOn) return localizationProvider.getLocalizedString(.participantInformationAccessibilityLable, participantModel.displayName, status, videoStatus) } private func getDisplayingVideoStreamModel(_ participantModel: ParticipantInfoModel) -> ParticipantVideoViewInfoModel { let screenShareVideoStreamIdentifier = participantModel.screenShareVideoStreamModel?.videoStreamIdentifier let cameraVideoStreamIdentifier = isCameraEnabled ? participantModel.cameraVideoStreamModel?.videoStreamIdentifier : nil let screenShareVideoStreamType = participantModel.screenShareVideoStreamModel?.mediaStreamType let cameraVideoStreamType = participantModel.cameraVideoStreamModel?.mediaStreamType return screenShareVideoStreamIdentifier != nil ? ParticipantVideoViewInfoModel(videoStreamType: screenShareVideoStreamType, videoStreamId: screenShareVideoStreamIdentifier) : ParticipantVideoViewInfoModel(videoStreamType: cameraVideoStreamType, videoStreamId: cameraVideoStreamIdentifier) } private static func isOutgoingCallDialingInProgress(callType: CompositeCallType, participantStatus: ParticipantStatus?) -> Bool { return callType == .oneToNOutgoing && (participantStatus == nil || participantStatus == .connecting || participantStatus == .ringing) } private func postParticipantStatusAccessibilityAnnouncements(isHold: Bool, participantModel: ParticipantInfoModel) { let holdResumeAccessibilityAnnouncement = isHold ? localizationProvider.getLocalizedString(.onHoldAccessibilityLabel, participantModel.displayName) : localizationProvider.getLocalizedString(.participantResumeAccessibilityLabel, participantModel.displayName) accessibilityProvider.postQueuedAnnouncement(holdResumeAccessibilityAnnouncement) } }