2 * Copyright (C) 2016-2022 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "PVRGUIActionsTimers.h"
12 #include "ServiceBroker.h"
13 #include "dialogs/GUIDialogProgress.h"
14 #include "dialogs/GUIDialogSelect.h"
15 #include "dialogs/GUIDialogYesNo.h"
16 #include "guilib/GUIComponent.h"
17 #include "guilib/GUIWindowManager.h"
18 #include "guilib/LocalizeStrings.h"
19 #include "guilib/WindowIDs.h"
20 #include "messaging/helpers/DialogHelper.h"
21 #include "messaging/helpers/DialogOKHelper.h"
22 #include "pvr/PVREventLogJob.h"
23 #include "pvr/PVRItem.h"
24 #include "pvr/PVRManager.h"
25 #include "pvr/PVRPlaybackState.h"
26 #include "pvr/addons/PVRClient.h"
27 #include "pvr/channels/PVRChannel.h"
28 #include "pvr/channels/PVRChannelGroupMember.h"
29 #include "pvr/dialogs/GUIDialogPVRTimerSettings.h"
30 #include "pvr/epg/EpgInfoTag.h"
31 #include "pvr/guilib/PVRGUIActionsChannels.h"
32 #include "pvr/guilib/PVRGUIActionsParentalControl.h"
33 #include "pvr/guilib/PVRGUIActionsPlayback.h"
34 #include "pvr/recordings/PVRRecording.h"
35 #include "pvr/timers/PVRTimerInfoTag.h"
36 #include "pvr/timers/PVRTimers.h"
37 #include "settings/Settings.h"
38 #include "utils/StringUtils.h"
39 #include "utils/SystemInfo.h"
40 #include "utils/Variant.h"
41 #include "utils/log.h"
51 using namespace KODI::MESSAGING
;
53 CPVRGUIActionsTimers::CPVRGUIActionsTimers()
54 : m_settings({CSettings::SETTING_PVRRECORD_INSTANTRECORDTIME
,
55 CSettings::SETTING_PVRRECORD_INSTANTRECORDACTION
,
56 CSettings::SETTING_PVRREMINDERS_AUTOCLOSEDELAY
,
57 CSettings::SETTING_PVRREMINDERS_AUTORECORD
,
58 CSettings::SETTING_PVRREMINDERS_AUTOSWITCH
})
62 bool CPVRGUIActionsTimers::ShowTimerSettings(const std::shared_ptr
<CPVRTimerInfoTag
>& timer
) const
64 CGUIDialogPVRTimerSettings
* pDlgInfo
=
65 CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogPVRTimerSettings
>(
66 WINDOW_DIALOG_PVR_TIMER_SETTING
);
69 CLog::LogF(LOGERROR
, "Unable to get WINDOW_DIALOG_PVR_TIMER_SETTING!");
73 pDlgInfo
->SetTimer(timer
);
76 return pDlgInfo
->IsConfirmed();
79 bool CPVRGUIActionsTimers::AddReminder(const CFileItem
& item
) const
81 const std::shared_ptr
<CPVREpgInfoTag
> epgTag
= CPVRItem(item
).GetEpgInfoTag();
84 CLog::LogF(LOGERROR
, "No epg tag!");
88 if (CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epgTag
))
90 HELPERS::ShowOKDialogText(CVariant
{19033}, // "Information"
91 CVariant
{19034}); // "There is already a timer set for this event"
95 const std::shared_ptr
<CPVRTimerInfoTag
> newTimer
=
96 CPVRTimerInfoTag::CreateReminderFromEpg(epgTag
);
99 HELPERS::ShowOKDialogText(CVariant
{19033}, // "Information"
100 CVariant
{19094}); // Timer creation failed. Unsupported timer type.
104 return AddTimer(newTimer
);
107 bool CPVRGUIActionsTimers::AddTimer(bool bRadio
) const
109 const std::shared_ptr
<CPVRTimerInfoTag
> newTimer(new CPVRTimerInfoTag(bRadio
));
110 if (ShowTimerSettings(newTimer
))
112 return AddTimer(newTimer
);
117 bool CPVRGUIActionsTimers::AddTimer(const CFileItem
& item
, bool bShowTimerSettings
) const
119 return AddTimer(item
, false, bShowTimerSettings
, false);
122 bool CPVRGUIActionsTimers::AddTimerRule(const CFileItem
& item
,
123 bool bShowTimerSettings
,
124 bool bFallbackToOneShotTimer
) const
126 return AddTimer(item
, true, bShowTimerSettings
, bFallbackToOneShotTimer
);
129 bool CPVRGUIActionsTimers::AddTimer(const CFileItem
& item
,
131 bool bShowTimerSettings
,
132 bool bFallbackToOneShotTimer
) const
134 const std::shared_ptr
<CPVRChannel
> channel(CPVRItem(item
).GetChannel());
137 CLog::LogF(LOGERROR
, "No channel!");
141 if (CServiceBroker::GetPVRManager().Get
<PVR::GUI::Parental
>().CheckParentalLock(channel
) !=
142 ParentalCheckResult::SUCCESS
)
145 std::shared_ptr
<CPVREpgInfoTag
> epgTag
= CPVRItem(item
).GetEpgInfoTag();
148 if (epgTag
->IsGapTag())
149 epgTag
.reset(); // for gap tags, we can only create instant timers
151 else if (bCreateRule
)
153 CLog::LogF(LOGERROR
, "No epg tag!");
157 std::shared_ptr
<CPVRTimerInfoTag
> timer(
158 bCreateRule
|| !epgTag
? nullptr
159 : CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epgTag
));
160 std::shared_ptr
<CPVRTimerInfoTag
> rule(
161 bCreateRule
? CServiceBroker::GetPVRManager().Timers()->GetTimerRule(timer
) : nullptr);
164 HELPERS::ShowOKDialogText(
166 CVariant
{19034}); // "Information", "There is already a timer set for this event"
170 std::shared_ptr
<CPVRTimerInfoTag
> newTimer(
171 epgTag
? CPVRTimerInfoTag::CreateFromEpg(epgTag
, bCreateRule
)
172 : CPVRTimerInfoTag::CreateInstantTimerTag(channel
));
175 if (bCreateRule
&& bFallbackToOneShotTimer
)
176 newTimer
= CPVRTimerInfoTag::CreateFromEpg(epgTag
, false);
180 HELPERS::ShowOKDialogText(
181 CVariant
{19033}, // "Information"
182 bCreateRule
? CVariant
{19095} // Timer rule creation failed. Unsupported timer type.
183 : CVariant
{19094}); // Timer creation failed. Unsupported timer type.
188 if (bShowTimerSettings
)
190 if (!ShowTimerSettings(newTimer
))
194 return AddTimer(newTimer
);
197 bool CPVRGUIActionsTimers::AddTimer(const std::shared_ptr
<CPVRTimerInfoTag
>& item
) const
199 if (!item
->Channel() && !item
->GetTimerType()->IsEpgBasedTimerRule())
201 CLog::LogF(LOGERROR
, "No channel given");
202 HELPERS::ShowOKDialogText(
205 19109}); // "Error", "Could not save the timer. Check the log for more information about this message."
209 if (!item
->IsTimerRule() && item
->GetEpgInfoTag() && !item
->GetEpgInfoTag()->IsRecordable())
211 HELPERS::ShowOKDialogText(
213 CVariant
{19189}); // "Information", "The PVR backend does not allow to record this event."
217 if (CServiceBroker::GetPVRManager().Get
<PVR::GUI::Parental
>().CheckParentalLock(
218 item
->Channel()) != ParentalCheckResult::SUCCESS
)
221 if (!CServiceBroker::GetPVRManager().Timers()->AddTimer(item
))
223 HELPERS::ShowOKDialogText(
226 19109}); // "Error", "Could not save the timer. Check the log for more information about this message."
235 enum PVRRECORD_INSTANTRECORDACTION
238 RECORD_CURRENT_SHOW
= 0,
239 RECORD_INSTANTRECORDTIME
= 1,
241 RECORD_30_MINUTES
= 3,
242 RECORD_60_MINUTES
= 4,
243 RECORD_120_MINUTES
= 5,
247 class InstantRecordingActionSelector
250 explicit InstantRecordingActionSelector(int iInstantRecordTime
);
251 virtual ~InstantRecordingActionSelector() = default;
253 void AddAction(PVRRECORD_INSTANTRECORDACTION eAction
, const std::string
& title
);
254 void PreSelectAction(PVRRECORD_INSTANTRECORDACTION eAction
);
255 PVRRECORD_INSTANTRECORDACTION
Select();
258 int m_iInstantRecordTime
;
259 CGUIDialogSelect
* m_pDlgSelect
; // not owner!
260 std::map
<PVRRECORD_INSTANTRECORDACTION
, int> m_actions
;
263 InstantRecordingActionSelector::InstantRecordingActionSelector(int iInstantRecordTime
)
264 : m_iInstantRecordTime(iInstantRecordTime
),
265 m_pDlgSelect(CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogSelect
>(
266 WINDOW_DIALOG_SELECT
))
270 m_pDlgSelect
->Reset();
271 m_pDlgSelect
->SetMultiSelection(false);
272 m_pDlgSelect
->SetHeading(CVariant
{19086}); // Instant recording action
276 CLog::LogF(LOGERROR
, "Unable to obtain WINDOW_DIALOG_SELECT instance");
280 void InstantRecordingActionSelector::AddAction(PVRRECORD_INSTANTRECORDACTION eAction
,
281 const std::string
& title
)
283 if (m_actions
.find(eAction
) == m_actions
.end())
287 case RECORD_INSTANTRECORDTIME
:
289 StringUtils::Format(g_localizeStrings
.Get(19090),
290 m_iInstantRecordTime
)); // Record next <default duration> minutes
292 case RECORD_30_MINUTES
:
294 StringUtils::Format(g_localizeStrings
.Get(19090), 30)); // Record next 30 minutes
296 case RECORD_60_MINUTES
:
298 StringUtils::Format(g_localizeStrings
.Get(19090), 60)); // Record next 60 minutes
300 case RECORD_120_MINUTES
:
302 StringUtils::Format(g_localizeStrings
.Get(19090), 120)); // Record next 120 minutes
304 case RECORD_CURRENT_SHOW
:
305 m_pDlgSelect
->Add(StringUtils::Format(g_localizeStrings
.Get(19091),
306 title
)); // Record current show (<title>)
308 case RECORD_NEXT_SHOW
:
309 m_pDlgSelect
->Add(StringUtils::Format(g_localizeStrings
.Get(19092),
310 title
)); // Record next show (<title>)
318 m_actions
.insert(std::make_pair(eAction
, static_cast<int>(m_actions
.size())));
322 void InstantRecordingActionSelector::PreSelectAction(PVRRECORD_INSTANTRECORDACTION eAction
)
324 const auto& it
= m_actions
.find(eAction
);
325 if (it
!= m_actions
.end())
326 m_pDlgSelect
->SetSelected(it
->second
);
329 PVRRECORD_INSTANTRECORDACTION
InstantRecordingActionSelector::Select()
331 PVRRECORD_INSTANTRECORDACTION eAction
= NONE
;
333 m_pDlgSelect
->Open();
335 if (m_pDlgSelect
->IsConfirmed())
337 int iSelection
= m_pDlgSelect
->GetSelectedItem();
339 std::find_if(m_actions
.cbegin(), m_actions
.cend(),
340 [iSelection
](const auto& action
) { return action
.second
== iSelection
; });
342 if (it
!= m_actions
.cend())
343 eAction
= (*it
).first
;
349 } // unnamed namespace
351 bool CPVRGUIActionsTimers::ToggleRecordingOnPlayingChannel()
353 const std::shared_ptr
<CPVRChannel
> channel
=
354 CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel();
355 if (channel
&& channel
->CanRecord())
356 return SetRecordingOnChannel(
357 channel
, !CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*channel
));
362 bool CPVRGUIActionsTimers::SetRecordingOnChannel(const std::shared_ptr
<CPVRChannel
>& channel
,
365 bool bReturn
= false;
370 if (CServiceBroker::GetPVRManager().Get
<PVR::GUI::Parental
>().CheckParentalLock(channel
) !=
371 ParentalCheckResult::SUCCESS
)
374 const std::shared_ptr
<const CPVRClient
> client
=
375 CServiceBroker::GetPVRManager().GetClient(channel
->ClientID());
376 if (client
&& client
->GetClientCapabilities().SupportsTimers())
378 /* timers are supported on this channel */
379 if (bOnOff
&& !CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*channel
))
381 std::shared_ptr
<CPVREpgInfoTag
> epgTag
;
382 int iDuration
= m_settings
.GetIntValue(CSettings::SETTING_PVRRECORD_INSTANTRECORDTIME
);
384 int iAction
= m_settings
.GetIntValue(CSettings::SETTING_PVRRECORD_INSTANTRECORDACTION
);
387 case RECORD_CURRENT_SHOW
:
388 epgTag
= channel
->GetEPGNow();
391 case RECORD_INSTANTRECORDTIME
:
397 PVRRECORD_INSTANTRECORDACTION ePreselect
= RECORD_INSTANTRECORDTIME
;
398 const int iDurationDefault
=
399 m_settings
.GetIntValue(CSettings::SETTING_PVRRECORD_INSTANTRECORDTIME
);
400 InstantRecordingActionSelector
selector(iDurationDefault
);
401 std::shared_ptr
<CPVREpgInfoTag
> epgTagNext
;
403 // fixed length recordings
404 selector
.AddAction(RECORD_30_MINUTES
, "");
405 selector
.AddAction(RECORD_60_MINUTES
, "");
406 selector
.AddAction(RECORD_120_MINUTES
, "");
408 if (iDurationDefault
!= 30 && iDurationDefault
!= 60 && iDurationDefault
!= 120)
409 selector
.AddAction(RECORD_INSTANTRECORDTIME
, "");
411 // epg-based recordings
412 epgTag
= channel
->GetEPGNow();
415 bool bLocked
= CServiceBroker::GetPVRManager().IsParentalLocked(epgTag
);
418 const std::string currentTitle
=
419 bLocked
? g_localizeStrings
.Get(19266) /* Parental locked */ : epgTag
->Title();
420 selector
.AddAction(RECORD_CURRENT_SHOW
, currentTitle
);
421 ePreselect
= RECORD_CURRENT_SHOW
;
424 epgTagNext
= channel
->GetEPGNext();
427 const std::string nextTitle
= bLocked
428 ? g_localizeStrings
.Get(19266) /* Parental locked */
429 : epgTagNext
->Title();
430 selector
.AddAction(RECORD_NEXT_SHOW
, nextTitle
);
432 // be smart. if current show is almost over, preselect next show.
433 if (epgTag
->ProgressPercentage() > 90.0f
)
434 ePreselect
= RECORD_NEXT_SHOW
;
438 if (ePreselect
== RECORD_INSTANTRECORDTIME
)
440 if (iDurationDefault
== 30)
441 ePreselect
= RECORD_30_MINUTES
;
442 else if (iDurationDefault
== 60)
443 ePreselect
= RECORD_60_MINUTES
;
444 else if (iDurationDefault
== 120)
445 ePreselect
= RECORD_120_MINUTES
;
448 selector
.PreSelectAction(ePreselect
);
450 PVRRECORD_INSTANTRECORDACTION eSelected
= selector
.Select();
454 return false; // dialog canceled
456 case RECORD_30_MINUTES
:
461 case RECORD_60_MINUTES
:
466 case RECORD_120_MINUTES
:
471 case RECORD_INSTANTRECORDTIME
:
472 iDuration
= iDurationDefault
;
476 case RECORD_CURRENT_SHOW
:
479 case RECORD_NEXT_SHOW
:
485 "Unknown instant record action selection ({}), defaulting to fixed "
487 static_cast<int>(eSelected
));
496 "Unknown instant record action setting value ({}), defaulting to fixed "
502 const std::shared_ptr
<CPVRTimerInfoTag
> newTimer(
503 epgTag
? CPVRTimerInfoTag::CreateFromEpg(epgTag
, false)
504 : CPVRTimerInfoTag::CreateInstantTimerTag(channel
, iDuration
));
507 bReturn
= CServiceBroker::GetPVRManager().Timers()->AddTimer(newTimer
);
510 HELPERS::ShowOKDialogText(
513 19164}); // "Error", "Could not start recording. Check the log for more information about this message."
515 else if (!bOnOff
&& CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*channel
))
517 /* delete active timers */
519 CServiceBroker::GetPVRManager().Timers()->DeleteTimersOnChannel(channel
, true, true);
522 HELPERS::ShowOKDialogText(
525 19170}); // "Error", "Could not stop recording. Check the log for more information about this message."
532 bool CPVRGUIActionsTimers::ToggleTimer(const CFileItem
& item
) const
534 if (!item
.HasEPGInfoTag())
537 const std::shared_ptr
<const CPVRTimerInfoTag
> timer(CPVRItem(item
).GetTimerInfoTag());
540 if (timer
->IsRecording())
541 return StopRecording(item
);
543 return DeleteTimer(item
);
546 return AddTimer(item
, false);
549 bool CPVRGUIActionsTimers::ToggleTimerState(const CFileItem
& item
) const
551 if (!item
.HasPVRTimerInfoTag())
554 const std::shared_ptr
<CPVRTimerInfoTag
> timer
= item
.GetPVRTimerInfoTag();
555 if (timer
->IsDisabled())
556 timer
->SetState(PVR_TIMER_STATE_SCHEDULED
);
558 timer
->SetState(PVR_TIMER_STATE_DISABLED
);
560 if (CServiceBroker::GetPVRManager().Timers()->UpdateTimer(timer
))
563 HELPERS::ShowOKDialogText(
566 19263}); // "Error", "Could not update the timer. Check the log for more information about this message."
570 bool CPVRGUIActionsTimers::EditTimer(const CFileItem
& item
) const
572 const std::shared_ptr
<CPVRTimerInfoTag
> timer(CPVRItem(item
).GetTimerInfoTag());
575 CLog::LogF(LOGERROR
, "No timer!");
580 const std::shared_ptr
<CPVRTimerInfoTag
> newTimer(new CPVRTimerInfoTag
);
581 newTimer
->UpdateEntry(timer
);
583 if (ShowTimerSettings(newTimer
) &&
584 (!timer
->GetTimerType()->IsReadOnly() || timer
->GetTimerType()->SupportsEnableDisable()))
586 if (newTimer
->GetTimerType() == timer
->GetTimerType())
588 if (CServiceBroker::GetPVRManager().Timers()->UpdateTimer(newTimer
))
591 HELPERS::ShowOKDialogText(
594 19263}); // "Error", "Could not update the timer. Check the log for more information about this message."
599 // timer type changed. delete the original timer, then create the new timer. this order is
600 // important. for instance, the new timer might be a rule which schedules the original timer.
601 // deleting the original timer after creating the rule would do literally this and we would
602 // end up with one timer missing wrt to the rule defined by the new timer.
603 if (DeleteTimer(timer
, timer
->IsRecording(), false))
605 if (AddTimer(newTimer
))
609 return AddTimer(timer
);
616 bool CPVRGUIActionsTimers::EditTimerRule(const CFileItem
& item
) const
618 const std::shared_ptr
<CFileItem
> parentTimer
= GetTimerRule(item
);
620 return EditTimer(*parentTimer
);
625 std::shared_ptr
<CFileItem
> CPVRGUIActionsTimers::GetTimerRule(const CFileItem
& item
) const
627 std::shared_ptr
<CPVRTimerInfoTag
> timer
;
628 if (item
.HasEPGInfoTag())
629 timer
= CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(item
.GetEPGInfoTag());
630 else if (item
.HasPVRTimerInfoTag())
631 timer
= item
.GetPVRTimerInfoTag();
635 timer
= CServiceBroker::GetPVRManager().Timers()->GetTimerRule(timer
);
637 return std::make_shared
<CFileItem
>(timer
);
642 bool CPVRGUIActionsTimers::DeleteTimer(const CFileItem
& item
) const
644 return DeleteTimer(item
, false, false);
647 bool CPVRGUIActionsTimers::DeleteTimerRule(const CFileItem
& item
) const
649 return DeleteTimer(item
, false, true);
652 bool CPVRGUIActionsTimers::DeleteTimer(const CFileItem
& item
,
654 bool bDeleteRule
) const
656 std::shared_ptr
<CPVRTimerInfoTag
> timer
;
657 const std::shared_ptr
<const CPVRRecording
> recording(CPVRItem(item
).GetRecording());
659 timer
= recording
->GetRecordingTimer();
662 timer
= CPVRItem(item
).GetTimerInfoTag();
666 CLog::LogF(LOGERROR
, "No timer!");
670 if (bDeleteRule
&& !timer
->IsTimerRule())
671 timer
= CServiceBroker::GetPVRManager().Timers()->GetTimerRule(timer
);
675 CLog::LogF(LOGERROR
, "No timer rule!");
681 if (ConfirmStopRecording(timer
))
683 if (CServiceBroker::GetPVRManager().Timers()->DeleteTimer(timer
, true, false) ==
684 TimerOperationResult::OK
)
687 HELPERS::ShowOKDialogText(
690 19170}); // "Error", "Could not stop recording. Check the log for more information about this message."
694 else if (!timer
->GetTimerType()->AllowsDelete())
700 bool bAlsoDeleteRule(false);
701 if (ConfirmDeleteTimer(timer
, bAlsoDeleteRule
))
702 return DeleteTimer(timer
, false, bAlsoDeleteRule
);
707 bool CPVRGUIActionsTimers::DeleteTimer(const std::shared_ptr
<CPVRTimerInfoTag
>& timer
,
709 bool bDeleteRule
) const
711 TimerOperationResult result
=
712 CServiceBroker::GetPVRManager().Timers()->DeleteTimer(timer
, bIsRecording
, bDeleteRule
);
715 case TimerOperationResult::RECORDING
:
717 // recording running. ask the user if it should be deleted anyway
718 if (HELPERS::ShowYesNoDialogText(
719 CVariant
{122}, // "Confirm delete"
721 19122}) // "This timer is still recording. Are you sure you want to delete this timer?"
722 != HELPERS::DialogResponse::CHOICE_YES
)
725 return DeleteTimer(timer
, true, bDeleteRule
);
727 case TimerOperationResult::OK
:
731 case TimerOperationResult::FAILED
:
733 HELPERS::ShowOKDialogText(
736 19110}); // "Error", "Could not delete the timer. Check the log for more information about this message."
741 CLog::LogF(LOGERROR
, "Unhandled TimerOperationResult ({})!", static_cast<int>(result
));
748 bool CPVRGUIActionsTimers::ConfirmDeleteTimer(const std::shared_ptr
<const CPVRTimerInfoTag
>& timer
,
749 bool& bDeleteRule
) const
751 bool bConfirmed(false);
752 const std::shared_ptr
<const CPVRTimerInfoTag
> parentTimer(
753 CServiceBroker::GetPVRManager().Timers()->GetTimerRule(timer
));
755 if (parentTimer
&& parentTimer
->GetTimerType()->AllowsDelete())
757 // timer was scheduled by a deletable timer rule. prompt user for confirmation for deleting the timer rule, including scheduled timers.
759 bDeleteRule
= CGUIDialogYesNo::ShowAndGetInput(
760 CVariant
{122}, // "Confirm delete"
762 840}, // "Do you want to delete only this timer or also the timer rule that has scheduled it?"
763 CVariant
{""}, CVariant
{timer
->Title()}, bCancel
, CVariant
{841}, // "Only this"
764 CVariant
{593}, // "All"
766 bConfirmed
= !bCancel
;
772 // prompt user for confirmation for deleting the timer
773 bConfirmed
= CGUIDialogYesNo::ShowAndGetInput(
774 CVariant
{122}, // "Confirm delete"
777 // "Are you sure you want to delete this timer rule and all timers it has scheduled?"
778 : CVariant
{846}, // "Are you sure you want to delete this timer?"
779 CVariant
{""}, CVariant
{timer
->Title()});
785 bool CPVRGUIActionsTimers::StopRecording(const CFileItem
& item
) const
787 if (!DeleteTimer(item
, true, false))
790 CServiceBroker::GetPVRManager().TriggerRecordingsUpdate();
794 bool CPVRGUIActionsTimers::ConfirmStopRecording(
795 const std::shared_ptr
<const CPVRTimerInfoTag
>& timer
) const
797 return CGUIDialogYesNo::ShowAndGetInput(
798 CVariant
{847}, // "Confirm stop recording"
799 CVariant
{848}, // "Are you sure you want to stop this recording?"
800 CVariant
{""}, CVariant
{timer
->Title()});
805 std::string
GetAnnouncerText(const std::shared_ptr
<const CPVRTimerInfoTag
>& timer
,
810 if (timer
->IsEpgBased())
812 text
= StringUtils::Format(g_localizeStrings
.Get(idEpg
),
813 timer
->Title(), // tv show title
814 timer
->ChannelName(),
815 timer
->StartAsLocalTime().GetAsLocalizedDateTime(false, false));
819 text
= StringUtils::Format(g_localizeStrings
.Get(idNoEpg
), timer
->ChannelName(),
820 timer
->StartAsLocalTime().GetAsLocalizedDateTime(false, false));
825 void AddEventLogEntry(const std::shared_ptr
<const CPVRTimerInfoTag
>& timer
, int idEpg
, int idNoEpg
)
830 const std::shared_ptr
<const CPVRClient
> client
=
831 CServiceBroker::GetPVRManager().GetClient(timer
->GetTimerType()->GetClientId());
834 name
= client
->GetFriendlyName();
835 icon
= client
->Icon();
839 name
= g_sysinfo
.GetAppName();
840 icon
= "special://xbmc/media/icon256x256.png";
843 CPVREventLogJob
* job
= new CPVREventLogJob
;
844 job
->AddEvent(false, // do not display a toast, only log event
845 EventLevel::Information
, // info, no error
846 name
, GetAnnouncerText(timer
, idEpg
, idNoEpg
), icon
);
847 CServiceBroker::GetJobManager()->AddJob(job
, nullptr);
849 } // unnamed namespace
851 void CPVRGUIActionsTimers::AnnounceReminder(const std::shared_ptr
<CPVRTimerInfoTag
>& timer
) const
853 if (!timer
->IsReminder())
855 CLog::LogF(LOGERROR
, "No reminder timer!");
859 if (timer
->EndAsUTC() < CDateTime::GetUTCDateTime())
861 // expired. timer end is in the past. write event log entry.
862 AddEventLogEntry(timer
, 19305, 19306); // Deleted missed PVR reminder ...
866 if (CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingChannel(timer
->Channel()))
868 // no need for an announcement. channel in question is already playing.
872 // show the reminder dialog
873 CGUIDialogProgress
* dialog
=
874 CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogProgress
>(
875 WINDOW_DIALOG_PROGRESS
);
881 dialog
->SetHeading(CVariant
{19312}); // "PVR reminder"
882 dialog
->ShowChoice(0, CVariant
{19165}); // "Switch"
884 std::string text
= GetAnnouncerText(timer
, 19307, 19308); // Reminder for ...
886 bool bCanRecord
= false;
887 const std::shared_ptr
<const CPVRClient
> client
=
888 CServiceBroker::GetPVRManager().GetClient(timer
->ClientID());
889 if (client
&& client
->GetClientCapabilities().SupportsTimers())
892 dialog
->ShowChoice(1, CVariant
{264}); // "Record"
893 dialog
->ShowChoice(2, CVariant
{222}); // "Cancel"
895 if (m_settings
.GetBoolValue(CSettings::SETTING_PVRREMINDERS_AUTORECORD
))
896 text
+= "\n\n" + g_localizeStrings
.Get(
897 19309); // (Auto-close of this reminder will schedule a recording...)
898 else if (m_settings
.GetBoolValue(CSettings::SETTING_PVRREMINDERS_AUTOSWITCH
))
899 text
+= "\n\n" + g_localizeStrings
.Get(
900 19331); // (Auto-close of this reminder will switch to channel...)
904 dialog
->ShowChoice(1, CVariant
{222}); // "Cancel"
907 dialog
->SetText(text
);
908 dialog
->SetPercentage(100);
912 int result
= CGUIDialogProgress::CHOICE_NONE
;
914 static constexpr int PROGRESS_TIMESLICE_MILLISECS
= 50;
916 const int iWait
= m_settings
.GetIntValue(CSettings::SETTING_PVRREMINDERS_AUTOCLOSEDELAY
) * 1000;
917 int iRemaining
= iWait
;
918 while (iRemaining
> 0)
920 result
= dialog
->GetChoice();
921 if (result
!= CGUIDialogProgress::CHOICE_NONE
)
924 std::this_thread::sleep_for(std::chrono::milliseconds(PROGRESS_TIMESLICE_MILLISECS
));
926 iRemaining
-= PROGRESS_TIMESLICE_MILLISECS
;
927 dialog
->SetPercentage(iRemaining
* 100 / iWait
);
933 bool bAutoClosed
= (iRemaining
<= 0);
934 bool bSwitch
= (result
== 0);
935 bool bRecord
= (result
== 1);
939 bRecord
= (bCanRecord
&& m_settings
.GetBoolValue(CSettings::SETTING_PVRREMINDERS_AUTORECORD
));
940 bSwitch
= m_settings
.GetBoolValue(CSettings::SETTING_PVRREMINDERS_AUTOSWITCH
);
945 std::shared_ptr
<CPVRTimerInfoTag
> newTimer
;
947 std::shared_ptr
<CPVREpgInfoTag
> epgTag
= timer
->GetEpgInfoTag();
950 newTimer
= CPVRTimerInfoTag::CreateFromEpg(epgTag
, false);
953 // an epgtag can only have max one timer - we need to clear the reminder to be able to
954 // attach the recording timer
955 DeleteTimer(timer
, false, false);
960 int iDuration
= (timer
->EndAsUTC() - timer
->StartAsUTC()).GetSecondsTotal() / 60;
961 newTimer
= CPVRTimerInfoTag::CreateTimerTag(timer
->Channel(), timer
->StartAsUTC(), iDuration
);
966 // schedule recording
967 AddTimer(CFileItem(newTimer
), false);
972 AddEventLogEntry(timer
, 19310,
973 19311); // Scheduled recording for auto-closed PVR reminder ...
979 const std::shared_ptr
<CPVRChannelGroupMember
> groupMember
=
980 CServiceBroker::GetPVRManager().Get
<PVR::GUI::Channels
>().GetChannelGroupMember(
984 CServiceBroker::GetPVRManager().Get
<PVR::GUI::Playback
>().SwitchToChannel(
985 CFileItem(groupMember
), false);
989 AddEventLogEntry(timer
, 19332,
990 19333); // Switched channel for auto-closed PVR reminder ...
996 void CPVRGUIActionsTimers::AnnounceReminders() const
998 // Prevent multiple yesno dialogs, all on same call stack, due to gui message processing while dialog is open.
999 if (m_bReminderAnnouncementRunning
)
1002 m_bReminderAnnouncementRunning
= true;
1003 std::shared_ptr
<CPVRTimerInfoTag
> timer
=
1004 CServiceBroker::GetPVRManager().Timers()->GetNextReminderToAnnnounce();
1007 AnnounceReminder(timer
);
1008 timer
= CServiceBroker::GetPVRManager().Timers()->GetNextReminderToAnnnounce();
1010 m_bReminderAnnouncementRunning
= false;