1 /*
2  *  Copyright (C) 2005-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 "PVRGUIActionListener.h"
10 
11 #include "Application.h"
12 #include "ServiceBroker.h"
13 #include "dialogs/GUIDialogNumeric.h"
14 #include "guilib/GUIComponent.h"
15 #include "guilib/GUIWindowManager.h"
16 #include "guilib/WindowIDs.h"
17 #include "input/actions/Action.h"
18 #include "input/actions/ActionIDs.h"
19 #include "messaging/ApplicationMessenger.h"
20 #include "pvr/PVRManager.h"
21 #include "pvr/PVRPlaybackState.h"
22 #include "pvr/addons/PVRClients.h"
23 #include "pvr/channels/PVRChannel.h"
24 #include "pvr/channels/PVRChannelGroup.h"
25 #include "pvr/channels/PVRChannelGroups.h"
26 #include "pvr/channels/PVRChannelGroupsContainer.h"
27 #include "pvr/guilib/PVRGUIActions.h"
28 #include "settings/Settings.h"
29 #include "settings/SettingsComponent.h"
30 #include "settings/lib/Setting.h"
31 
32 #include <memory>
33 #include <string>
34 
35 namespace PVR
36 {
37 
CPVRGUIActionListener()38 CPVRGUIActionListener::CPVRGUIActionListener()
39 {
40   g_application.RegisterActionListener(this);
41   CServiceBroker::GetSettingsComponent()->GetSettings()->RegisterCallback(this, {
42     CSettings::SETTING_PVRPARENTAL_ENABLED,
43     CSettings::SETTING_PVRMANAGER_RESETDB,
44     CSettings::SETTING_EPG_RESETEPG,
45     CSettings::SETTING_PVRMANAGER_CLIENTPRIORITIES,
46     CSettings::SETTING_PVRMANAGER_CHANNELMANAGER,
47     CSettings::SETTING_PVRMANAGER_GROUPMANAGER,
48     CSettings::SETTING_PVRMANAGER_CHANNELSCAN,
49     CSettings::SETTING_PVRMENU_SEARCHICONS,
50     CSettings::SETTING_PVRCLIENT_MENUHOOK,
51     CSettings::SETTING_EPG_PAST_DAYSTODISPLAY,
52     CSettings::SETTING_EPG_FUTURE_DAYSTODISPLAY
53   });
54 }
55 
~CPVRGUIActionListener()56 CPVRGUIActionListener::~CPVRGUIActionListener()
57 {
58   CServiceBroker::GetSettingsComponent()->GetSettings()->UnregisterCallback(this);
59   g_application.UnregisterActionListener(this);
60 }
61 
Init(CPVRManager & mgr)62 void CPVRGUIActionListener::Init(CPVRManager& mgr)
63 {
64   mgr.Events().Subscribe(this, &CPVRGUIActionListener::OnPVRManagerEvent);
65 }
66 
Deinit(CPVRManager & mgr)67 void CPVRGUIActionListener::Deinit(CPVRManager& mgr)
68 {
69   mgr.Events().Unsubscribe(this);
70 }
71 
OnPVRManagerEvent(const PVREvent & event)72 void CPVRGUIActionListener::OnPVRManagerEvent(const PVREvent& event)
73 {
74   if (event == PVREvent::AnnounceReminder)
75   {
76     if (g_application.IsInitialized())
77     {
78       // if GUI is ready, dispatch to GUI thread and handle the action there
79       KODI::MESSAGING::CApplicationMessenger::GetInstance().PostMsg(
80           TMSG_GUI_ACTION, WINDOW_INVALID, -1,
81           static_cast<void*>(new CAction(ACTION_PVR_ANNOUNCE_REMINDERS)));
82     }
83   }
84 }
85 
GetChannelSwitchMode(int iAction)86 ChannelSwitchMode CPVRGUIActionListener::GetChannelSwitchMode(int iAction)
87 {
88   if ((iAction == ACTION_MOVE_UP || iAction == ACTION_MOVE_DOWN) &&
89       CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_PVRPLAYBACK_CONFIRMCHANNELSWITCH))
90     return ChannelSwitchMode::NO_SWITCH;
91 
92   return ChannelSwitchMode::INSTANT_OR_DELAYED_SWITCH;
93 }
94 
OnAction(const CAction & action)95 bool CPVRGUIActionListener::OnAction(const CAction& action)
96 {
97   bool bIsJumpSMS = false;
98   bool bIsPlayingPVR = CServiceBroker::GetPVRManager().PlaybackState()->IsPlaying() &&
99                        g_application.CurrentFileItem().HasPVRChannelInfoTag();
100 
101   switch (action.GetID())
102   {
103     case ACTION_PVR_PLAY:
104     case ACTION_PVR_PLAY_TV:
105     case ACTION_PVR_PLAY_RADIO:
106     {
107       // see if we're already playing a PVR stream and if not or the stream type
108       // doesn't match the demanded type, start playback of according type
109       switch (action.GetID())
110       {
111         case ACTION_PVR_PLAY:
112           if (!bIsPlayingPVR)
113             CServiceBroker::GetPVRManager().GUIActions()->SwitchToChannel(PlaybackTypeAny);
114           break;
115         case ACTION_PVR_PLAY_TV:
116           if (!bIsPlayingPVR || g_application.CurrentFileItem().GetPVRChannelInfoTag()->IsRadio())
117             CServiceBroker::GetPVRManager().GUIActions()->SwitchToChannel(PlaybackTypeTV);
118           break;
119         case ACTION_PVR_PLAY_RADIO:
120           if (!bIsPlayingPVR || !g_application.CurrentFileItem().GetPVRChannelInfoTag()->IsRadio())
121             CServiceBroker::GetPVRManager().GUIActions()->SwitchToChannel(PlaybackTypeRadio);
122           break;
123       }
124       return true;
125     }
126 
127     case ACTION_JUMP_SMS2:
128     case ACTION_JUMP_SMS3:
129     case ACTION_JUMP_SMS4:
130     case ACTION_JUMP_SMS5:
131     case ACTION_JUMP_SMS6:
132     case ACTION_JUMP_SMS7:
133     case ACTION_JUMP_SMS8:
134     case ACTION_JUMP_SMS9:
135       bIsJumpSMS = true;
136       // fallthru is intended
137     case REMOTE_0:
138     case REMOTE_1:
139     case REMOTE_2:
140     case REMOTE_3:
141     case REMOTE_4:
142     case REMOTE_5:
143     case REMOTE_6:
144     case REMOTE_7:
145     case REMOTE_8:
146     case REMOTE_9:
147     case ACTION_CHANNEL_NUMBER_SEP:
148     {
149       if (!bIsPlayingPVR)
150         return false;
151 
152       if (CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_FULLSCREEN_VIDEO) || CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_VISUALISATION))
153       {
154         // do not consume action if a python modal is the top most dialog
155         // as a python modal can't return that it consumed the action.
156         if (CServiceBroker::GetGUI()->GetWindowManager().IsPythonWindow(CServiceBroker::GetGUI()->GetWindowManager().GetTopmostModalDialog()))
157           return false;
158 
159         char cCharacter;
160         if (action.GetID() == ACTION_CHANNEL_NUMBER_SEP)
161         {
162           cCharacter = CPVRChannelNumber::SEPARATOR;
163         }
164         else
165         {
166           int iRemote = bIsJumpSMS ? action.GetID() - (ACTION_JUMP_SMS2 - REMOTE_2) : action.GetID();
167           cCharacter = (iRemote - REMOTE_0) + '0';
168         }
169         CServiceBroker::GetPVRManager().GUIActions()->GetChannelNumberInputHandler().AppendChannelNumberCharacter(cCharacter);
170         return true;
171       }
172       return false;
173     }
174 
175     case ACTION_SHOW_INFO:
176     {
177       if (!bIsPlayingPVR)
178         return false;
179 
180       CServiceBroker::GetPVRManager().GUIActions()->GetChannelNavigator().ToggleInfo();
181       return true;
182     }
183 
184     case ACTION_SELECT_ITEM:
185     {
186       if (!bIsPlayingPVR)
187         return false;
188 
189       // If the button that caused this action matches action "Select" ...
190       if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_PVRPLAYBACK_CONFIRMCHANNELSWITCH) &&
191           CServiceBroker::GetPVRManager().GUIActions()->GetChannelNavigator().IsPreview())
192       {
193         // ... and if "confirm channel switch" setting is active and a channel
194         // preview is currently shown, switch to the currently previewed channel.
195         CServiceBroker::GetPVRManager().GUIActions()->GetChannelNavigator().SwitchToCurrentChannel();
196         return true;
197       }
198       else if (CServiceBroker::GetPVRManager().GUIActions()->GetChannelNumberInputHandler().CheckInputAndExecuteAction())
199       {
200         // ... or if the action was processed by direct channel number input, we're done.
201         return true;
202       }
203       return false;
204     }
205 
206     case ACTION_NEXT_ITEM:
207     {
208       if (!bIsPlayingPVR)
209         return false;
210 
211       CServiceBroker::GetPVRManager().GUIActions()->SeekForward();
212       return true;
213     }
214 
215     case ACTION_PREV_ITEM:
216     {
217       if (!bIsPlayingPVR)
218         return false;
219 
220       CServiceBroker::GetPVRManager().GUIActions()->SeekBackward(CApplication::ACTION_PREV_ITEM_THRESHOLD);
221       return true;
222     }
223 
224     case ACTION_MOVE_UP:
225     case ACTION_CHANNEL_UP:
226     {
227       if (!bIsPlayingPVR)
228         return false;
229 
230       CServiceBroker::GetPVRManager().GUIActions()->GetChannelNavigator().SelectNextChannel(GetChannelSwitchMode(action.GetID()));
231       return true;
232     }
233 
234     case ACTION_MOVE_DOWN:
235     case ACTION_CHANNEL_DOWN:
236     {
237       if (!bIsPlayingPVR)
238         return false;
239 
240       CServiceBroker::GetPVRManager().GUIActions()->GetChannelNavigator().SelectPreviousChannel(GetChannelSwitchMode(action.GetID()));
241       return true;
242     }
243 
244     case ACTION_CHANNEL_SWITCH:
245     {
246       if (!bIsPlayingPVR)
247         return false;
248 
249       int iChannelNumber = static_cast<int>(action.GetAmount(0));
250       int iSubChannelNumber = static_cast<int>(action.GetAmount(1));
251 
252       const std::shared_ptr<CPVRChannel> currentChannel = CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel();
253       const std::shared_ptr<CPVRChannelGroup> selectedGroup = CServiceBroker::GetPVRManager().ChannelGroups()->Get(currentChannel->IsRadio())->GetSelectedGroup();
254       const std::shared_ptr<CPVRChannel> channel = selectedGroup->GetByChannelNumber(CPVRChannelNumber(iChannelNumber, iSubChannelNumber));
255 
256       if (!channel)
257         return false;
258 
259       CServiceBroker::GetPVRManager().GUIActions()->SwitchToChannel(std::make_shared<CFileItem>(channel), false);
260       return true;
261     }
262 
263     case ACTION_RECORD:
264     {
265       CServiceBroker::GetPVRManager().GUIActions()->ToggleRecordingOnPlayingChannel();
266       return true;
267     }
268 
269     case ACTION_PVR_ANNOUNCE_REMINDERS:
270     {
271       CServiceBroker::GetPVRManager().GUIActions()->AnnounceReminders();
272       return true;
273     }
274   }
275   return false;
276 }
277 
OnSettingChanged(const std::shared_ptr<const CSetting> & setting)278 void CPVRGUIActionListener::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
279 {
280   if (setting == nullptr)
281     return;
282 
283   const std::string& settingId = setting->GetId();
284   if (settingId == CSettings::SETTING_PVRPARENTAL_ENABLED)
285   {
286     if (std::static_pointer_cast<const CSettingBool>(setting)->GetValue() && CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_PVRPARENTAL_PIN).empty())
287     {
288       std::string newPassword = "";
289       // password set... save it
290       if (CGUIDialogNumeric::ShowAndVerifyNewPassword(newPassword))
291         CServiceBroker::GetSettingsComponent()->GetSettings()->SetString(CSettings::SETTING_PVRPARENTAL_PIN, newPassword);
292       // password not set... disable parental
293       else
294         std::static_pointer_cast<CSettingBool>(std::const_pointer_cast<CSetting>(setting))->SetValue(false);
295     }
296   }
297   else if (settingId == CSettings::SETTING_EPG_PAST_DAYSTODISPLAY)
298   {
299     CServiceBroker::GetPVRManager().Clients()->SetEPGMaxPastDays(
300         std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
301   }
302   else if (settingId == CSettings::SETTING_EPG_FUTURE_DAYSTODISPLAY)
303   {
304     CServiceBroker::GetPVRManager().Clients()->SetEPGMaxFutureDays(
305         std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
306   }
307 }
308 
OnSettingAction(const std::shared_ptr<const CSetting> & setting)309 void CPVRGUIActionListener::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
310 {
311   if (setting == nullptr)
312     return;
313 
314   const std::string& settingId = setting->GetId();
315   if (settingId == CSettings::SETTING_PVRMANAGER_RESETDB)
316   {
317     CServiceBroker::GetPVRManager().GUIActions()->ResetPVRDatabase(false);
318   }
319   else if (settingId == CSettings::SETTING_EPG_RESETEPG)
320   {
321     CServiceBroker::GetPVRManager().GUIActions()->ResetPVRDatabase(true);
322   }
323   else if (settingId == CSettings::SETTING_PVRMANAGER_CLIENTPRIORITIES)
324   {
325     if (CServiceBroker::GetPVRManager().IsStarted())
326     {
327       CGUIDialog* dialog = CServiceBroker::GetGUI()->GetWindowManager().GetDialog(WINDOW_DIALOG_PVR_CLIENT_PRIORITIES);
328       if (dialog)
329       {
330         dialog->Open();
331         CServiceBroker::GetPVRManager().ChannelGroups()->Update();
332       }
333     }
334   }
335   else if (settingId == CSettings::SETTING_PVRMANAGER_CHANNELMANAGER)
336   {
337     if (CServiceBroker::GetPVRManager().IsStarted())
338     {
339       CGUIDialog* dialog = CServiceBroker::GetGUI()->GetWindowManager().GetDialog(WINDOW_DIALOG_PVR_CHANNEL_MANAGER);
340       if (dialog)
341         dialog->Open();
342     }
343   }
344   else if (settingId == CSettings::SETTING_PVRMANAGER_GROUPMANAGER)
345   {
346     if (CServiceBroker::GetPVRManager().IsStarted())
347     {
348       CGUIDialog* dialog = CServiceBroker::GetGUI()->GetWindowManager().GetDialog(WINDOW_DIALOG_PVR_GROUP_MANAGER);
349       if (dialog)
350         dialog->Open();
351     }
352   }
353   else if (settingId == CSettings::SETTING_PVRMANAGER_CHANNELSCAN)
354   {
355     CServiceBroker::GetPVRManager().GUIActions()->StartChannelScan();
356   }
357   else if (settingId == CSettings::SETTING_PVRMENU_SEARCHICONS)
358   {
359     CServiceBroker::GetPVRManager().TriggerSearchMissingChannelIcons();
360   }
361   else if (settingId == CSettings::SETTING_PVRCLIENT_MENUHOOK)
362   {
363     CServiceBroker::GetPVRManager().GUIActions()->ProcessSettingsMenuHooks();
364   }
365 }
366 
367 } // namespace PVR
368