using System; using System.Collections.Generic; using System.Linq; using Crestron.SimplSharp; using Crestron.SimplSharpPro; using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Devices.Codec; using PepperDash.Essentials.Core.Devices.VideoCodec; using PepperDash.Essentials.Core.PageManagers; using PepperDash.Essentials.Core.Touchpanels.Keyboards; using PepperDash.Essentials.UIDrivers; using PepperDash.Essentials.UIDrivers.VC; namespace PepperDash.Essentials { /// /// /// public class EssentialsHuddleVtc1PanelAvFunctionsDriver : PanelDriverBase, IAVWithVCDriver,IHasCallButton, IHasCalendarButton { #region UiDisplayMode enum public enum UiDisplayMode { Presentation, AudioSetup, Call, Start } #endregion /// /// Smart Object 15022 /// private readonly SubpageReferenceList _activityFooterSrl; /// /// For hitting feedbacks /// private readonly BoolInputSig _callButtonSig; private readonly List _currentDisplayModeSigsInUse = new List(); private readonly BoolInputSig _endMeetingButtonSig; /// /// The list of buttons on the header. Managed with visibility only /// //SmartObjectHeaderButtonList HeaderButtonsList; /// /// The AV page mangagers that have been used, to keep them alive for later /// private readonly Dictionary _pageManagers = new Dictionary(); /// /// The parent driver for this /// private readonly PanelDriverBase _parent; private readonly BoolInputSig _shareButtonSig; //// Important smart objects /// /// Smart Object 3200 /// protected readonly SubpageReferenceList SourceStagingSrl; private readonly CrestronTouchpanelPropertiesConfig _config; /// /// Interlocks the various call-related subpages /// private JoinedSigInterlock _callPagesInterlock; private BoolFeedback _callSharingInfoVisibleFeedback; /// /// All children attached to this driver. For hiding and showing as a group. /// private List _childDrivers = new List(); /// /// The mode showing. Presentation or call. /// protected UiDisplayMode CurrentMode = UiDisplayMode.Start; /// /// Current page manager running for a source /// private PageManager _currentSourcePageManager; /// /// Tracks the last meeting that was cancelled /// private string _lastMeetingDismissedId; private CTimer _nextMeetingTimer; /// /// /// private ModalDialog _powerDownModal; /// /// Will auto-timeout a power off /// private CTimer _powerOffTimer; /// /// Controls timeout of notification ribbon timer /// private CTimer _ribbonTimer; /// /// Interlock for various source, camera, call control bars. The bar above the activity footer. This is also /// used to show start page /// private JoinedSigInterlock _stagingBarInterlock; /// /// The Video codec driver /// protected EssentialsVideoCodecUiDriver VcDriver; private EssentialsHuddleVtc1Room _currentRoom; private EssentialsHuddleTechPageDriver _techDriver; /// /// Constructor /// public EssentialsHuddleVtc1PanelAvFunctionsDriver(PanelDriverBase parent, CrestronTouchpanelPropertiesConfig config) : base(parent.TriList) { _config = config; _parent = parent; PopupInterlock = new JoinedSigInterlock(TriList); _stagingBarInterlock = new JoinedSigInterlock(TriList); _callPagesInterlock = new JoinedSigInterlock(TriList); SourceStagingSrl = new SubpageReferenceList(TriList, UISmartObjectJoin.SourceStagingSRL, 3, 3, 3); _activityFooterSrl = new SubpageReferenceList(TriList, UISmartObjectJoin.ActivityFooterSRL, 3, 3, 3); _callButtonSig = _activityFooterSrl.BoolInputSig(2, 1); _shareButtonSig = _activityFooterSrl.BoolInputSig(1, 1); _endMeetingButtonSig = _activityFooterSrl.BoolInputSig(3, 1); MeetingOrContactMethodModalSrl = new SubpageReferenceList(TriList, UISmartObjectJoin.MeetingListSRL, 3, 3, 5); // buttons are added in SetCurrentRoom //HeaderButtonsList = new SmartObjectHeaderButtonList(TriList.SmartObjects[UISmartObjectJoin.HeaderButtonList]); SetupActivityFooterWhenRoomOff(); ShowVolumeGauge = true; Keyboard = new HabaneroKeyboardController(TriList); } /// /// Whether volume ramping from this panel will show the volume /// gauge popup. /// public bool ShowVolumeGauge { get; set; } /// /// /// public uint PowerOffTimeout { get; set; } /// /// /// public string DefaultRoomKey { get; set; } /// /// The driver for the tech page. Lazy getter for memory usage /// private EssentialsHuddleTechPageDriver TechDriver { get { return _techDriver ?? (_techDriver = new EssentialsHuddleTechPageDriver(TriList, _currentRoom.PropertiesConfig.Tech)); } } #region IAVWithVCDriver Members /// /// /// public EssentialsHuddleVtc1Room CurrentRoom { get { return _currentRoom; } set { SetCurrentRoom(value); } } /// /// /// public SubpageReferenceList MeetingOrContactMethodModalSrl { get; set; } /// /// /// //ModalDialog WarmingCoolingModal; /// /// Represents /// public JoinedSigInterlock PopupInterlock { get; private set; } /// /// The keyboard /// public HabaneroKeyboardController Keyboard { get; private set; } /// /// Reveals a message on the notification ribbon until cleared /// /// Text to display /// Time in ms to display. 0 to keep on screen public void ShowNotificationRibbon(string message, int timeout) { TriList.SetString(UIStringJoin.NotificationRibbonText, message); TriList.SetBool(UIBoolJoin.NotificationRibbonVisible, true); if (timeout > 0) { if (_ribbonTimer != null) { _ribbonTimer.Stop(); } _ribbonTimer = new CTimer(o => { TriList.SetBool(UIBoolJoin.NotificationRibbonVisible, false); _ribbonTimer = null; }, timeout); } } /// /// Hides the notification ribbon /// public void HideNotificationRibbon() { TriList.SetBool(UIBoolJoin.NotificationRibbonVisible, false); if (_ribbonTimer != null) { _ribbonTimer.Stop(); _ribbonTimer = null; } } /// /// Reveals the tech page and puts away anything that's in the way. /// public void ShowTech() { PopupInterlock.HideAndClear(); TechDriver.Show(); } /// /// /// public void ActivityCallButtonPressed() { if (VcDriver.IsVisible) { return; } HideLogo(); HideNextMeetingPopup(); TriList.SetBool(UIBoolJoin.StartPageVisible, false); TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, false); TriList.SetBool(UIBoolJoin.SelectASourceVisible, false); if (_currentSourcePageManager != null) { _currentSourcePageManager.Hide(); } PowerOnFromCall(); CurrentMode = UiDisplayMode.Call; SetActivityFooterFeedbacks(); VcDriver.Show(); } /// /// Puts away modals and things that might be up when call comes in /// public void PrepareForCodecIncomingCall() { if (_powerDownModal != null && _powerDownModal.ModalIsVisible) { _powerDownModal.CancelDialog(); } PopupInterlock.Hide(); } #endregion /// /// Add a video codec driver to this /// /// public void SetVideoCodecDriver(EssentialsVideoCodecUiDriver vcd) { VcDriver = vcd; } /// /// /// public override void Show() { if (CurrentRoom == null) { Debug.Console(1, "ERROR: AVUIFunctionsDriver, Cannot show. No room assigned"); return; } switch (_config.HeaderStyle.ToLower()) { case CrestronTouchpanelPropertiesConfig.Habanero: TriList.SetSigFalseAction(UIBoolJoin.HeaderRoomButtonPress, () => PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.RoomHeaderPageVisible)); break; case CrestronTouchpanelPropertiesConfig.Verbose: break; } TriList.SetBool(UIBoolJoin.DateAndTimeVisible, _config.ShowDate && _config.ShowTime); TriList.SetBool(UIBoolJoin.DateOnlyVisible, _config.ShowDate && !_config.ShowTime); TriList.SetBool(UIBoolJoin.TimeOnlyVisible, !_config.ShowDate && _config.ShowTime); TriList.SetBool(UIBoolJoin.TopBarHabaneroDynamicVisible, true); TriList.SetBool(UIBoolJoin.ActivityFooterVisible, true); // Privacy mute button TriList.SetSigFalseAction(UIBoolJoin.Volume1SpeechMutePressAndFB, _currentRoom.PrivacyModeToggle); _currentRoom.PrivacyModeIsOnFeedback.LinkInputSig( TriList.BooleanInput[UIBoolJoin.Volume1SpeechMutePressAndFB]); // Default to showing rooms/sources now. if (CurrentRoom.OnFeedback.BoolValue) { TriList.SetBool(UIBoolJoin.TapToBeginVisible, false); SetupActivityFooterWhenRoomOn(); } else { TriList.SetBool(UIBoolJoin.StartPageVisible, true); TriList.SetBool(UIBoolJoin.TapToBeginVisible, true); SetupActivityFooterWhenRoomOff(); } ShowCurrentDisplayModeSigsInUse(); // *** Header Buttons *** // Generic "close" button for popup modals TriList.SetSigFalseAction(UIBoolJoin.InterlockedModalClosePress, PopupInterlock.HideAndClear); // Volume related things TriList.SetSigFalseAction(UIBoolJoin.VolumeDefaultPress, () => CurrentRoom.SetDefaultLevels()); TriList.SetString(UIStringJoin.AdvancedVolumeSlider1Text, "Room"); //if (TriList is CrestronApp) // TriList.BooleanInput[UIBoolJoin.GearButtonVisible].BoolValue = false; //else // TriList.BooleanInput[UIBoolJoin.GearButtonVisible].BoolValue = true; // power-related functions // Note: some of these are not directly-related to the huddle space UI, but are held over // in case TriList.SetSigFalseAction(UIBoolJoin.ShowPowerOffPress, EndMeetingPress); TriList.SetSigFalseAction(UIBoolJoin.DisplayPowerTogglePress, () => { if (CurrentRoom != null && CurrentRoom.DefaultDisplay is IPower) { (CurrentRoom.DefaultDisplay as IPower).PowerToggle(); } }); SetupNextMeetingTimer(); base.Show(); } /// /// Allows PopupInterlock to be toggled if the calls list is already visible, or if the codec is in a call /// public void ShowActiveCallsList() { TriList.SetBool(UIBoolJoin.CallEndAllConfirmVisible, true); if (PopupInterlock.CurrentJoin == UIBoolJoin.HeaderActiveCallsListVisible) { PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.HeaderActiveCallsListVisible); } else { var videoCodecBase = _currentRoom.ScheduleSource as VideoCodecBase; if (videoCodecBase != null && videoCodecBase.IsInCall) { PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.HeaderActiveCallsListVisible); } } } /// /// /// private void ShowLogo() { if (CurrentRoom.LogoUrl == null) { TriList.SetBool(UIBoolJoin.LogoDefaultVisible, true); TriList.SetBool(UIBoolJoin.LogoUrlVisible, false); } else { TriList.SetBool(UIBoolJoin.LogoDefaultVisible, false); TriList.SetBool(UIBoolJoin.LogoUrlVisible, true); TriList.SetString(UIStringJoin.LogoUrl, _currentRoom.LogoUrl); } } /// /// /// private void HideLogo() { TriList.SetBool(UIBoolJoin.LogoDefaultVisible, false); TriList.SetBool(UIBoolJoin.LogoUrlVisible, false); } /// /// /// public override void Hide() { HideAndClearCurrentDisplayModeSigsInUse(); TriList.SetBool(UIBoolJoin.TopBarHabaneroDynamicVisible, false); TriList.BooleanInput[UIBoolJoin.ActivityFooterVisible].BoolValue = false; TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = false; TriList.BooleanInput[UIBoolJoin.TapToBeginVisible].BoolValue = false; TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; if (_nextMeetingTimer != null) { _nextMeetingTimer.Stop(); } HideNextMeetingPopup(); base.Hide(); } private void SetupNextMeetingTimer() { var ss = _currentRoom.ScheduleSource; if (ss != null) { _nextMeetingTimer = new CTimer(o => ShowNextMeetingTimerCallback(), null, 0, 60000); } } /// /// /// private void ShowNextMeetingTimerCallback() { // 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 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.ToShortTimeString() : ""); var meeting = meetings.LastOrDefault(m => m.Joinable); if (CurrentRoom.OnFeedback.BoolValue && lastMeetingDismissed == meeting) { return; } _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.ToShortTimeString()); TriList.SetString(UIStringJoin.NextMeetingEndTimeText, meeting.EndTime.ToShortTimeString()); 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 : TriList.SetString(UIStringJoin.NextMeetingFollowingMeetingText, indexOfNext < meetings.Count ? meetings[indexOfNext].StartTime.ToShortTimeString() : "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.SetBool(UIBoolJoin.NextMeetingModalVisible, true); } } } /// /// /// protected void HideNextMeetingPopup() { TriList.SetBool(UIBoolJoin.NextMeetingModalVisible, false); } /// /// Calendar should only be visible when it's supposed to /// public void CalendarPress() { //RefreshMeetingsList(); // List should be up-to-date PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.MeetingsOrContacMethodsListVisible); } /// /// Dials a meeting after turning on room (if necessary) /// private void RoomOnAndDialMeeting(Meeting meeting) { Action dialAction = () => { var d = _currentRoom.ScheduleSource as VideoCodecBase; if (d != null) { d.Dial(meeting); _lastMeetingDismissedId = meeting.Id; // To prevent prompts for already-joined call } }; if (CurrentRoom.OnFeedback.BoolValue) { dialAction(); } else { // Rig a one-time handler to catch when the room is warmed and then dial call EventHandler oneTimeHandler = null; oneTimeHandler = (o, a) => { if (!CurrentRoom.IsWarmingUpFeedback.BoolValue) { CurrentRoom.IsWarmingUpFeedback.OutputChange -= oneTimeHandler; dialAction(); } }; CurrentRoom.IsWarmingUpFeedback.OutputChange += oneTimeHandler; ActivityCallButtonPressed(); } } /// /// When the room is off, set the footer SRL /// private void SetupActivityFooterWhenRoomOff() { _activityFooterSrl.Clear(); _activityFooterSrl.AddItem(new SubpageReferenceListActivityItem(1, _activityFooterSrl, 0, b => { if (!b) { ActivityShareButtonPressed(); } })); _activityFooterSrl.AddItem(new SubpageReferenceListActivityItem(2, _activityFooterSrl, 3, b => { if (!b) { ActivityCallButtonPressed(); } })); _activityFooterSrl.Count = 2; TriList.SetUshort(UIUshortJoin.PresentationStagingCaretMode, 1); // right one slot TriList.SetUshort(UIUshortJoin.CallStagingCaretMode, 5); // left one slot } /// /// Sets up the footer SRL for when the room is on /// private void SetupActivityFooterWhenRoomOn() { _activityFooterSrl.Clear(); _activityFooterSrl.AddItem(new SubpageReferenceListActivityItem(1, _activityFooterSrl, 0, b => { if (!b) { ActivityShareButtonPressed(); } })); _activityFooterSrl.AddItem(new SubpageReferenceListActivityItem(2, _activityFooterSrl, 3, b => { if (!b) { ActivityCallButtonPressed(); } })); _activityFooterSrl.AddItem(new SubpageReferenceListActivityItem(3, _activityFooterSrl, 4, b => { if (!b) { EndMeetingPress(); } })); _activityFooterSrl.Count = 3; TriList.SetUshort(UIUshortJoin.PresentationStagingCaretMode, 2); // center TriList.SetUshort(UIUshortJoin.CallStagingCaretMode, 0); // left -2 } /// /// Single point call for setting the feedbacks on the activity buttons /// private void SetActivityFooterFeedbacks() { _callButtonSig.BoolValue = CurrentMode == UiDisplayMode.Call && CurrentRoom.ShutdownType == eShutdownType.None; _shareButtonSig.BoolValue = CurrentMode == UiDisplayMode.Presentation && CurrentRoom.ShutdownType == eShutdownType.None; _endMeetingButtonSig.BoolValue = CurrentRoom.ShutdownType != eShutdownType.None; } /// /// Attached to activity list share button /// protected virtual void ActivityShareButtonPressed() { SetupSourceList(); if (VcDriver.IsVisible) { VcDriver.Hide(); } HideNextMeetingPopup(); TriList.SetBool(UIBoolJoin.StartPageVisible, false); TriList.SetBool(UIBoolJoin.CallStagingBarVisible, false); TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, true); // 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.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.CurrentSourceInfoKey == EssentialsHuddleVtc1Room.DefaultCodecRouteString) { TriList.SetBool(UIBoolJoin.SelectASourceVisible, true); } else if (_currentSourcePageManager != null) { _currentSourcePageManager.Show(); } } CurrentMode = UiDisplayMode.Presentation; SetupSourceList(); SetActivityFooterFeedbacks(); } /// /// Powers up the system to the codec route, if not already on. /// private void PowerOnFromCall() { if (!CurrentRoom.OnFeedback.BoolValue) { _currentRoom.RunDefaultCallRoute(); } } /// /// Shows all sigs that are in CurrentDisplayModeSigsInUse /// private void ShowCurrentDisplayModeSigsInUse() { foreach (var sig in _currentDisplayModeSigsInUse) { sig.BoolValue = true; } } /// /// Hides all CurrentDisplayModeSigsInUse sigs and clears the array /// private void HideAndClearCurrentDisplayModeSigsInUse() { foreach (var sig in _currentDisplayModeSigsInUse) { sig.BoolValue = false; } _currentDisplayModeSigsInUse.Clear(); } /// /// Loads the appropriate Sigs into CurrentDisplayModeSigsInUse and shows them /// private void ShowCurrentSource() { if (CurrentRoom.CurrentSourceInfo == null) { return; } if (CurrentRoom.CurrentSourceInfo.SourceDevice == null) { TriList.SetBool(UIBoolJoin.SelectASourceVisible, true); return; } var uiDev = CurrentRoom.CurrentSourceInfo.SourceDevice as IUiDisplayInfo; // If we need a page manager, get an appropriate one if (uiDev == null) { return; } TriList.SetBool(UIBoolJoin.SelectASourceVisible, false); // Got an existing page manager, get it PageManager pm; if (_pageManagers.ContainsKey(uiDev)) { pm = _pageManagers[uiDev]; } // Otherwise make an apporiate one else if (uiDev is ISetTopBoxControls) { pm = new SetTopBoxThreePanelPageManager(uiDev as ISetTopBoxControls, TriList); } else if (uiDev is IDiscPlayerControls) { pm = new DiscPlayerMediumPageManager(uiDev as IDiscPlayerControls, TriList); } else { pm = new DefaultPageManager(uiDev, TriList); } _pageManagers[uiDev] = pm; _currentSourcePageManager = pm; pm.Show(); } /// /// Called from button presses on source, where We can assume we want /// to change to the proper screen. /// /// The key name of the route to run private void UiSelectSource(string key) { // Run the route and when it calls back, show the source CurrentRoom.RunRouteAction(key); } /// /// /// public void EndMeetingPress() { if (!CurrentRoom.OnFeedback.BoolValue || CurrentRoom.ShutdownPromptTimer.IsRunningFeedback.BoolValue) { return; } CurrentRoom.StartShutdown(eShutdownType.Manual); } /// /// /// /// /// private void ShutdownPromptTimer_HasStarted(object sender, EventArgs e) { // Do we need to check where the UI is? No? var timer = CurrentRoom.ShutdownPromptTimer; SetActivityFooterFeedbacks(); if (CurrentRoom.ShutdownType == eShutdownType.Manual || CurrentRoom.ShutdownType == eShutdownType.Vacancy) { _powerDownModal = new ModalDialog(TriList); var message = string.Format("Meeting will end in {0} seconds", CurrentRoom.ShutdownPromptSeconds); // Attach timer things to modal CurrentRoom.ShutdownPromptTimer.TimeRemainingFeedback.OutputChange += ShutdownPromptTimer_TimeRemainingFeedback_OutputChange; CurrentRoom.ShutdownPromptTimer.PercentFeedback.OutputChange += ShutdownPromptTimer_PercentFeedback_OutputChange; // respond to offs by cancelling dialog var onFb = CurrentRoom.OnFeedback; EventHandler offHandler = null; offHandler = (o, a) => { if (!onFb.BoolValue) { _powerDownModal.HideDialog(); SetActivityFooterFeedbacks(); onFb.OutputChange -= offHandler; } }; onFb.OutputChange += offHandler; _powerDownModal.PresentModalDialog(2, "End Meeting", "Power", message, "Cancel", "End Meeting Now", true, true, but => { if (but != 2) // any button except for End cancels { timer.Cancel(); } else { timer.Finish(); } }); } } /// /// /// /// /// private void ShutdownPromptTimer_HasFinished(object sender, EventArgs e) { SetActivityFooterFeedbacks(); CurrentRoom.ShutdownPromptTimer.TimeRemainingFeedback.OutputChange -= ShutdownPromptTimer_TimeRemainingFeedback_OutputChange; CurrentRoom.ShutdownPromptTimer.PercentFeedback.OutputChange -= ShutdownPromptTimer_PercentFeedback_OutputChange; } /// /// /// /// /// private void ShutdownPromptTimer_WasCancelled(object sender, EventArgs e) { if (_powerDownModal != null) { _powerDownModal.HideDialog(); } SetActivityFooterFeedbacks(); CurrentRoom.ShutdownPromptTimer.TimeRemainingFeedback.OutputChange += ShutdownPromptTimer_TimeRemainingFeedback_OutputChange; CurrentRoom.ShutdownPromptTimer.PercentFeedback.OutputChange -= ShutdownPromptTimer_PercentFeedback_OutputChange; } /// /// Event handler for countdown timer on power off modal /// private void ShutdownPromptTimer_TimeRemainingFeedback_OutputChange(object sender, EventArgs e) { var stringFeedback = sender as StringFeedback; if (stringFeedback == null) { return; } var message = string.Format("Meeting will end in {0} seconds", stringFeedback.StringValue); TriList.StringInput[ModalDialog.MessageTextJoin].StringValue = message; } /// /// Event handler for percentage on power off countdown /// private void ShutdownPromptTimer_PercentFeedback_OutputChange(object sender, EventArgs e) { var intFeedback = sender as IntFeedback; if (intFeedback == null) { return; } var value = (ushort) (intFeedback.UShortValue*65535/100); TriList.UShortInput[ModalDialog.TimerGaugeJoin].UShortValue = value; } /// /// /// private void CancelPowerOffTimer() { if (_powerOffTimer != null) { _powerOffTimer.Stop(); _powerOffTimer = null; } } /// /// /// /// public void VolumeUpPress(bool state) { if (CurrentRoom.CurrentVolumeControls != null) { CurrentRoom.CurrentVolumeControls.VolumeUp(state); } } /// /// /// /// public void VolumeDownPress(bool state) { if (CurrentRoom.CurrentVolumeControls != null) { CurrentRoom.CurrentVolumeControls.VolumeDown(state); } } /// /// Helper for property setter. Sets the panel to the given room, latching up all functionality /// protected void RefreshCurrentRoom(EssentialsHuddleVtc1Room room) { if (_currentRoom != null) { // Disconnect current room _currentRoom.CurrentVolumeDeviceChange -= CurrentRoom_CurrentAudioDeviceChange; ClearAudioDeviceConnections(); _currentRoom.CurrentSourceChange -= CurrentRoom_SourceInfoChange; DisconnectSource(_currentRoom.CurrentSourceInfo); _currentRoom.ShutdownPromptTimer.HasStarted -= ShutdownPromptTimer_HasStarted; _currentRoom.ShutdownPromptTimer.HasFinished -= ShutdownPromptTimer_HasFinished; _currentRoom.ShutdownPromptTimer.WasCancelled -= ShutdownPromptTimer_WasCancelled; _currentRoom.OnFeedback.OutputChange -= CurrentRoom_OnFeedback_OutputChange; _currentRoom.IsWarmingUpFeedback.OutputChange -= CurrentRoom_IsWarmingFeedback_OutputChange; _currentRoom.IsCoolingDownFeedback.OutputChange -= CurrentRoom_IsCoolingDownFeedback_OutputChange; _currentRoom.InCallFeedback.OutputChange -= CurrentRoom_InCallFeedback_OutputChange; } _currentRoom = room; if (_currentRoom != null) { // get the source list config and set up the source list SetupSourceList(); // Name and logo TriList.StringInput[UIStringJoin.CurrentRoomName].StringValue = _currentRoom.Name; ShowLogo(); // Shutdown timer _currentRoom.ShutdownPromptTimer.HasStarted += ShutdownPromptTimer_HasStarted; _currentRoom.ShutdownPromptTimer.HasFinished += ShutdownPromptTimer_HasFinished; _currentRoom.ShutdownPromptTimer.WasCancelled += ShutdownPromptTimer_WasCancelled; // Link up all the change events from the room _currentRoom.OnFeedback.OutputChange += CurrentRoom_OnFeedback_OutputChange; CurrentRoom_SyncOnFeedback(); _currentRoom.IsWarmingUpFeedback.OutputChange += CurrentRoom_IsWarmingFeedback_OutputChange; _currentRoom.IsCoolingDownFeedback.OutputChange += CurrentRoom_IsCoolingDownFeedback_OutputChange; _currentRoom.InCallFeedback.OutputChange += CurrentRoom_InCallFeedback_OutputChange; _currentRoom.CurrentVolumeDeviceChange += CurrentRoom_CurrentAudioDeviceChange; RefreshAudioDeviceConnections(); _currentRoom.CurrentSourceChange += CurrentRoom_SourceInfoChange; RefreshSourceInfo(); if (_currentRoom.VideoCodec is IHasScheduleAwareness) { (_currentRoom.VideoCodec as IHasScheduleAwareness).CodecSchedule.MeetingsListHasChanged += CodecSchedule_MeetingsListHasChanged; } _callSharingInfoVisibleFeedback = new BoolFeedback(() => _currentRoom.VideoCodec.SharingContentIsOnFeedback.BoolValue); _currentRoom.VideoCodec.SharingContentIsOnFeedback.OutputChange += SharingContentIsOnFeedback_OutputChange; _callSharingInfoVisibleFeedback.LinkInputSig(TriList.BooleanInput[UIBoolJoin.CallSharedSourceInfoVisible]); SetActiveCallListSharingContentStatus(); if (_currentRoom != null) { _currentRoom.CurrentSourceChange += CurrentRoom_CurrentSingleSourceChange; } TriList.SetSigFalseAction(UIBoolJoin.CallStopSharingPress, () => _currentRoom.RunRouteAction("codecOsd", _currentRoom.SourceListKey)); var essentialsPanelMainInterfaceDriver = _parent as EssentialsPanelMainInterfaceDriver; if (essentialsPanelMainInterfaceDriver != null) { essentialsPanelMainInterfaceDriver.HeaderDriver.SetupHeaderButtons(this, _currentRoom); } } else { // Clear sigs that need to be TriList.StringInput[UIStringJoin.CurrentRoomName].StringValue = "Select a room"; } } private void SetCurrentRoom(EssentialsHuddleVtc1Room room) { if (_currentRoom == room || room == null) { return; } // Disconnect current (probably never called) room.ConfigChanged -= room_ConfigChanged; room.ConfigChanged += room_ConfigChanged; RefreshCurrentRoom(room); } /// /// Fires when room config of current room has changed. Meant to refresh room values to propegate any updates to UI /// /// /// private void room_ConfigChanged(object sender, EventArgs e) { RefreshCurrentRoom(_currentRoom); } /// /// /// /// /// private void CurrentRoom_InCallFeedback_OutputChange(object sender, EventArgs e) { var inCall = _currentRoom.InCallFeedback.BoolValue; if (inCall) { // Check if transitioning to in call - and non-sharable source is in use if (CurrentRoom.CurrentSourceInfo != null && CurrentRoom.CurrentSourceInfo.DisableCodecSharing) { Debug.Console(1, CurrentRoom, "Transitioning to in-call, cancelling non-sharable source"); CurrentRoom.RunRouteAction("codecOsd", CurrentRoom.SourceListKey); } } SetupSourceList(); } /// /// /// protected virtual void SetupSourceList() { var inCall = _currentRoom.InCallFeedback.BoolValue; var config = ConfigReader.ConfigObject.SourceLists; if (!config.ContainsKey(_currentRoom.SourceListKey)) { return; } var srcList = config[_currentRoom.SourceListKey].OrderBy(kv => kv.Value.Order); // Setup sources list SourceStagingSrl.Clear(); uint i = 1; // counter for UI list foreach (var kvp in srcList) { var srcConfig = kvp.Value; Debug.Console(1, "**** {0}, {1}, {2}, {3}, {4}", srcConfig.PreferredName, srcConfig.IncludeInSourceList, srcConfig.DisableCodecSharing, inCall, CurrentMode); // Skip sources marked as not included, and filter list of non-sharable sources when in call // or on share screen if (!srcConfig.IncludeInSourceList || (inCall && srcConfig.DisableCodecSharing) || CurrentMode == UiDisplayMode.Call && srcConfig.DisableCodecSharing) { Debug.Console(1, "Skipping {0}", srcConfig.PreferredName); continue; } var routeKey = kvp.Key; var item = new SubpageReferenceListSourceItem(i++, SourceStagingSrl, srcConfig, b => { if (!b) { UiSelectSource(routeKey); } }); SourceStagingSrl.AddItem(item); // add to the SRL item.RegisterForSourceChange(_currentRoom); } SourceStagingSrl.Count = (ushort) (i - 1); } /// /// If the schedule changes, this event will fire /// /// /// private void CodecSchedule_MeetingsListHasChanged(object sender, EventArgs e) { RefreshMeetingsList(); } /// /// Updates the current shared source label on the call list when the source changes /// /// /// private void CurrentRoom_CurrentSingleSourceChange(SourceListItem info, ChangeType type) { if (_currentRoom.VideoCodec.SharingContentIsOnFeedback.BoolValue && _currentRoom.CurrentSourceInfo != null) { TriList.StringInput[UIStringJoin.CallSharedSourceNameText].StringValue = _currentRoom.CurrentSourceInfo.PreferredName; } } /// /// Fires when the sharing source feedback of the codec changes /// /// /// private void SharingContentIsOnFeedback_OutputChange(object sender, EventArgs e) { SetActiveCallListSharingContentStatus(); } /// /// Sets the values for the text and button visibilty for the active call list source sharing info /// private void SetActiveCallListSharingContentStatus() { _callSharingInfoVisibleFeedback.FireUpdate(); string callListSharedSourceLabel; if (_currentRoom.VideoCodec.SharingContentIsOnFeedback.BoolValue && _currentRoom.CurrentSourceInfo != null) { Debug.Console(0, "*#* CurrentRoom.CurrentSourceInfo = {0}", _currentRoom.CurrentSourceInfo != null ? _currentRoom.CurrentSourceInfo.SourceKey : "Nada!"); callListSharedSourceLabel = _currentRoom.CurrentSourceInfo.PreferredName; } else { callListSharedSourceLabel = "None"; } TriList.StringInput[UIStringJoin.CallSharedSourceNameText].StringValue = callListSharedSourceLabel; } /// /// /// private void RefreshMeetingsList() { // 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.MeetingsOrContactMethodListTitleText, "Today's Meetings"); ushort i = 0; foreach (var m in _currentRoom.ScheduleSource.CodecSchedule.Meetings) { i++; MeetingOrContactMethodModalSrl.StringInputSig(i, 1).StringValue = m.StartTime.ToShortTimeString(); MeetingOrContactMethodModalSrl.StringInputSig(i, 2).StringValue = m.EndTime.ToShortTimeString(); MeetingOrContactMethodModalSrl.StringInputSig(i, 3).StringValue = m.Title; MeetingOrContactMethodModalSrl.StringInputSig(i, 4).StringValue = string.Format("
{0}", m.Organizer); MeetingOrContactMethodModalSrl.StringInputSig(i, 5).StringValue = "Join"; MeetingOrContactMethodModalSrl.BoolInputSig(i, 2).BoolValue = m.Joinable; var mm = m; // lambda scope MeetingOrContactMethodModalSrl.GetBoolFeedbackSig(i, 1).SetSigFalseAction(() => { PopupInterlock.Hide(); ActivityCallButtonPressed(); var d = _currentRoom.ScheduleSource as VideoCodecBase; if (d != null) { RoomOnAndDialMeeting(mm); } }); } MeetingOrContactMethodModalSrl.Count = i; if (i == 0) // Show item indicating no meetings are booked for rest of day { MeetingOrContactMethodModalSrl.Count = 1; MeetingOrContactMethodModalSrl.StringInputSig(1, 1).StringValue = string.Empty; MeetingOrContactMethodModalSrl.StringInputSig(1, 2).StringValue = string.Empty; MeetingOrContactMethodModalSrl.StringInputSig(1, 3).StringValue = "No Meetings are booked for the remainder of the day."; MeetingOrContactMethodModalSrl.StringInputSig(1, 4).StringValue = string.Empty; MeetingOrContactMethodModalSrl.StringInputSig(1, 5).StringValue = string.Empty; } } /// /// For room on/off changes /// private void CurrentRoom_OnFeedback_OutputChange(object sender, EventArgs e) { CurrentRoom_SyncOnFeedback(); } /// /// /// private void CurrentRoom_SyncOnFeedback() { var value = _currentRoom.OnFeedback.BoolValue; TriList.BooleanInput[UIBoolJoin.RoomIsOn].BoolValue = value; TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = !value; if (value) //ON { SetupActivityFooterWhenRoomOn(); TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; TriList.BooleanInput[UIBoolJoin.VolumeDualMute1Visible].BoolValue = true; } else { CurrentMode = UiDisplayMode.Start; if (VcDriver.IsVisible) { VcDriver.Hide(); } SetupActivityFooterWhenRoomOff(); ShowLogo(); SetActivityFooterFeedbacks(); TriList.BooleanInput[UIBoolJoin.VolumeDualMute1Visible].BoolValue = false; TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = false; // Clear this so that the pesky meeting warning can resurface every minute when off _lastMeetingDismissedId = null; } } /// /// /// private void CurrentRoom_IsWarmingFeedback_OutputChange(object sender, EventArgs e) { if (CurrentRoom.IsWarmingUpFeedback.BoolValue) { ShowNotificationRibbon("Room is powering on. Please wait...", 0); } else { ShowNotificationRibbon("Room is powered on. Welcome.", 2000); } } /// /// /// /// /// private void CurrentRoom_IsCoolingDownFeedback_OutputChange(object sender, EventArgs e) { if (CurrentRoom.IsCoolingDownFeedback.BoolValue) { ShowNotificationRibbon("Room is powering off. Please wait.", 0); } else { HideNotificationRibbon(); } } /// /// Hides source for provided source info /// /// private void DisconnectSource(SourceListItem previousInfo) { if (previousInfo == null) { return; } // Hide whatever is showing if (IsVisible) { if (_currentSourcePageManager != null) { _currentSourcePageManager.Hide(); _currentSourcePageManager = null; } } var previousDev = previousInfo.SourceDevice; // device type interfaces if (previousDev is ISetTopBoxControls) { (previousDev as ISetTopBoxControls).UnlinkButtons(TriList); } // common interfaces if (previousDev is IChannel) { (previousDev as IChannel).UnlinkButtons(TriList); } if (previousDev is IColor) { (previousDev as IColor).UnlinkButtons(TriList); } if (previousDev is IDPad) { (previousDev as IDPad).UnlinkButtons(TriList); } if (previousDev is IDvr) { (previousDev as IDvr).UnlinkButtons(TriList); } if (previousDev is INumericKeypad) { (previousDev as INumericKeypad).UnlinkButtons(TriList); } if (previousDev is IPower) { (previousDev as IPower).UnlinkButtons(TriList); } if (previousDev is ITransport) { (previousDev as ITransport).UnlinkButtons(TriList); } } /// /// Refreshes and shows the room's current source /// private void RefreshSourceInfo() { var routeInfo = CurrentRoom.CurrentSourceInfo; // This will show off popup too if (IsVisible && !VcDriver.IsVisible) { ShowCurrentSource(); } if (routeInfo == null) // || !CurrentRoom.OnFeedback.BoolValue) { // Check for power off and insert "Room is off" TriList.StringInput[UIStringJoin.CurrentSourceName].StringValue = "Room is off"; TriList.StringInput[UIStringJoin.CurrentSourceIcon].StringValue = "Power"; Hide(); _parent.Show(); return; } TriList.StringInput[UIStringJoin.CurrentSourceName].StringValue = routeInfo.PreferredName; TriList.StringInput[UIStringJoin.CurrentSourceIcon].StringValue = routeInfo.Icon; // defaults to "blank" //code that was here was unreachable becuase if we get past the if statement, routeInfo is // Connect controls if (routeInfo.SourceDevice != null) { ConnectControlDeviceMethods(routeInfo.SourceDevice); } } /// /// Attach the source to the buttons and things /// private void ConnectControlDeviceMethods(Device dev) { if (dev is ISetTopBoxControls) { (dev as ISetTopBoxControls).LinkButtons(TriList); } if (dev is IChannel) { (dev as IChannel).LinkButtons(TriList); } if (dev is IColor) { (dev as IColor).LinkButtons(TriList); } if (dev is IDPad) { (dev as IDPad).LinkButtons(TriList); } if (dev is IDvr) { (dev as IDvr).LinkButtons(TriList); } if (dev is INumericKeypad) { (dev as INumericKeypad).LinkButtons(TriList); } if (dev is IPower) { (dev as IPower).LinkButtons(TriList); } if (dev is ITransport) { (dev as ITransport).LinkButtons(TriList); } } /// /// Detaches the buttons and feedback from the room's current audio device /// private void ClearAudioDeviceConnections() { TriList.ClearBoolSigAction(UIBoolJoin.VolumeUpPress); TriList.ClearBoolSigAction(UIBoolJoin.VolumeDownPress); TriList.ClearBoolSigAction(UIBoolJoin.Volume1ProgramMutePressAndFB); var fDev = CurrentRoom.CurrentVolumeControls as IBasicVolumeWithFeedback; if (fDev != null) { TriList.ClearUShortSigAction(UIUshortJoin.VolumeSlider1Value); fDev.VolumeLevelFeedback.UnlinkInputSig( TriList.UShortInput[UIUshortJoin.VolumeSlider1Value]); } } /// /// Attaches the buttons and feedback to the room's current audio device /// private void RefreshAudioDeviceConnections() { var dev = CurrentRoom.CurrentVolumeControls; if (dev != null) // connect buttons { TriList.SetBoolSigAction(UIBoolJoin.VolumeUpPress, VolumeUpPress); TriList.SetBoolSigAction(UIBoolJoin.VolumeDownPress, VolumeDownPress); TriList.SetSigFalseAction(UIBoolJoin.Volume1ProgramMutePressAndFB, dev.MuteToggle); } var fbDev = dev as IBasicVolumeWithFeedback; if (fbDev == null) // this should catch both IBasicVolume and IBasicVolumeWithFeeback { TriList.UShortInput[UIUshortJoin.VolumeSlider1Value].UShortValue = 0; } else { // slider TriList.SetUShortSigAction(UIUshortJoin.VolumeSlider1Value, fbDev.SetVolume); // feedbacks fbDev.MuteFeedback.LinkInputSig(TriList.BooleanInput[UIBoolJoin.Volume1ProgramMutePressAndFB]); fbDev.VolumeLevelFeedback.LinkInputSig( TriList.UShortInput[UIUshortJoin.VolumeSlider1Value]); } } /// /// Handler for when the room's volume control device changes /// private void CurrentRoom_CurrentAudioDeviceChange(object sender, VolumeDeviceChangeEventArgs args) { if (args.Type == ChangeType.WillChange) { ClearAudioDeviceConnections(); } else // did change { RefreshAudioDeviceConnections(); } } /// /// Handles source change /// private void CurrentRoom_SourceInfoChange(SourceListItem info, ChangeType change) { if (change == ChangeType.WillChange) { DisconnectSource(info); } else { RefreshSourceInfo(); } } } /// /// For hanging off various common AV things that child drivers might need from a parent AV driver /// public interface IAVDriver { JoinedSigInterlock PopupInterlock { get; } void ShowNotificationRibbon(string message, int timeout); void HideNotificationRibbon(); void ShowTech(); } /// /// For hanging off various common VC things that child drivers might need from a parent AV driver /// public interface IAVWithVCDriver : IAVDriver { EssentialsHuddleVtc1Room CurrentRoom { get; } HabaneroKeyboardController Keyboard { get; } SubpageReferenceList MeetingOrContactMethodModalSrl { get; } /// /// Exposes the ability to switch into call mode /// void ActivityCallButtonPressed(); /// /// Allows the codec to trigger the main UI to clear up if call is coming in. /// void PrepareForCodecIncomingCall(); } }