diff --git a/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs b/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs index dd454d97..1a127000 100644 --- a/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs +++ b/PepperDashEssentials/UI/JoinConstants/UIBoolJoin.cs @@ -49,6 +49,16 @@ namespace PepperDash.Essentials /// 1006 /// public const uint CallEndAllConfirmVisible = 1006; + /// + /// 1007 + /// + public const uint MeetingPasswordVisible = 1007; + /// + /// 1008 + /// + public const uint MeetingLeavePress = 1008; + + @@ -153,6 +163,11 @@ namespace PepperDash.Essentials public const uint VCFavoriteVisibleStart = 1221; // RANGE IN USE public const uint VCFavoriteVisibleEnd = 1225; + + /// + /// 1230 + /// + public const uint VCStagingMeetNowPress = 1230; /// /// 1231 /// @@ -752,10 +767,10 @@ namespace PepperDash.Essentials /// 15044 Close button for source modal overlay /// public const uint SourceBackgroundOverlayClosePress = 15044; - ///// - ///// 15045 - Visibility for the bar containing call navigation button list - ///// - //public const uint CallStagingBarVisible = 15045; + /// + /// 15045 + /// + public const uint ZoomRoomContentSharingVisible = 15045; /// /// 15046 /// @@ -844,6 +859,11 @@ namespace PepperDash.Essentials /// 15067 /// public const uint NotificationRibbonVisible = 15067; + /// + /// 15068 + /// + public const uint HeaderMeetingInfoVisible = 15068; + /// /// 15083 - Press for Call help desk on AC/VC /// diff --git a/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs b/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs index 67a5c6e2..38a86fbb 100644 --- a/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs +++ b/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs @@ -27,6 +27,28 @@ namespace PepperDash.Essentials /// 1004 /// public const uint CallSharedSourceNameText = 1004; + /// + /// 1005 + /// + public const uint MeetingIdText = 1005; + /// + /// 1006 + /// + public const uint MeetingHostText = 1006; + /// + /// 1007 + /// + public const uint MeetingPasswordText = 1007; + /// + /// 1008 + /// + public const uint MeetingLeaveText = 1008; + /// + /// 1009 + /// + public const uint MeetingNameText = 1009; + + /// diff --git a/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs b/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs index 818498d7..8cff13c2 100644 --- a/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs +++ b/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs @@ -164,7 +164,7 @@ namespace PepperDash.Essentials CallCaretVisible = tempJoin + 10; TriList.SetSigFalseAction(tempJoin, () => { - avDriver.ShowActiveCallsList(); + avDriver.ShowActiveCallsListOrMeetingInfo(); if(avDriver.CurrentRoom.InCallFeedback.BoolValue) CaretInterlock.ShowInterlocked(CallCaretVisible); }); @@ -256,7 +256,7 @@ namespace PepperDash.Essentials TriList.SetSigFalseAction(UIBoolJoin.HeaderCallStatusLabelPress, () => { - avDriver.ShowActiveCallsList(); + avDriver.ShowActiveCallsListOrMeetingInfo(); if (avDriver.CurrentRoom.InCallFeedback.BoolValue) CaretInterlock.ShowInterlocked(CallCaretVisible); }); @@ -354,6 +354,8 @@ namespace PepperDash.Essentials headerPopupShown = true; else if (e.NewJoin == UIBoolJoin.HeaderActiveCallsListVisible) headerPopupShown = true; + else if (e.NewJoin == UIBoolJoin.HeaderMeetingInfoVisible) + headerPopupShown = true; else if (e.NewJoin == UIBoolJoin.HelpPageVisible) headerPopupShown = true; else if (e.NewJoin == UIBoolJoin.MeetingsOrContacMethodsListVisible) diff --git a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs index 2d5bd1dd..09443d45 100644 --- a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs +++ b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs @@ -14,6 +14,7 @@ using PepperDash.Essentials.Core.PageManagers; using PepperDash.Essentials.Room.Config; using PepperDash.Essentials.Devices.Common.Codec; using PepperDash.Essentials.Devices.Common.VideoCodec; +using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; namespace PepperDash.Essentials { @@ -99,6 +100,9 @@ namespace PepperDash.Essentials /// public SubpageReferenceList MeetingOrContactMethodModalSrl { get; set; } + public uint CallListOrMeetingInfoPopoverVisibilityJoin { get; private set; } + + /// /// The list of buttons on the header. Managed with visibility only /// @@ -177,6 +181,16 @@ namespace PepperDash.Essentials private UiDisplayMode _currentMode; + private bool _isZoomRoomWithNoExternalSources + { + get + { + return CurrentRoom.VideoCodec is Essentials.Devices.Common.VideoCodec.ZoomRoom.ZoomRoom && _sourceListCount <= 1; + } + } + + private uint _sourceListCount; + /// /// The mode showing. Presentation or call. /// @@ -351,15 +365,17 @@ namespace PepperDash.Essentials /// /// Allows PopupInterlock to be toggled if the calls list is already visible, or if the codec is in a call /// - public void ShowActiveCallsList() + public void ShowActiveCallsListOrMeetingInfo() { TriList.SetBool(UIBoolJoin.CallEndAllConfirmVisible, true); - if(PopupInterlock.CurrentJoin == UIBoolJoin.HeaderActiveCallsListVisible) - PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.HeaderActiveCallsListVisible); + + + if(PopupInterlock.CurrentJoin == CallListOrMeetingInfoPopoverVisibilityJoin) + PopupInterlock.ShowInterlockedWithToggle(CallListOrMeetingInfoPopoverVisibilityJoin); else { - if((CurrentRoom.ScheduleSource as VideoCodecBase).IsInCall) - PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.HeaderActiveCallsListVisible); + if(CurrentRoom.VideoCodec.IsInCall) + PopupInterlock.ShowInterlockedWithToggle(CallListOrMeetingInfoPopoverVisibilityJoin); } } @@ -641,9 +657,24 @@ namespace PepperDash.Essentials TriList.SetBool(StartPageVisibleJoin, startMode ? true : false); - TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, presentationMode ? true : false); + if (presentationMode &&_isZoomRoomWithNoExternalSources) + { + // For now, if this is a Zoom Room and there are no shareable sources just display the informational subpage + TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, false); + TriList.SetBool(UIBoolJoin.ZoomRoomContentSharingVisible, true); + } + else + { + // Otherwise, show the staging bar + TriList.SetBool(UIBoolJoin.ZoomRoomContentSharingVisible, false); + TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, presentationMode ? true : false); + + } if (!presentationMode) + { + TriList.SetBool(UIBoolJoin.ZoomRoomContentSharingVisible, false); TriList.SetBool(UIBoolJoin.SelectASourceVisible, false); + } CallButtonSig.BoolValue = callMode && CurrentRoom.ShutdownType == eShutdownType.None; @@ -681,25 +712,39 @@ namespace PepperDash.Essentials if (VCDriver.IsVisible) VCDriver.Hide(); HideNextMeetingPopup(); - // Run default source when room is off and share is pressed - if (!CurrentRoom.OnFeedback.BoolValue) - { - // If there's no default, show UI elements - if (!(CurrentRoom as IRunDefaultPresentRoute).RunDefaultPresentRoute()) - TriList.SetBool(UIBoolJoin.SelectASourceVisible, true); - } - else // room is on show what's active or select a source if nothing is yet active + + + if (_isZoomRoomWithNoExternalSources) { - if(CurrentRoom.CurrentSourceInfo == null || (CurrentRoom.VideoCodec != null && CurrentRoom.CurrentSourceInfo.SourceDevice.Key == CurrentRoom.VideoCodec.OsdSource.Key)) - TriList.SetBool(UIBoolJoin.SelectASourceVisible, true); - else if (CurrentSourcePageManager != null) + (CurrentRoom as IRunDefaultPresentRoute).RunDefaultPresentRoute(); + // For now, if this is a Zoom Room and there are no shareable sources just display the informational subpage + TriList.SetBool(UIBoolJoin.ZoomRoomContentSharingVisible, true); + + if (CurrentSourcePageManager != null) + CurrentSourcePageManager.Hide(); + } + else + { + // Run default source when room is off and share is pressed + if (!CurrentRoom.OnFeedback.BoolValue) { - TriList.SetBool(UIBoolJoin.SelectASourceVisible, false); - CurrentSourcePageManager.Show(); + // If there's no default, show UI elements + if (!(CurrentRoom as IRunDefaultPresentRoute).RunDefaultPresentRoute()) + TriList.SetBool(UIBoolJoin.SelectASourceVisible, true); } + else // room is on show what's active or select a source if nothing is yet active + { + if (CurrentRoom.CurrentSourceInfo == null || (CurrentRoom.VideoCodec != null && CurrentRoom.CurrentSourceInfo.SourceDevice.Key == CurrentRoom.VideoCodec.OsdSource.Key)) + TriList.SetBool(UIBoolJoin.SelectASourceVisible, true); + else if (CurrentSourcePageManager != null) + { + TriList.SetBool(UIBoolJoin.SelectASourceVisible, false); + CurrentSourcePageManager.Show(); + } + } + SetupSourceList(); } CurrentMode = UiDisplayMode.Presentation; - SetupSourceList(); } /// @@ -738,7 +783,7 @@ namespace PepperDash.Essentials /// void ShowCurrentSource() { - if (CurrentRoom.CurrentSourceInfo == null) + if (CurrentRoom.CurrentSourceInfo == null || _isZoomRoomWithNoExternalSources) return; CurrentMode = UiDisplayMode.Presentation; @@ -948,6 +993,18 @@ namespace PepperDash.Essentials _CurrentRoom.IsWarmingUpFeedback.OutputChange -= CurrentRoom_IsWarmingFeedback_OutputChange; _CurrentRoom.IsCoolingDownFeedback.OutputChange -= CurrentRoom_IsCoolingDownFeedback_OutputChange; _CurrentRoom.InCallFeedback.OutputChange -= CurrentRoom_InCallFeedback_OutputChange; + + var scheduleAwareCodec = _CurrentRoom.VideoCodec as IHasScheduleAwareness; + if (scheduleAwareCodec != null) + { + scheduleAwareCodec.CodecSchedule.MeetingsListHasChanged -= CodecSchedule_MeetingsListHasChanged; + } + + var meetingInfoCodec = _CurrentRoom.VideoCodec as IHasMeetingInfo; + if (meetingInfoCodec != null) + { + meetingInfoCodec.MeetingInfoChanged -= meetingInfoCodec_MeetingInfoChanged; + } } _CurrentRoom = room; @@ -980,9 +1037,23 @@ namespace PepperDash.Essentials _CurrentRoom.CurrentSourceChange += CurrentRoom_SourceInfoChange; RefreshSourceInfo(); - if (_CurrentRoom.VideoCodec is IHasScheduleAwareness) + + var scheduleAwareCodec = _CurrentRoom.VideoCodec as IHasScheduleAwareness; + if (scheduleAwareCodec != null) { - (_CurrentRoom.VideoCodec as IHasScheduleAwareness).CodecSchedule.MeetingsListHasChanged += CodecSchedule_MeetingsListHasChanged; + scheduleAwareCodec.CodecSchedule.MeetingsListHasChanged += CodecSchedule_MeetingsListHasChanged; + } + + var meetingInfoCodec = _CurrentRoom.VideoCodec as IHasMeetingInfo; + if (meetingInfoCodec != null) + { + meetingInfoCodec.MeetingInfoChanged += new EventHandler(meetingInfoCodec_MeetingInfoChanged); + + CallListOrMeetingInfoPopoverVisibilityJoin = UIBoolJoin.HeaderMeetingInfoVisible; + } + else + { + CallListOrMeetingInfoPopoverVisibilityJoin = UIBoolJoin.HeaderActiveCallsListVisible; } CallSharingInfoVisibleFeedback = new BoolFeedback(() => _CurrentRoom.VideoCodec.SharingContentIsOnFeedback.BoolValue); @@ -994,7 +1065,8 @@ namespace PepperDash.Essentials if (_CurrentRoom != null) _CurrentRoom.CurrentSourceChange += new SourceInfoChangeHandler(CurrentRoom_CurrentSingleSourceChange); - TriList.SetSigFalseAction(UIBoolJoin.CallStopSharingPress, () => _CurrentRoom.RunRouteAction("codecOsd", _CurrentRoom.SourceListKey)); + // Moved to EssentialsVideoCodecUiDriver + //TriList.SetSigFalseAction(UIBoolJoin.CallStopSharingPress, () => _CurrentRoom.RunRouteAction("codecOsd", _CurrentRoom.SourceListKey)); (Parent as EssentialsPanelMainInterfaceDriver).HeaderDriver.SetupHeaderButtons(this, CurrentRoom); } @@ -1005,6 +1077,21 @@ namespace PepperDash.Essentials } } + void meetingInfoCodec_MeetingInfoChanged(object sender, MeetingInfoEventArgs e) + { + TriList.SetString(UIStringJoin.MeetingIdText, e.Info.Id); + TriList.SetString(UIStringJoin.MeetingHostText, e.Info.Host); + TriList.SetString(UIStringJoin.MeetingNameText, e.Info.Name); + + TriList.SetString(UIStringJoin.MeetingPasswordText, e.Info.Password); + // Show the password fields if one is present + TriList.SetBool(UIBoolJoin.MeetingPasswordVisible, string.IsNullOrEmpty(e.Info.Password) ? false : true); + + TriList.SetString(UIStringJoin.CallSharedSourceNameText, e.Info.ShareStatus); + + TriList.SetString(UIStringJoin.MeetingLeaveText, e.Info.IsHost ? "End Meeting" : "Leave Meeting"); + } + void SetCurrentRoom(IEssentialsHuddleVtc1Room room) { if (_CurrentRoom == room) return; @@ -1118,7 +1205,8 @@ namespace PepperDash.Essentials Debug.Console(1, "**** KEY {0}", kvp.Key); } - SourceStagingSrl.Count = (ushort)(i - 1); + _sourceListCount = (i - 1); + SourceStagingSrl.Count = (ushort)_sourceListCount; } } @@ -1514,6 +1602,8 @@ namespace PepperDash.Essentials /// void PrepareForCodecIncomingCall(); + uint CallListOrMeetingInfoPopoverVisibilityJoin { get; } + SubpageReferenceList MeetingOrContactMethodModalSrl { get; } } } diff --git a/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs b/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs index f3f707c3..7ec44346 100644 --- a/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs +++ b/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs @@ -15,6 +15,7 @@ using PepperDash.Essentials.Core.SmartObjects; using PepperDash.Essentials.Core.Touchpanels.Keyboards; using PepperDash.Essentials.Devices.Common.Codec; using PepperDash.Essentials.Devices.Common.VideoCodec; +using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; using PepperDash.Essentials.Devices.Common.Cameras; namespace PepperDash.Essentials.UIDrivers.VC @@ -127,12 +128,6 @@ namespace PepperDash.Essentials.UIDrivers.VC codec.CallStatusChange += new EventHandler(Codec_CallStatusChange); - // If the codec is ready, then get the values we want, otherwise wait - if (Codec.IsReady) - Codec_IsReady(); - else - codec.IsReadyChange += (o, a) => Codec_IsReady(); - //InCall = new BoolFeedback(() => false); LocalPrivacyIsMuted = new BoolFeedback(() => false); @@ -157,7 +152,8 @@ namespace PepperDash.Essentials.UIDrivers.VC // Return formatted when dialing, straight digits when in call DialStringFeedback = new StringFeedback(() => { - if (KeypadMode == eKeypadMode.Dial) + // Format the number feedback if in dial mode and the codec is not IHasStartMeeting (ZoomRoom) + if (KeypadMode == eKeypadMode.Dial && !(Codec is IHasStartMeeting)) return GetFormattedDialString(DialStringBuilder.ToString()); else return DialStringBuilder.ToString(); @@ -223,6 +219,12 @@ namespace PepperDash.Essentials.UIDrivers.VC SetupPasswordPrompt(); } + + // If the codec is ready, then get the values we want, otherwise wait + if (Codec.IsReady) + Codec_IsReady(); + else + codec.IsReadyChange += (o, a) => Codec_IsReady(); } catch (Exception e) { @@ -422,8 +424,8 @@ namespace PepperDash.Essentials.UIDrivers.VC ActiveCallsSRL.Count = (ushort)activeList.Count; // If Active Calls list is visible and codec is not in a call, hide the list - if (!Codec.IsInCall && Parent.PopupInterlock.CurrentJoin == UIBoolJoin.HeaderActiveCallsListVisible) - Parent.PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.HeaderActiveCallsListVisible); + if (!Codec.IsInCall && Parent.PopupInterlock.CurrentJoin == Parent.CallListOrMeetingInfoPopoverVisibilityJoin) + Parent.PopupInterlock.ShowInterlockedWithToggle(Parent.CallListOrMeetingInfoPopoverVisibilityJoin); } /// @@ -514,20 +516,46 @@ namespace PepperDash.Essentials.UIDrivers.VC TriList.SetSigFalseAction(UIBoolJoin.VCStagingRecentsPress, ShowRecents); TriList.SetSigFalseAction(UIBoolJoin.VCStagingCameraPress, ShowCameraControls); TriList.SetSigFalseAction(UIBoolJoin.VCStagingConnectPress, ConnectPress); + TriList.SetSigFalseAction(UIBoolJoin.VCStagingMeetNowPress, MeetNowPress); + TriList.SetSigFalseAction(UIBoolJoin.CallStopSharingPress, CallStopSharingPress); + TriList.SetSigFalseAction(UIBoolJoin.CallEndPress, () => { if (Codec.ActiveCalls.Count > 1) { - Parent.PopupInterlock.ShowInterlocked(UIBoolJoin.HeaderActiveCallsListVisible); + Parent.PopupInterlock.ShowInterlocked(Parent.CallListOrMeetingInfoPopoverVisibilityJoin); } else Codec.EndAllCalls(); }); + TriList.SetSigFalseAction(UIBoolJoin.CallEndAllConfirmPress, () => { Parent.PopupInterlock.HideAndClear(); Codec.EndAllCalls(); }); + + var meetingInfoCodec = Codec as IHasMeetingInfo; + if (meetingInfoCodec != null) + { + TriList.SetSigFalseAction(UIBoolJoin.MeetingLeavePress, () => + { + Parent.PopupInterlock.HideAndClear(); + + if (meetingInfoCodec.MeetingInfo.IsHost) + { + Codec.EndAllCalls(); + } + else + { + var startMeetingCodec = Codec as IHasStartMeeting; + if (startMeetingCodec != null) + { + startMeetingCodec.LeaveMeeting(); + } + } + }); + } } void SetupCameraControls() @@ -1566,6 +1594,22 @@ namespace PepperDash.Essentials.UIDrivers.VC StagingButtonsFeedbackInterlock.ShowInterlocked(UIBoolJoin.VCStagingRecentsPress); } + /// + /// Meet Now button + /// + void MeetNowPress() + { + var startMeetingCodec = Codec as IHasStartMeeting; + if (startMeetingCodec != null) + { + startMeetingCodec.StartMeeting(startMeetingCodec.DefaultMeetingDurationMin); + } + else + { + Debug.Console(2, "Codce does not implment IHasStartMeeting. Cannot meet now"); + } + } + /// /// Connect call button /// @@ -1576,6 +1620,16 @@ namespace PepperDash.Essentials.UIDrivers.VC Codec.Dial(DialStringBuilder.ToString()); } + /// + /// Stop Sharing button + /// + void CallStopSharingPress() + { + Codec.StopSharing(); + Parent.CurrentRoom.RunRouteAction("codecOsd", Parent.CurrentRoom.SourceListKey); + } + + /// /// /// diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj index 75d01b0f..cff1e4c0 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj @@ -120,6 +120,7 @@ + diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingInfo.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingInfo.cs new file mode 100644 index 00000000..236fde80 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingInfo.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using Newtonsoft.Json; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces +{ + /// + /// Describes a device that provides meeting information (like a ZoomRoom) + /// + public interface IHasMeetingInfo + { + event EventHandler MeetingInfoChanged; + + MeetingInfo MeetingInfo { get; } + } + + /// + /// Represents the information about a meeting in progress + /// Currently used for Zoom meetings + /// + public class MeetingInfo + { + [JsonProperty("id")] + public string Id { get; private set; } + [JsonProperty("name")] + public string Name { get; private set; } + [JsonProperty("host")] + public string Host { get; private set; } + [JsonProperty("password")] + public string Password { get; private set; } + [JsonProperty("shareStatus")] + public string ShareStatus { get; private set; } + [JsonProperty("isHost")] + public Boolean IsHost { get; private set; } + + public MeetingInfo(string id, string name, string host, string password, string shareStatus, bool isHost) + { + Id = id; + Name = name; + Host = host; + Password = password; + ShareStatus = shareStatus; + IsHost = isHost; + } + } + + public class MeetingInfoEventArgs : EventArgs + { + public MeetingInfo Info { get; private set; } + + public MeetingInfoEventArgs(MeetingInfo info) + { + Info = info; + } + + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasParticipants.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasParticipants.cs index d8c0c55c..409ccd89 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasParticipants.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasParticipants.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Collections.Generic; using PepperDash.Core; using PepperDash.Essentials.Core; @@ -60,6 +61,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces } } + public Participant Host + { + get + { + return _currentParticipants.FirstOrDefault(p => p.IsHost); + } + } + public event EventHandler ParticipantsListHasChanged; public CodecParticipants() @@ -84,6 +93,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces { public int UserId { get; set; } public bool IsHost { get; set; } + public bool IsMyself { get; set; } public string Name { get; set; } public bool CanMuteVideo { get; set; } public bool CanUnmuteVideo { get; set; } diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasStartMeeting.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasStartMeeting.cs index a01ec099..6af59534 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasStartMeeting.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasStartMeeting.cs @@ -21,5 +21,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces /// /// void StartMeeting(uint duration); + + /// + /// Leaves a meeting without ending it + /// + void LeaveMeeting(); } } \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVC.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVC.cs index ca199957..78302fcc 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVC.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/MockVC/MockVC.cs @@ -78,6 +78,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec SetupCameras(); + CreateOsdSource(); + SetIsReady(); } @@ -117,6 +119,19 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec } bool _StandbyIsOn; + /// + /// Creates the fake OSD source, and connects it's AudioVideo output to the CodecOsdIn input + /// to enable routing + /// + private void CreateOsdSource() + { + OsdSource = new DummyRoutingInputsDevice(Key + "[osd]"); + DeviceManager.AddDevice(OsdSource); + var tl = new TieLine(OsdSource.AudioVideoOutputPort, CodecOsdIn); + TieLineCollection.Default.Add(tl); + + //foreach(var input in Status.Video. + } /// /// Dials, yo! @@ -567,6 +582,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec void SetupCameras() { + SupportsCameraAutoMode = true; + + SupportsCameraOff = false; + Cameras = new List(); var internalCamera = new MockVCCamera(Key + "-camera1", "Near End", this); diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs index 28073eb8..aac45f51 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs @@ -431,6 +431,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { private string _dispState; private string _password; + private bool _isAirHostClientConnected; + private bool _isSharingBlackMagic; + private bool _isDirectPresentationConnected; + public string directPresentationPairingCode { get; set; } /// @@ -452,11 +456,51 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } } } - public bool isAirHostClientConnected { get; set; } + + public bool isAirHostClientConnected + { + get { return _isAirHostClientConnected; } + set + { + if (value != _isAirHostClientConnected) + { + _isAirHostClientConnected = value; + NotifyPropertyChanged("isAirHostClientConnected"); + } + } + } + public bool isBlackMagicConnected { get; set; } public bool isBlackMagicDataAvailable { get; set; } - public bool isDirectPresentationConnected { get; set; } - public bool isSharingBlackMagic { get; set; } + + public bool isDirectPresentationConnected + { + get { return _isDirectPresentationConnected; } + set + { + if (value != _isDirectPresentationConnected) + { + _isDirectPresentationConnected = value; + NotifyPropertyChanged("isDirectPresentationConnected"); + } + } + } + + public bool isSharingBlackMagic + { + get { return _isSharingBlackMagic; } + set + { + if (value != _isSharingBlackMagic) + { + _isSharingBlackMagic = value; + NotifyPropertyChanged("isSharingBlackMagic"); + } + } + } + + + /// /// IOS Airplay code /// @@ -779,7 +823,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom private bool _paused; private eSharingState _state; - public bool IsSharing; + public bool IsSharing { get; private set; } [JsonProperty("paused")] public bool Paused @@ -1445,6 +1489,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom UserId = p.UserId, Name = p.UserName, IsHost = p.IsHost, + IsMyself = p.IsMyself, CanMuteVideo = p.IsVideoCanMuteByHost, CanUnmuteVideo = p.IsVideoCanUnmuteByHost, AudioMuteFb = p.AudioStatusState == "AUDIO_MUTED", diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs index 1b1a9629..2b4685a2 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs @@ -25,7 +25,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom IRouting, IHasScheduleAwareness, IHasCodecCameras, IHasParticipants, IHasCameraOff, IHasCameraMute, IHasCameraAutoMode, IHasFarEndContentStatus, IHasSelfviewPosition, IHasPhoneDialing, IHasZoomRoomLayouts, IHasParticipantPinUnpin, - IHasParticipantAudioMute, IHasSelfviewSize, IPasswordPrompt, IHasStartMeeting + IHasParticipantAudioMute, IHasSelfviewSize, IPasswordPrompt, IHasStartMeeting, IHasMeetingInfo { private const long MeetingRefreshTimer = 60000; public uint DefaultMeetingDurationMin { get; private set; } @@ -470,6 +470,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { SharingContentIsOnFeedback.FireUpdate(); ReceivingContent.FireUpdate(); + + // Update the share status of the meeting info + var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, Participants.Host.Name, MeetingInfo.Password, GetSharingStatus(), GetIsHostMyself()); + MeetingInfo = meetingInfo; } }; @@ -618,6 +622,15 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom break; case "password": break; + case "isAirHostClientConnected": + case "isDirectPresentationConnected": + case "isSharingBlackMagic": + { + // Update the share status of the meeting info + var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, MeetingInfo.Host, MeetingInfo.Password, GetSharingStatus(), GetIsHostMyself()); + MeetingInfo = meetingInfo; + break; + } } }; @@ -966,9 +979,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { _syncState.LoginMessageReceived(); + // Fire up a thread to send the intial commands. CrestronInvoke.BeginInvoke(o => { + // Currently the feedback exclusions don't work when using the API in JSON response mode + // But leave these here in case the API gets updated in the future + + Thread.Sleep(100); // disable echo of commands SendText("echo off"); @@ -1188,6 +1206,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom Status.Call.Participants); Participants.CurrentParticipants = participants; + + // Update the share status of the meeting info + var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, Participants.Host.Name, MeetingInfo.Password, MeetingInfo.ShareStatus, GetIsHostMyself()); + MeetingInfo = meetingInfo; + PrintCurrentCallParticipants(); break; @@ -1651,7 +1674,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom // If not crrently in a meeting, intialize the call object if (callStatus != zStatus.eCallStatus.IN_MEETING && callStatus != zStatus.eCallStatus.CONNECTING_MEETING) { - Debug.Console(1, this, "[UpdateCallStatus] Creating new Status.Call object"); + //Debug.Console(1, this, "[UpdateCallStatus] Creating new Status.Call object"); Status.Call = new zStatus.Call {Status = callStatus}; OnCallStatusChange(new CodecActiveCallItem() {Status = eCodecCallStatus.Disconnected}); @@ -1689,15 +1712,15 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom ActiveCalls.Add(newCall); - Debug.Console(1, this, "[UpdateCallStatus] IF w/ meeting_id AcitveCalls.Count == {1} - Current Call Status: {0}", - Status.Call != null ? Status.Call.Status.ToString() : "no call", ActiveCalls.Count); + //Debug.Console(1, this, "[UpdateCallStatus] IF w/ meeting_id AcitveCalls.Count == {1} - Current Call Status: {0}", + //Status.Call != null ? Status.Call.Status.ToString() : "no call", ActiveCalls.Count); OnCallStatusChange(newCall); } else { - Debug.Console(1, this, "[UpdateCallStatus] IF w/o meeting_id AcitveCalls.Count == {1} - Current Call Status: {0}", - Status.Call != null ? Status.Call.Status.ToString() : "no call", ActiveCalls.Count); + //Debug.Console(1, this, "[UpdateCallStatus] IF w/o meeting_id AcitveCalls.Count == {1} - Current Call Status: {0}", + //Status.Call != null ? Status.Call.Status.ToString() : "no call", ActiveCalls.Count); } } } @@ -1757,6 +1780,24 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { base.OnCallStatusChange(item); + if (item.Status == eCodecCallStatus.Connected) + { + + var host = ""; + + if (Participants.Host != null) + host = Participants.Host.Name; + + MeetingInfo = new MeetingInfo( + Status.Call.Info.meeting_id, + Status.Call.Info.meeting_list_item.meetingName, + host, + Status.Call.Info.meeting_password, + GetSharingStatus(), + GetIsHostMyself() + ); + } + Debug.Console(1, this, "[OnCallStatusChange] Current Call Status: {0}", Status.Call != null ? Status.Call.Status.ToString() : "no call"); @@ -1766,6 +1807,47 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } } + private string GetSharingStatus() + { + string sharingState = "None"; + + if (Status.Call.Sharing.State == zEvent.eSharingState.Receiving) + { + sharingState = "Receiving Content"; + } + if (Status.Sharing.isAirHostClientConnected) + { + sharingState = "Sharing AirPlay"; + } + if (Status.Sharing.isDirectPresentationConnected) + { + sharingState = "Sharing Laptop"; + } + if (Status.Sharing.isSharingBlackMagic) + { + sharingState = "Sharing HDMI Source"; + } + + return sharingState; + } + + /// + /// Will return true if the host is myself (this zoom room) + /// + /// + private bool GetIsHostMyself() + { + var host = Participants.Host; + + if(host == null) + { + Debug.Console(2, this, "Host is currently null"); + return false; + } + Debug.Console(2, this, "Host is: '{0}' IsMyself?: {1}", host.Name, host.IsMyself); + return host.IsMyself; + } + public override void StartSharing() { SendText("zCommand Call Sharing HDMI Start"); @@ -2122,6 +2204,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom SendText(string.Format("zCommand Dial StartPmi Duration: {0}", dur)); } + public void LeaveMeeting() + { + SendText("zCommand Call Leave"); + } + public override void EndCall(CodecActiveCallItem call) { SendText("zCommand Call Disconnect"); @@ -2746,6 +2833,32 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } #endregion + + #region IHasMeetingInfo Members + + public event EventHandler MeetingInfoChanged; + + private MeetingInfo _meetingInfo; + + public MeetingInfo MeetingInfo + { + get { return _meetingInfo; } + private set + { + if (value != _meetingInfo) + { + _meetingInfo = value; + + var handler = MeetingInfoChanged; + if (handler != null) + { + handler(this, new MeetingInfoEventArgs(_meetingInfo)); + } + } + } + } + + #endregion } ///