1 /*
2  *  Copyright (C) 2017-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "PVRGUIChannelNavigator.h"
10 
11 #include "FileItem.h"
12 #include "GUIInfoManager.h"
13 #include "ServiceBroker.h"
14 #include "guilib/GUIComponent.h"
15 #include "pvr/PVRManager.h"
16 #include "pvr/PVRPlaybackState.h"
17 #include "pvr/channels/PVRChannelGroup.h"
18 #include "pvr/guilib/PVRGUIActions.h"
19 #include "settings/Settings.h"
20 #include "settings/SettingsComponent.h"
21 #include "threads/SingleLock.h"
22 #include "utils/Job.h"
23 #include "utils/JobManager.h"
24 #include "utils/XTimeUtils.h"
25 
26 namespace
27 {
28 class CPVRChannelTimeoutJobBase : public CJob, public IJobCallback
29 {
30 public:
31   CPVRChannelTimeoutJobBase() = delete;
CPVRChannelTimeoutJobBase(PVR::CPVRGUIChannelNavigator & channelNavigator,int iTimeout)32   CPVRChannelTimeoutJobBase(PVR::CPVRGUIChannelNavigator& channelNavigator, int iTimeout)
33   : m_channelNavigator(channelNavigator)
34   {
35     m_delayTimer.Set(iTimeout);
36   }
37 
38   ~CPVRChannelTimeoutJobBase() override = default;
39 
40   virtual void OnTimeout() = 0;
41 
OnJobComplete(unsigned int iJobID,bool bSuccess,CJob * job)42   void OnJobComplete(unsigned int iJobID, bool bSuccess, CJob* job) override {}
43 
DoWork()44   bool DoWork() override
45   {
46     while (!ShouldCancel(0, 0))
47     {
48       if (m_delayTimer.IsTimePast())
49       {
50         OnTimeout();
51         return true;
52       }
53       KODI::TIME::Sleep(10);
54     }
55     return false;
56   }
57 
58 protected:
59   PVR::CPVRGUIChannelNavigator& m_channelNavigator;
60 
61 private:
62   XbmcThreads::EndTime m_delayTimer;
63 };
64 
65 class CPVRChannelEntryTimeoutJob : public CPVRChannelTimeoutJobBase
66 {
67 public:
CPVRChannelEntryTimeoutJob(PVR::CPVRGUIChannelNavigator & channelNavigator,int iTimeout)68   CPVRChannelEntryTimeoutJob(PVR::CPVRGUIChannelNavigator& channelNavigator, int iTimeout)
69   : CPVRChannelTimeoutJobBase(channelNavigator, iTimeout) {}
70   ~CPVRChannelEntryTimeoutJob() override = default;
GetType() const71   const char* GetType() const override { return "pvr-channel-entry-timeout-job"; }
OnTimeout()72   void OnTimeout() override { m_channelNavigator.SwitchToCurrentChannel(); }
73 };
74 
75 class CPVRChannelInfoTimeoutJob : public CPVRChannelTimeoutJobBase
76 {
77 public:
CPVRChannelInfoTimeoutJob(PVR::CPVRGUIChannelNavigator & channelNavigator,int iTimeout)78   CPVRChannelInfoTimeoutJob(PVR::CPVRGUIChannelNavigator& channelNavigator, int iTimeout)
79   : CPVRChannelTimeoutJobBase(channelNavigator, iTimeout) {}
80   ~CPVRChannelInfoTimeoutJob() override = default;
GetType() const81   const char* GetType() const override { return "pvr-channel-info-timeout-job"; }
OnTimeout()82   void OnTimeout() override { m_channelNavigator.HideInfo(); }
83 };
84 } // unnamed namespace
85 
86 namespace PVR
87 {
SelectNextChannel(ChannelSwitchMode eSwitchMode)88   void CPVRGUIChannelNavigator::SelectNextChannel(ChannelSwitchMode eSwitchMode)
89   {
90     if (!CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetPlayerInfoProvider().GetShowInfo() && eSwitchMode == ChannelSwitchMode::NO_SWITCH)
91     {
92       // show info for current channel on first next channel selection.
93       ShowInfo(false);
94       return;
95     }
96 
97     const std::shared_ptr<CPVRChannel> nextChannel = GetNextOrPrevChannel(true);
98     if (nextChannel)
99       SelectChannel(nextChannel, eSwitchMode);
100   }
101 
SelectPreviousChannel(ChannelSwitchMode eSwitchMode)102   void CPVRGUIChannelNavigator::SelectPreviousChannel(ChannelSwitchMode eSwitchMode)
103   {
104     if (!CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetPlayerInfoProvider().GetShowInfo() && eSwitchMode == ChannelSwitchMode::NO_SWITCH)
105     {
106       // show info for current channel on first previous channel selection.
107       ShowInfo(false);
108       return;
109     }
110 
111     const std::shared_ptr<CPVRChannel> prevChannel = GetNextOrPrevChannel(false);
112     if (prevChannel)
113       SelectChannel(prevChannel, eSwitchMode);
114   }
115 
GetNextOrPrevChannel(bool bNext)116   std::shared_ptr<CPVRChannel> CPVRGUIChannelNavigator::GetNextOrPrevChannel(bool bNext)
117   {
118     const bool bPlayingRadio = CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingRadio();
119     const bool bPlayingTV = CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingTV();
120 
121     if (bPlayingTV || bPlayingRadio)
122     {
123       const std::shared_ptr<CPVRChannelGroup> group = CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingGroup(bPlayingRadio);
124       if (group)
125       {
126         CSingleLock lock(m_critSection);
127         return bNext ? group->GetNextChannel(m_currentChannel) : group->GetPreviousChannel(m_currentChannel);
128       }
129     }
130     return {};
131   }
132 
SelectChannel(const std::shared_ptr<CPVRChannel> & channel,ChannelSwitchMode eSwitchMode)133   void CPVRGUIChannelNavigator::SelectChannel(const std::shared_ptr<CPVRChannel>& channel,
134                                               ChannelSwitchMode eSwitchMode)
135   {
136     CServiceBroker::GetGUI()->GetInfoManager().SetCurrentItem(CFileItem(channel));
137 
138     CSingleLock lock(m_critSection);
139     m_currentChannel = channel;
140     ShowInfo(false);
141 
142     if (IsPreview() && eSwitchMode == ChannelSwitchMode::INSTANT_OR_DELAYED_SWITCH)
143     {
144       int iTimeout = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_PVRPLAYBACK_CHANNELENTRYTIMEOUT);
145       if (iTimeout > 0)
146       {
147         // delayed switch
148         if (m_iChannelEntryJobId >= 0)
149           CJobManager::GetInstance().CancelJob(m_iChannelEntryJobId);
150 
151         CPVRChannelEntryTimeoutJob* job = new CPVRChannelEntryTimeoutJob(*this, iTimeout);
152         m_iChannelEntryJobId = CJobManager::GetInstance().AddJob(job, dynamic_cast<IJobCallback*>(job));
153       }
154       else
155       {
156         // instant switch
157         SwitchToCurrentChannel();
158       }
159     }
160   }
161 
SwitchToCurrentChannel()162   void CPVRGUIChannelNavigator::SwitchToCurrentChannel()
163   {
164     CFileItemPtr item;
165 
166     {
167       CSingleLock lock(m_critSection);
168 
169       if (m_iChannelEntryJobId >= 0)
170       {
171         CJobManager::GetInstance().CancelJob(m_iChannelEntryJobId);
172         m_iChannelEntryJobId = -1;
173       }
174 
175       item.reset(new CFileItem(m_currentChannel));
176     }
177 
178     if (item)
179       CServiceBroker::GetPVRManager().GUIActions()->SwitchToChannel(item, false);
180   }
181 
IsPreview() const182   bool CPVRGUIChannelNavigator::IsPreview() const
183   {
184     CSingleLock lock(m_critSection);
185     return m_currentChannel != m_playingChannel;
186   }
187 
IsPreviewAndShowInfo() const188   bool CPVRGUIChannelNavigator::IsPreviewAndShowInfo() const
189   {
190     return IsPreview() && CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetPlayerInfoProvider().GetShowInfo();
191   }
192 
ShowInfo()193   void CPVRGUIChannelNavigator::ShowInfo()
194   {
195     ShowInfo(true);
196   }
197 
ShowInfo(bool bForce)198   void CPVRGUIChannelNavigator::ShowInfo(bool bForce)
199   {
200     int iTimeout = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_PVRMENU_DISPLAYCHANNELINFO);
201 
202     if (bForce || iTimeout > 0)
203     {
204       CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetPlayerInfoProvider().SetShowInfo(true);
205 
206       CSingleLock lock(m_critSection);
207 
208       if (m_iChannelInfoJobId >= 0)
209       {
210         CJobManager::GetInstance().CancelJob(m_iChannelInfoJobId);
211         m_iChannelInfoJobId = -1;
212       }
213 
214       if (!bForce && iTimeout > 0)
215       {
216         CPVRChannelInfoTimeoutJob* job = new CPVRChannelInfoTimeoutJob(*this, iTimeout * 1000);
217         m_iChannelInfoJobId = CJobManager::GetInstance().AddJob(job, dynamic_cast<IJobCallback*>(job));
218       }
219     }
220   }
221 
HideInfo()222   void CPVRGUIChannelNavigator::HideInfo()
223   {
224     CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetPlayerInfoProvider().SetShowInfo(false);
225 
226     CFileItemPtr item;
227 
228     {
229       CSingleLock lock(m_critSection);
230 
231       if (m_iChannelInfoJobId >= 0)
232       {
233         CJobManager::GetInstance().CancelJob(m_iChannelInfoJobId);
234         m_iChannelInfoJobId = -1;
235       }
236 
237       if (m_currentChannel != m_playingChannel)
238       {
239         m_currentChannel = m_playingChannel;
240         if (m_playingChannel)
241           item.reset(new CFileItem(m_playingChannel));
242       }
243     }
244 
245     if (item)
246       CServiceBroker::GetGUI()->GetInfoManager().SetCurrentItem(*item);
247   }
248 
ToggleInfo()249   void CPVRGUIChannelNavigator::ToggleInfo()
250   {
251     if (CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetPlayerInfoProvider().GetShowInfo())
252       HideInfo();
253     else
254       ShowInfo();
255   }
256 
SetPlayingChannel(const std::shared_ptr<CPVRChannel> & channel)257   void CPVRGUIChannelNavigator::SetPlayingChannel(const std::shared_ptr<CPVRChannel>& channel)
258   {
259     CFileItemPtr item;
260 
261     if (channel)
262     {
263       CSingleLock lock(m_critSection);
264 
265       m_playingChannel = channel;
266       if (m_currentChannel != m_playingChannel)
267       {
268         m_currentChannel = m_playingChannel;
269         if (m_playingChannel)
270           item.reset(new CFileItem(m_playingChannel));
271       }
272     }
273 
274     if (item)
275       CServiceBroker::GetGUI()->GetInfoManager().SetCurrentItem(*item);
276 
277     ShowInfo(false);
278   }
279 
ClearPlayingChannel()280   void CPVRGUIChannelNavigator::ClearPlayingChannel()
281   {
282     CSingleLock lock(m_critSection);
283     m_playingChannel.reset();
284     HideInfo();
285   }
286 
287 } // namespace PVR
288