diff --git a/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs b/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs index 38a86fbb..6741be35 100644 --- a/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs +++ b/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs @@ -48,6 +48,11 @@ namespace PepperDash.Essentials /// public const uint MeetingNameText = 1009; + /// + /// 1240 - Used to determine text for meeting start button + /// + public const uint MeetingStartButtonText = 1240; + diff --git a/PepperDashEssentials/UI/JoinConstants/UIUshortJoin.cs b/PepperDashEssentials/UI/JoinConstants/UIUshortJoin.cs index ffc7f19e..73356bf3 100644 --- a/PepperDashEssentials/UI/JoinConstants/UIUshortJoin.cs +++ b/PepperDashEssentials/UI/JoinConstants/UIUshortJoin.cs @@ -7,7 +7,7 @@ { // Video Codec /// - /// 1234: values 0 = Connect, 1 = End + /// 1234: values 0 = Connect, 1 = End, 2 = Start Meeting /// public const uint VCStagingConnectButtonMode = 1234; diff --git a/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs b/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs index 8cff13c2..c63af9d5 100644 --- a/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs +++ b/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs @@ -12,6 +12,7 @@ using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.SmartObjects; using PepperDash.Essentials.Core.PageManagers; +using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; using PepperDash.Essentials.Room.Config; using PepperDash.Essentials.Devices.Common.Codec; using PepperDash.Essentials.Devices.Common.VideoCodec; @@ -191,32 +192,56 @@ namespace PepperDash.Essentials return; } + var meetingInfoCodec = codec as IHasMeetingInfo; + // Set mode of header button + SetHeaderCallIcon(codec); + + // Set the call status text + Debug.Console(1, "Active Call Count: {0}", codec.ActiveCalls.Count); + + if (codec.ActiveCalls.Count > 0) + { + if (codec.ActiveCalls.Count == 1 && meetingInfoCodec == null) + TriList.SetString(UIStringJoin.HeaderCallStatusLabel, "1 Active Call"); + else if (codec.ActiveCalls.Count == 1 && meetingInfoCodec != null) + { + var headerCallStatusLabel = meetingInfoCodec.MeetingInfo.IsSharingMeeting + ? "Sharing-Only Meeting" + : "Active Meeting"; + + headerCallStatusLabel = meetingInfoCodec.MeetingInfo.WaitingForHost + ? "Waiting For Host" + : headerCallStatusLabel; + + TriList.SetString(UIStringJoin.HeaderCallStatusLabel, headerCallStatusLabel); + } + else if (codec.ActiveCalls.Count > 1) + TriList.SetString(UIStringJoin.HeaderCallStatusLabel, string.Format("{0} Active Calls", codec.ActiveCalls.Count)); + } + else + TriList.SetString(UIStringJoin.HeaderCallStatusLabel, "No Active Calls"); + } + + private void SetHeaderCallIcon(VideoCodecBase codec) + { if (!codec.IsInCall) { HeaderCallButtonIconSig.StringValue = "DND"; //HeaderCallButton.SetIcon(HeaderListButton.OnHook); } else if (codec.ActiveCalls.Any(c => c.Type == eCodecCallType.Video)) + { HeaderCallButtonIconSig.StringValue = "Misc-06_Dark"; - //HeaderCallButton.SetIcon(HeaderListButton.Camera); - //TriList.SetUshort(UIUshortJoin.CallHeaderButtonMode, 2); + } + //HeaderCallButton.SetIcon(HeaderListButton.Camera); + //TriList.SetUshort(UIUshortJoin.CallHeaderButtonMode, 2); else + { HeaderCallButtonIconSig.StringValue = "Misc-09_Dark"; + } //HeaderCallButton.SetIcon(HeaderListButton.Phone); //TriList.SetUshort(UIUshortJoin.CallHeaderButtonMode, 1); - - // Set the call status text - Debug.Console(1, "Active Call Count: {0}", codec.ActiveCalls.Count); - if (codec.ActiveCalls.Count > 0) - { - if (codec.ActiveCalls.Count == 1) - TriList.SetString(UIStringJoin.HeaderCallStatusLabel, "1 Active Call"); - else if (codec.ActiveCalls.Count > 1) - TriList.SetString(UIStringJoin.HeaderCallStatusLabel, string.Format("{0} Active Calls", codec.ActiveCalls.Count)); - } - else - TriList.SetString(UIStringJoin.HeaderCallStatusLabel, "No Active Calls"); } /// diff --git a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs index 09443d45..26bdaee9 100644 --- a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs +++ b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs @@ -9,6 +9,7 @@ using Crestron.SimplSharpPro.UI; using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.DeviceTypeInterfaces; using PepperDash.Essentials.Core.SmartObjects; using PepperDash.Essentials.Core.PageManagers; using PepperDash.Essentials.Room.Config; @@ -471,85 +472,92 @@ namespace PepperDash.Essentials /// void ShowNextMeetingTimerCallback() { + //Update calendar for Zoom. Zoom doesn't automatically update when meetings are in the past + if (_isZoomRoomWithNoExternalSources) + { + CurrentRoom.ScheduleSource.GetSchedule(); + } + // Every 60 seconds, refresh the calendar RefreshMeetingsList(); // check meetings list for the closest, joinable meeting var ss = CurrentRoom.ScheduleSource; var meetings = ss.CodecSchedule.Meetings; - if (meetings.Count > 0) - { - // If the room is off pester the user - // If the room is on, and the meeting is joinable - // and the LastMeetingDismissed != this meeting + if (meetings.Count <= 0) + { + return; + } + // If the room is off pester the user + // If the room is on, and the meeting is joinable + // and the LastMeetingDismissed != this meeting - var lastMeetingDismissed = meetings.FirstOrDefault(m => m.Id == LastMeetingDismissedId); - Debug.Console(0, "*#* Room on: {0}, lastMeetingDismissedId: {1} {2} *#*", - CurrentRoom.OnFeedback.BoolValue, - LastMeetingDismissedId, - lastMeetingDismissed != null ? lastMeetingDismissed.StartTime.ToString("t", Global.Culture) : ""); + var lastMeetingDismissed = meetings.FirstOrDefault(m => m.Id == LastMeetingDismissedId); + Debug.Console(0, "*#* Room on: {0}, lastMeetingDismissedId: {1} {2} *#*", + CurrentRoom.OnFeedback.BoolValue, + LastMeetingDismissedId, + lastMeetingDismissed != null ? lastMeetingDismissed.StartTime.ToString("t", Global.Culture) : ""); - var meeting = meetings.LastOrDefault(m => m.Joinable); - if (CurrentRoom.OnFeedback.BoolValue - && lastMeetingDismissed == meeting) - { - // meeting no longer joinable, hide popup - if(meeting == null) - HideNextMeetingPopup(); + var meeting = meetings.LastOrDefault(m => m.Joinable); + if (CurrentRoom.OnFeedback.BoolValue + && lastMeetingDismissed == meeting) + { + // meeting no longer joinable, hide popup + if(meeting == null) + HideNextMeetingPopup(); - return; - } + return; + } - LastMeetingDismissedId = null; - // Clear the popup when we run out of meetings - if (meeting == null) - { - HideNextMeetingPopup(); - } - else - { + LastMeetingDismissedId = null; + // Clear the popup when we run out of meetings + if (meeting == null) + { + HideNextMeetingPopup(); + } + else + { - TriList.SetString(UIStringJoin.MeetingsOrContactMethodListTitleText, "Upcoming meeting"); - TriList.SetString(UIStringJoin.NextMeetingStartTimeText, meeting.StartTime.ToString("t", Global.Culture)); - TriList.SetString(UIStringJoin.NextMeetingEndTimeText, meeting.EndTime.ToString("t", Global.Culture)); - TriList.SetString(UIStringJoin.NextMeetingTitleText, meeting.Title); - TriList.SetString(UIStringJoin.NextMeetingNameText, meeting.Organizer); - TriList.SetString(UIStringJoin.NextMeetingButtonLabel, "Join"); - TriList.SetSigFalseAction(UIBoolJoin.NextMeetingJoinPress, () => - { - HideNextMeetingPopup(); - PopupInterlock.Hide(); - RoomOnAndDialMeeting(meeting); - }); - TriList.SetString(UIStringJoin.NextMeetingSecondaryButtonLabel, "Show Schedule"); - TriList.SetSigFalseAction(UIBoolJoin.CalendarHeaderButtonPress, () => - { - HideNextMeetingPopup(); - //CalendarPress(); - RefreshMeetingsList(); - PopupInterlock.ShowInterlocked(UIBoolJoin.MeetingsOrContacMethodsListVisible); - }); - var indexOfNext = meetings.IndexOf(meeting) + 1; + TriList.SetString(UIStringJoin.MeetingsOrContactMethodListTitleText, "Upcoming meeting"); + TriList.SetString(UIStringJoin.NextMeetingStartTimeText, meeting.StartTime.ToString("t", Global.Culture)); + TriList.SetString(UIStringJoin.NextMeetingEndTimeText, meeting.EndTime.ToString("t", Global.Culture)); + TriList.SetString(UIStringJoin.NextMeetingTitleText, meeting.Title); + TriList.SetString(UIStringJoin.NextMeetingNameText, meeting.Organizer); + TriList.SetString(UIStringJoin.NextMeetingButtonLabel, "Join"); + TriList.SetSigFalseAction(UIBoolJoin.NextMeetingJoinPress, () => + { + HideNextMeetingPopup(); + PopupInterlock.Hide(); + RoomOnAndDialMeeting(meeting); + }); + TriList.SetString(UIStringJoin.NextMeetingSecondaryButtonLabel, "Show Schedule"); + TriList.SetSigFalseAction(UIBoolJoin.CalendarHeaderButtonPress, () => + { + HideNextMeetingPopup(); + //CalendarPress(); + RefreshMeetingsList(); + PopupInterlock.ShowInterlocked(UIBoolJoin.MeetingsOrContacMethodsListVisible); + }); + var indexOfNext = meetings.IndexOf(meeting) + 1; - // indexOf = 3, 4 meetings : - if (indexOfNext < meetings.Count) - TriList.SetString(UIStringJoin.NextMeetingFollowingMeetingText, - meetings[indexOfNext].StartTime.ToString("t", Global.Culture)); - else - TriList.SetString(UIStringJoin.NextMeetingFollowingMeetingText, "No more meetings today"); + // indexOf = 3, 4 meetings : + if (indexOfNext < meetings.Count) + TriList.SetString(UIStringJoin.NextMeetingFollowingMeetingText, + meetings[indexOfNext].StartTime.ToString("t", Global.Culture)); + else + TriList.SetString(UIStringJoin.NextMeetingFollowingMeetingText, "No more meetings today"); - TriList.SetSigFalseAction(UIBoolJoin.NextMeetingModalClosePress, () => - { - // Mark the meeting to not re-harass the user - if(CurrentRoom.OnFeedback.BoolValue) - LastMeetingDismissedId = meeting.Id; - HideNextMeetingPopup(); - }); + TriList.SetSigFalseAction(UIBoolJoin.NextMeetingModalClosePress, () => + { + // Mark the meeting to not re-harass the user + if(CurrentRoom.OnFeedback.BoolValue) + LastMeetingDismissedId = meeting.Id; + HideNextMeetingPopup(); + }); - TriList.SetBool(UIBoolJoin.NextMeetingModalVisible, true); - } - } + TriList.SetBool(UIBoolJoin.NextMeetingModalVisible, true); + } } /// @@ -575,19 +583,26 @@ namespace PepperDash.Essentials /// void RoomOnAndDialMeeting(Meeting meeting) { + Debug.Console(1, "[RoomOnAndDialMeeting] Joining meeting [{0}]", meeting); Action dialAction = () => { var d = CurrentRoom.ScheduleSource as VideoCodecBase; if (d != null) { + Debug.Console(1, + "[RoomOnAndDialMeeting] [dialAction] Sending command to codec to join meeting {0}", meeting); d.Dial(meeting); LastMeetingDismissedId = meeting.Id; // To prevent prompts for already-joined call } }; - if (CurrentRoom.OnFeedback.BoolValue) - dialAction(); + if (CurrentRoom.OnFeedback.BoolValue) + { + Debug.Console(1, "[RoomOnAndDialMeeting] Room is on."); + dialAction(); + } else - { + { + Debug.Console(1, "RoomOnAndDialMeeting] Room is off or warming. Registering for Warming Feedback"); // Rig a one-time handler to catch when the room is warmed and then dial call EventHandler oneTimeHandler = null; oneTimeHandler = (o, a) => @@ -716,10 +731,32 @@ namespace PepperDash.Essentials if (_isZoomRoomWithNoExternalSources) { - (CurrentRoom as IRunDefaultPresentRoute).RunDefaultPresentRoute(); + if (!CurrentRoom.OnFeedback.BoolValue) + { + CurrentRoom.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); + var presentationMeetingCodec = CurrentRoom.VideoCodec as IHasPresentationOnlyMeeting; + var farEndContentStatusCodec = CurrentRoom.VideoCodec as IHasFarEndContentStatus; + var receivingContent = false; + + if (farEndContentStatusCodec != null) + { + receivingContent = farEndContentStatusCodec.ReceivingContent.BoolValue; + } + + if (presentationMeetingCodec != null && !CurrentRoom.VideoCodec.IsInCall) + { + presentationMeetingCodec.StartSharingOnlyMeeting(eSharingMeetingMode.Laptop); + } + else if (CurrentRoom.VideoCodec.IsInCall && !CurrentRoom.VideoCodec.SharingContentIsOnFeedback.BoolValue && + !receivingContent) + { + CurrentRoom.VideoCodec.StartSharing(); + } + if (CurrentSourcePageManager != null) CurrentSourcePageManager.Hide(); } @@ -734,7 +771,9 @@ namespace PepperDash.Essentials } 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)) + if (CurrentRoom.CurrentSourceInfo == null || + (CurrentRoom.VideoCodec != null && + CurrentRoom.CurrentSourceInfo.SourceDevice.Key == CurrentRoom.VideoCodec.OsdSource.Key)) TriList.SetBool(UIBoolJoin.SelectASourceVisible, true); else if (CurrentSourcePageManager != null) { @@ -1079,13 +1118,18 @@ namespace PepperDash.Essentials void meetingInfoCodec_MeetingInfoChanged(object sender, MeetingInfoEventArgs e) { + if (e.Info == null) + { + return; + } + 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.SetBool(UIBoolJoin.MeetingPasswordVisible, !string.IsNullOrEmpty(e.Info.Password)); TriList.SetString(UIStringJoin.CallSharedSourceNameText, e.Info.ShareStatus); @@ -1295,9 +1339,8 @@ namespace PepperDash.Essentials { // See if this is helpful or if the callback response in the codec class maybe doesn't come it time? // Let's build list from event - // CurrentRoom.ScheduleSource.GetSchedule(); - TriList.SetString(UIStringJoin.MeetingsOrContactMethodListIcon, "Calendar"); + TriList.SetString(UIStringJoin.MeetingsOrContactMethodListIcon, "Calendar"); TriList.SetString(UIStringJoin.MeetingsOrContactMethodListTitleText, "Today's Meetings"); ushort i = 0; diff --git a/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs b/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs index 7ec44346..cc5ac265 100644 --- a/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs +++ b/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs @@ -134,11 +134,12 @@ namespace PepperDash.Essentials.UIDrivers.VC VCControlsInterlock = new JoinedSigInterlock(triList); VCCameraControlModeInterlock = new JoinedSigInterlock(triList); + VCControlsInterlock.HideAndClear(); - if (CodecHasFavorites) + /* if (CodecHasFavorites || codec is IHasZoomRoomLayouts) //Checking for Zoom Room...picked a ZoomRoom specific interface to check for VCControlsInterlock.SetButDontShow(UIBoolJoin.VCKeypadWithFavoritesVisible); else - VCControlsInterlock.SetButDontShow(UIBoolJoin.VCKeypadVisible); + VCControlsInterlock.SetButDontShow(UIBoolJoin.VCKeypadVisible); */ StagingBarsInterlock = new JoinedSigInterlock(triList); if(Codec is IHasCallHistory) @@ -320,25 +321,20 @@ namespace PepperDash.Essentials.UIDrivers.VC void Codec_CallStatusChange(object sender, CodecCallStatusItemChangeEventArgs e) { var call = e.CallItem; + var meetingInfoSender = sender as IHasMeetingInfo; switch (e.CallItem.Status) { case eCodecCallStatus.Connected: // fire at SRL item - HidePasswordPrompt(); - KeypadMode = eKeypadMode.DTMF; - DialStringBuilder.Remove(0, DialStringBuilder.Length); - DialStringFeedback.FireUpdate(); - DialStringTextCheckEnables(); Parent.ShowNotificationRibbon("Connected", 2000); - StagingButtonsFeedbackInterlock.ShowInterlocked(UIBoolJoin.VCStagingKeypadPress); - ShowKeypad(); - ((Parent.CurrentRoom as IHasCurrentVolumeControls).CurrentVolumeControls as IBasicVolumeWithFeedback).MuteOff(); + OnCallConnected(); //VCControlsInterlock.ShowInterlocked(UIBoolJoin.VCKeypadVisible); break; case eCodecCallStatus.Connecting: // fire at SRL item Parent.ShowNotificationRibbon("Connecting", 0); + OnCallConnected(); break; case eCodecCallStatus.Dialing: Parent.ShowNotificationRibbon("Connecting", 0); @@ -354,7 +350,10 @@ namespace PepperDash.Essentials.UIDrivers.VC DialStringBuilder.Remove(0, DialStringBuilder.Length); DialStringFeedback.FireUpdate(); Parent.ShowNotificationRibbon("Disconnected", 2000); + Debug.Console(0, "Setting Connect Button mode to 0"); } + + break; case eCodecCallStatus.Disconnecting: break; @@ -375,12 +374,23 @@ namespace PepperDash.Essentials.UIDrivers.VC ShowIncomingModal(call); break; } - default: - break; } - TriList.UShortInput[UIUshortJoin.VCStagingConnectButtonMode].UShortValue = (ushort)(Codec.IsInCall ? 1 : 0); - - uint stageJoin; + + if (meetingInfoSender != null && Codec.IsInCall) + { + var meetingInfo = meetingInfoSender.MeetingInfo; + + TriList.UShortInput[UIUshortJoin.VCStagingConnectButtonMode].UShortValue = + (ushort) (meetingInfo.IsSharingMeeting ? 2 : 1); + } + else + { + + TriList.UShortInput[UIUshortJoin.VCStagingConnectButtonMode].UShortValue = + (ushort) (Codec.IsInCall ? 1 : 0); + } + + uint stageJoin; if (Codec.IsInCall) stageJoin = UIBoolJoin.VCStagingActivePopoverVisible; else @@ -401,6 +411,36 @@ namespace PepperDash.Essentials.UIDrivers.VC UpdateHeaderActiveCallList(); } + private void OnCallConnected() + { + HidePasswordPrompt(); + KeypadMode = eKeypadMode.DTMF; + DialStringBuilder.Remove(0, DialStringBuilder.Length); + DialStringFeedback.FireUpdate(); + DialStringTextCheckEnables(); + + StagingButtonsFeedbackInterlock.ShowInterlocked(UIBoolJoin.VCStagingKeypadPress); + ShowKeypad(); + + UnmuteRoomOnCallConnect(); + } + + private void UnmuteRoomOnCallConnect() + { + var volControl = Parent.CurrentRoom as IHasCurrentVolumeControls; + + if (volControl == null) + { + return; + } + var currentVolControls = volControl.CurrentVolumeControls as IBasicVolumeWithFeedback; + + if (currentVolControls != null) + { + currentVolControls.MuteOff(); + } + } + /// /// Redraws the calls list on the header /// @@ -519,14 +559,35 @@ namespace PepperDash.Essentials.UIDrivers.VC TriList.SetSigFalseAction(UIBoolJoin.VCStagingMeetNowPress, MeetNowPress); TriList.SetSigFalseAction(UIBoolJoin.CallStopSharingPress, CallStopSharingPress); + var meetingInfoCodec = Codec as IHasMeetingInfo; + TriList.SetSigFalseAction(UIBoolJoin.CallEndPress, () => { if (Codec.ActiveCalls.Count > 1) { Parent.PopupInterlock.ShowInterlocked(Parent.CallListOrMeetingInfoPopoverVisibilityJoin); } + else if (meetingInfoCodec != null && Codec.ActiveCalls.Count == 1) + { + var meetingInfo = meetingInfoCodec.MeetingInfo; + + if (meetingInfo != null && meetingInfo.IsSharingMeeting) + { + var presentationMeetingCodec = Codec as IHasPresentationOnlyMeeting; + if (presentationMeetingCodec != null) + { + presentationMeetingCodec.StartNormalMeetingFromSharingOnlyMeeting(); + } + } + else + { + Codec.EndAllCalls(); + } + } else + { Codec.EndAllCalls(); + } }); TriList.SetSigFalseAction(UIBoolJoin.CallEndAllConfirmPress, () => @@ -535,7 +596,7 @@ namespace PepperDash.Essentials.UIDrivers.VC Codec.EndAllCalls(); }); - var meetingInfoCodec = Codec as IHasMeetingInfo; + if (meetingInfoCodec != null) { TriList.SetSigFalseAction(UIBoolJoin.MeetingLeavePress, () => @@ -1801,7 +1862,7 @@ namespace PepperDash.Essentials.UIDrivers.VC /// void PasswordKeypadClear() { - PasswordStringBuilder.Remove(0, SearchStringBuilder.Length); + PasswordStringBuilder.Remove(0, PasswordStringBuilder.Length); PasswordStringFeedback.FireUpdate(); PasswordStringCheckEnables(); @@ -1929,6 +1990,7 @@ namespace PepperDash.Essentials.UIDrivers.VC _passwordPromptDialogVisible = false; Parent.Keyboard.Hide(); TriList.SetBool(UIBoolJoin.PasswordPromptDialogVisible, _passwordPromptDialogVisible); + PasswordKeypadClear(); } } } diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs index 24f1968b..06edf7bf 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs @@ -235,6 +235,15 @@ namespace PepperDash.Essentials.Devices.Common.Codec { Calls = new List(); } + + #region Overrides of Object + + public override string ToString() + { + return String.Format("{0}:{1}: {2}-{3}", Title, Agenda, StartTime, EndTime); + } + + #endregion } public class Call 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 cff1e4c0..fc5f68d4 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 @@ -122,6 +122,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 index 236fde80..0720b319 100644 --- 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 @@ -36,8 +36,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces public string ShareStatus { get; private set; } [JsonProperty("isHost")] public Boolean IsHost { get; private set; } + [JsonProperty("isSharingMeeting")] + public Boolean IsSharingMeeting { get; private set; } + [JsonProperty("waitingForHost")] + public Boolean WaitingForHost { get; private set; } - public MeetingInfo(string id, string name, string host, string password, string shareStatus, bool isHost) + public MeetingInfo(string id, string name, string host, string password, string shareStatus, bool isHost, bool isSharingMeeting, bool waitingForHost) { Id = id; Name = name; @@ -45,6 +49,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces Password = password; ShareStatus = shareStatus; IsHost = isHost; + IsSharingMeeting = isSharingMeeting; + WaitingForHost = waitingForHost; } } diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasPresentationOnlyMeeting.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasPresentationOnlyMeeting.cs new file mode 100644 index 00000000..06730d2a --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasPresentationOnlyMeeting.cs @@ -0,0 +1,18 @@ +namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces +{ + public interface IHasPresentationOnlyMeeting + { + void StartSharingOnlyMeeting(); + void StartSharingOnlyMeeting(eSharingMeetingMode mode); + void StartSharingOnlyMeeting(eSharingMeetingMode mode, ushort duration); + void StartSharingOnlyMeeting(eSharingMeetingMode mode, ushort duration, string password); + void StartNormalMeetingFromSharingOnlyMeeting(); + } + + public enum eSharingMeetingMode + { + None, + Laptop, + Ios, + } +} \ No newline at end of file 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 aac45f51..e6efd063 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 @@ -59,6 +59,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public List AudioOuputs { get; set; } public List Cameras { get; set; } public zEvent.PhoneCallStatus PhoneCall { get; set; } + public zEvent.NeedWaitForHost NeedWaitForHost { get; set; } public ZoomRoomStatus() { @@ -76,6 +77,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom AudioOuputs = new List(); Cameras = new List(); PhoneCall = new zEvent.PhoneCallStatus(); + NeedWaitForHost = new zEvent.NeedWaitForHost(); } } @@ -756,6 +758,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// public class zEvent { + public class StartLocalPresentMeeting + { + public bool Success { get; set; } + } public class NeedWaitForHost { public bool Wait { get; set; } @@ -1471,6 +1477,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public static List GetGenericParticipantListFromParticipantsResult( List participants) { + if (participants.Count == 0) + { + return new List(); + } //return participants.Select(p => new Participant // { // UserId = p.UserId, 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 2b4685a2..32a24a28 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 @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Text; using Crestron.SimplSharp; @@ -25,7 +26,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom IRouting, IHasScheduleAwareness, IHasCodecCameras, IHasParticipants, IHasCameraOff, IHasCameraMute, IHasCameraAutoMode, IHasFarEndContentStatus, IHasSelfviewPosition, IHasPhoneDialing, IHasZoomRoomLayouts, IHasParticipantPinUnpin, - IHasParticipantAudioMute, IHasSelfviewSize, IPasswordPrompt, IHasStartMeeting, IHasMeetingInfo + IHasParticipantAudioMute, IHasSelfviewSize, IPasswordPrompt, IHasStartMeeting, IHasMeetingInfo, IHasPresentationOnlyMeeting { private const long MeetingRefreshTimer = 60000; public uint DefaultMeetingDurationMin { get; private set; } @@ -111,6 +112,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom CodecSchedule = new CodecScheduleAwareness(MeetingRefreshTimer); + if (_props.MinutesBeforeMeetingStart > 0) + { + CodecSchedule.MeetingWarningMinutes = _props.MinutesBeforeMeetingStart; + } + ReceivingContent = new BoolFeedback(FarEndIsSharingContentFeedbackFunc); SelfviewPipPositionFeedback = new StringFeedback(SelfviewPipPositionFeedbackFunc); @@ -464,18 +470,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom private void SetUpCallFeedbackActions() { - Status.Call.Sharing.PropertyChanged += (o, a) => - { - if (a.PropertyName == "State") - { - 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; - } - }; + Status.Call.Sharing.PropertyChanged += HandleSharingStateUpdate; Status.Call.PropertyChanged += (o, a) => { @@ -487,6 +482,39 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom }; } + private void HandleSharingStateUpdate(object sender, PropertyChangedEventArgs a) + { + if (a.PropertyName != "State") + { + return; + } + + SharingContentIsOnFeedback.FireUpdate(); + ReceivingContent.FireUpdate(); + try + { + + // Update the share status of the meeting info + if (MeetingInfo == null) + { + var sharingStatus = GetSharingStatus(); + + MeetingInfo = new MeetingInfo("", "", "", "", sharingStatus, GetIsHostMyself(), true, false); + return; + } + + var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, Participants.Host != null ? Participants.Host.Name : "None", + MeetingInfo.Password, GetSharingStatus(), GetIsHostMyself(), MeetingInfo.IsSharingMeeting, MeetingInfo.WaitingForHost); + MeetingInfo = meetingInfo; + } + catch (Exception e) + { + Debug.Console(1, this, "Error processing state property update. {0}", e.Message); + Debug.Console(2, this, e.StackTrace); + MeetingInfo = new MeetingInfo("", "", "", "", "None", false, false, false); + } + } + /// /// Subscribes to the PropertyChanged events on the state objects and fires the corresponding feedbacks. /// @@ -626,8 +654,16 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom case "isDirectPresentationConnected": case "isSharingBlackMagic": { + Debug.Console(2, this, "Updating sharing status: {0}", a.PropertyName); + + SharingContentIsOnFeedback.FireUpdate(); + if (MeetingInfo == null) + { + //Ignoring for now, as the CallInfo return will create the appropriate value + return; + } // Update the share status of the meeting info - var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, MeetingInfo.Host, MeetingInfo.Password, GetSharingStatus(), GetIsHostMyself()); + var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, MeetingInfo.Host, MeetingInfo.Password, GetSharingStatus(), GetIsHostMyself(), MeetingInfo.IsSharingMeeting, MeetingInfo.WaitingForHost); MeetingInfo = meetingInfo; break; } @@ -1208,7 +1244,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom 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()); + var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, Participants.Host.Name, MeetingInfo.Password, MeetingInfo.ShareStatus, GetIsHostMyself(), MeetingInfo.IsSharingMeeting, MeetingInfo.WaitingForHost); MeetingInfo = meetingInfo; PrintCurrentCallParticipants(); @@ -1403,15 +1439,40 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } case "needwaitforhost": { - var needWait = - JsonConvert.DeserializeObject(responseObj.ToString()); + Status.NeedWaitForHost = JsonConvert.DeserializeObject(responseObj.ToString()); - if (needWait.Wait) - { - // TODO: notify user to wait for host - } + Debug.Console(1, this, "NeedWaitForHost: {0}", Status.NeedWaitForHost.Wait); - break; + if (Status.NeedWaitForHost.Wait) + { + if (MeetingInfo == null) + { + MeetingInfo = new MeetingInfo("Waiting For Host", "Waiting For Host", "Waiting For Host", "", + GetSharingStatus(), false, false, true); + + UpdateCallStatus(); + break; + } + + MeetingInfo = new MeetingInfo("Waiting For Host", "Waiting For Host", "Waiting For Host", "", + GetSharingStatus(), false, false, true); + + UpdateCallStatus(); + + break; + } + + if (MeetingInfo == null) + { + MeetingInfo = new MeetingInfo("Waiting For Host", "Waiting For Host", "Waiting For Host", "", + GetSharingStatus(), false, false, false); + break; + } + + MeetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, MeetingInfo.Host, MeetingInfo.Password, + GetSharingStatus(), GetIsHostMyself(), false, false); + + break; } case "openvideofailforhoststop": { @@ -1487,6 +1548,18 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom break; } + case "startlocalpresentmeeting": + { + var result = JsonConvert.DeserializeObject(responseObj.ToString()); + + if (result.Success) + { + MeetingInfo = new MeetingInfo("", "", "", "", "", true, true, MeetingInfo.WaitingForHost); + break; + } + + break; + } default: { break; @@ -1604,7 +1677,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } catch (Exception ex) { - Debug.Console(1, this, "Error Deserializing feedback: {0}", ex); + Debug.Console(1, this, "Error Deserializing feedback: {0}", ex.Message); + Debug.Console(2, this, "{0}", ex); + + if (ex.InnerException != null) + { + Debug.Console(1, this,"Error Deserializing feedback inner exception: {0}", ex.InnerException.Message); + Debug.Console(2, this, "{0}", ex.InnerException.StackTrace); + } } } @@ -1664,8 +1744,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// private void UpdateCallStatus() { - Debug.Console(1, this, "[UpdateCallStatus] Current Call Status: {0}", - Status.Call != null ? Status.Call.Status.ToString() : "no call"); + Debug.Console(1, this, + "[UpdateCallStatus] Current Call Status: {0} Active Call Count: {1} Need Wait For Host: {2}", + Status.Call != null ? Status.Call.Status.ToString() : "no call", ActiveCalls.Count, Status.NeedWaitForHost.Wait); if (Status.Call != null) { @@ -1710,17 +1791,32 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom Type = eCodecCallType.Video, }; + if (!String.IsNullOrEmpty(_lastDialedMeetingNumber)) + { + _lastDialedMeetingNumber = String.Empty; + } 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); - OnCallStatusChange(newCall); - } - else + } else if (String.IsNullOrEmpty(Status.Call.Info.meeting_id) && Status.NeedWaitForHost.Wait) { - //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); + var newCall = new CodecActiveCallItem + { + Name = "Waiting For Host", + Number = "Waiting For Host", + Id = "Waiting For Host", + Status = newStatus, + Type = eCodecCallType.Video, + }; + + if (!String.IsNullOrEmpty(_lastDialedMeetingNumber)) + { + _lastDialedMeetingNumber = String.Empty; + } + + ActiveCalls.Add(newCall); + + OnCallStatusChange(newCall); } } } @@ -1731,15 +1827,24 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom switch (callStatus) { case zStatus.eCallStatus.IN_MEETING: + if (Status.NeedWaitForHost.Wait) + { + Status.NeedWaitForHost.Wait = false; + } existingCall.Status = eCodecCallStatus.Connected; break; case zStatus.eCallStatus.NOT_IN_MEETING: + if (Status.NeedWaitForHost.Wait) + { + Status.NeedWaitForHost.Wait = false; + } existingCall.Status = eCodecCallStatus.Disconnected; break; } Debug.Console(1, this, "[UpdateCallStatus] ELSE ActiveCalls.Count == {1} - Current Call Status: {0}", Status.Call != null ? Status.Call.Status.ToString() : "no call", ActiveCalls.Count); + OnCallStatusChange(existingCall); } @@ -1778,8 +1883,6 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom protected override void OnCallStatusChange(CodecActiveCallItem item) { - base.OnCallStatusChange(item); - if (item.Status == eCodecCallStatus.Connected) { @@ -1794,10 +1897,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom host, Status.Call.Info.meeting_password, GetSharingStatus(), - GetIsHostMyself() + GetIsHostMyself(), + !String.Equals(Status.Call.Info.meeting_type,"NORMAL"), + false ); } + base.OnCallStatusChange(item); + Debug.Console(1, this, "[OnCallStatusChange] Current Call Status: {0}", Status.Call != null ? Status.Call.Status.ToString() : "no call"); @@ -1811,24 +1918,33 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { string sharingState = "None"; - if (Status.Call.Sharing.State == zEvent.eSharingState.Receiving) + try { - 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"; - } + 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; + return sharingState; + } + catch (Exception e) + { + Debug.Console(1, this, "Exception getting sharing status: {0}", e.Message); + Debug.Console(2, this, "{0}", e.StackTrace); + return sharingState; + } } /// @@ -1837,15 +1953,30 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// private bool GetIsHostMyself() { - var host = Participants.Host; - - if(host == null) + try { - Debug.Console(2, this, "Host is currently null"); + if (Participants.CurrentParticipants.Count == 0) + { + Debug.Console(2, this, "No current participants"); + return false; + } + + 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; + } + catch (Exception e) + { + Debug.Console(1, "Exception getting isHost: {0}", e.Message); + Debug.Console(2, "{0}", e.StackTrace); return false; } - Debug.Console(2, this, "Host is: '{0}' IsMyself?: {1}", host.Name, host.IsMyself); - return host.IsMyself; } public override void StartSharing() @@ -1858,7 +1989,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// public override void StopSharing() { - SendText("zCommand Call Sharing Disconnect"); + if (Status.Sharing.isSharingBlackMagic) + { + SendText("zCommand Call Sharing HDMI Stop"); + } + else + { + SendText("zCommand Call Sharing Disconnect"); + } } public override void PrivacyModeOn() @@ -2144,11 +2282,13 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public override void Dial(Meeting meeting) { Debug.Console(1, this, "Dialing meeting.Id: {0} Title: {1}", meeting.Id, meeting.Title); + _lastDialedMeetingNumber = meeting.Id; SendText(string.Format("zCommand Dial Start meetingNumber: {0}", meeting.Id)); } public override void Dial(string number) { + Debug.Console(2, this, "Dialing number: {0}", number); _lastDialedMeetingNumber = number; SendText(string.Format("zCommand Dial Join meetingNumber: {0}", number)); } @@ -2859,7 +2999,37 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } #endregion - } + + #region Implementation of IHasPresentationOnlyMeeting + + public void StartSharingOnlyMeeting() + { + StartSharingOnlyMeeting(eSharingMeetingMode.None, 30, String.Empty); + } + + public void StartSharingOnlyMeeting(eSharingMeetingMode mode) + { + StartSharingOnlyMeeting(mode, 30, String.Empty); + } + + public void StartSharingOnlyMeeting(eSharingMeetingMode mode, ushort duration) + { + StartSharingOnlyMeeting(mode, duration, String.Empty); + } + + public void StartSharingOnlyMeeting(eSharingMeetingMode mode, ushort duration, string password) + { + SendText(String.Format("zCommand Dial Sharing Duration: {0} DisplayState: {1} Password: {2}", duration, mode, password)); + } + + public void StartNormalMeetingFromSharingOnlyMeeting() + { + Debug.Console(2, this, "Converting Sharing Meeting to Normal Meeting"); + SendText("zCommand call sharing ToNormal"); + } + + #endregion + } /// /// Zoom Room specific info object diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomPropertiesConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomPropertiesConfig.cs index 38238023..4c9b08c2 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomPropertiesConfig.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomPropertiesConfig.cs @@ -25,5 +25,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom //This layout will be selected when a call is connected and no content is being shared public string DefaultCallLayout { get; set; } + + public int MinutesBeforeMeetingStart { get; set; } } } \ No newline at end of file