diff options
28 files changed, 412 insertions, 118 deletions
diff --git a/Kodi.xcodeproj/project.pbxproj b/Kodi.xcodeproj/project.pbxproj index fee595be33..b9356802a6 100644 --- a/Kodi.xcodeproj/project.pbxproj +++ b/Kodi.xcodeproj/project.pbxproj @@ -2577,6 +2577,9 @@ 2A4AAD251CEBC2060057AD44 /* DVDDemuxVobsub.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDDemuxVobsub.h; sourceTree = "<group>"; }; 2A4AAD261CEBC2130057AD44 /* DVDFactoryDemuxer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDFactoryDemuxer.h; sourceTree = "<group>"; }; 2A4AAD281CEBC64A0057AD44 /* GUIInfoLabels.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIInfoLabels.h; sourceTree = "<group>"; }; + 2A5531F81D4A0F9C00BDCE99 /* AddonEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddonEvents.h; sourceTree = "<group>"; }; + 2A5531F91D4A0FEB00BDCE99 /* EventStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventStream.h; sourceTree = "<group>"; }; + 2A5531FA1D4A0FEB00BDCE99 /* EventStreamDetail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventStreamDetail.h; sourceTree = "<group>"; }; 2A7B2BDB1BD6F16600044BCD /* PVRSettings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PVRSettings.cpp; sourceTree = "<group>"; }; 2A7B2BDE1BD6F18B00044BCD /* PVRSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PVRSettings.h; sourceTree = "<group>"; }; 2A7E24C71CEBBC3B003096EB /* libKODI_peripheral.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = libKODI_peripheral.h; path = "kodi-addon-dev-kit/include/kodi/libKODI_peripheral.h"; sourceTree = "<group>"; }; @@ -5117,6 +5120,7 @@ 18B7C3821294203F009E7A26 /* AddonDatabase.cpp */, 18B7C3831294203F009E7A26 /* AddonDatabase.h */, 18B49FF31152BFA5001AF8A6 /* AddonDll.h */, + 2A5531F81D4A0F9C00BDCE99 /* AddonEvents.h */, 7C4705AC12EF584C00369E51 /* AddonInstaller.cpp */, 7C4705AD12EF584C00369E51 /* AddonInstaller.h */, 18B49FF41152BFA5001AF8A6 /* AddonManager.cpp */, @@ -8699,6 +8703,8 @@ 436B38F3106628850049AB3B /* EndianSwap.h */, DF529BAC1741697B00523FB4 /* Environment.cpp */, DF529BAD1741697B00523FB4 /* Environment.h */, + 2A5531F91D4A0FEB00BDCE99 /* EventStream.h */, + 2A5531FA1D4A0FEB00BDCE99 /* EventStreamDetail.h */, E36C29E90DA72486001F0C9D /* Fanart.cpp */, 6E97BDC30DA2B620003A2A89 /* Fanart.h */, F5F244641110DC6B009126C6 /* FileOperationJob.cpp */, diff --git a/project/VS2010Express/XBMC.vcxproj b/project/VS2010Express/XBMC.vcxproj index 8667a55212..3efccee4c2 100644 --- a/project/VS2010Express/XBMC.vcxproj +++ b/project/VS2010Express/XBMC.vcxproj @@ -2331,6 +2331,8 @@ copy "..\Win32BuildSetup\dependencies\python27.dll" "$(TargetDir)"</Command> <ClInclude Include="..\..\xbmc\utils\Crc32.h" /> <ClInclude Include="..\..\xbmc\utils\DatabaseUtils.h" /> <ClInclude Include="..\..\xbmc\utils\EndianSwap.h" /> + <ClInclude Include="..\..\xbmc\utils\EventStream.h" /> + <ClInclude Include="..\..\xbmc\utils\EventStreamDetail.h" /> <ClInclude Include="..\..\xbmc\utils\Fanart.h" /> <ClInclude Include="..\..\xbmc\utils\FileOperationJob.h" /> <ClInclude Include="..\..\xbmc\utils\FileUtils.h" /> @@ -2564,6 +2566,7 @@ copy "..\Win32BuildSetup\dependencies\python27.dll" "$(TargetDir)"</Command> <ClInclude Include="..\..\xbmc\FileSystem\VideoDatabaseDirectory\QueryParams.h" /> <ClInclude Include="..\..\xbmc\addons\Addon.h" /> <ClInclude Include="..\..\xbmc\addons\AddonDll.h" /> + <ClInclude Include="..\..\xbmc\addons\AddonEvents.h" /> <ClInclude Include="..\..\xbmc\addons\AddonManager.h" /> <ClInclude Include="..\..\xbmc\addons\AddonStatusHandler.h" /> <ClInclude Include="..\..\xbmc\addons\AudioEncoder.h" /> diff --git a/project/VS2010Express/XBMC.vcxproj.filters b/project/VS2010Express/XBMC.vcxproj.filters index 595e123080..a8ecdd48be 100644 --- a/project/VS2010Express/XBMC.vcxproj.filters +++ b/project/VS2010Express/XBMC.vcxproj.filters @@ -3868,6 +3868,9 @@ <ClInclude Include="..\..\xbmc\addons\AddonDll.h"> <Filter>addons</Filter> </ClInclude> + <ClInclude Include="..\..\xbmc\addons\AddonEvents.h"> + <Filter>addons</Filter> + </ClInclude> <ClInclude Include="..\..\xbmc\addons\AddonManager.h"> <Filter>addons</Filter> </ClInclude> @@ -4545,6 +4548,12 @@ <ClInclude Include="..\..\xbmc\utils\EndianSwap.h"> <Filter>utils</Filter> </ClInclude> + <ClInclude Include="..\..\xbmc\utils\EventStream.h"> + <Filter>utils</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\utils\EventStreamDetail.h"> + <Filter>utils</Filter> + </ClInclude> <ClInclude Include="..\..\xbmc\utils\Fanart.h"> <Filter>utils</Filter> </ClInclude> diff --git a/xbmc/addons/AddonEvents.h b/xbmc/addons/AddonEvents.h new file mode 100644 index 0000000000..04b8693b07 --- /dev/null +++ b/xbmc/addons/AddonEvents.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include <string> + +namespace ADDON +{ + struct AddonEvent + { + virtual ~AddonEvent() {}; + }; + + namespace AddonEvents + { + struct Enabled : AddonEvent + { + std::string id; + Enabled(std::string id) : id(std::move(id)) {} + }; + + struct Disabled : AddonEvent + { + std::string id; + Disabled(std::string id) : id(std::move(id)) {} + }; + + struct MetadataChanged : AddonEvent + { + std::string id; + MetadataChanged(std::string id) : id(std::move(id)) {} + }; + + struct InstalledChanged : AddonEvent {}; + }; +}; diff --git a/xbmc/addons/AddonInstaller.cpp b/xbmc/addons/AddonInstaller.cpp index 9ecbbdb2c7..5fb65055a0 100644 --- a/xbmc/addons/AddonInstaller.cpp +++ b/xbmc/addons/AddonInstaller.cpp @@ -601,10 +601,7 @@ bool CAddonInstallJob::DoWork() if (!Install(installFrom, m_repo)) return false; - CAddonMgr::GetInstance().UnregisterAddon(m_addon->ID()); - CAddonMgr::GetInstance().FindAddons(); - - if (!CAddonMgr::GetInstance().GetAddon(m_addon->ID(), m_addon, ADDON_UNKNOWN, false)) + if (!CAddonMgr::GetInstance().ReloadAddon(m_addon)) { CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to reload addon", m_addon->ID().c_str()); return false; @@ -615,9 +612,6 @@ bool CAddonInstallJob::DoWork() ADDON::OnPostInstall(m_addon, m_update, IsModal()); - //Enable it if it was previously disabled - CAddonMgr::GetInstance().EnableAddon(m_addon->ID()); - { CAddonDatabase database; database.Open(); @@ -626,9 +620,6 @@ bool CAddonInstallJob::DoWork() database.SetLastUpdated(m_addon->ID(), CDateTime::GetCurrentDateTime()); } - // notify any observers that add-ons have changed - CAddonMgr::GetInstance().NotifyObservers(ObservableMessageAddons); - CEventLog::GetInstance().Add( EventPtr(new CAddonManagementEvent(m_addon, m_update ? 24065 : 24064)), !IsModal() && CSettings::GetInstance().GetBool(CSettings::SETTING_ADDONS_NOTIFICATIONS), false); @@ -854,7 +845,11 @@ bool CAddonUnInstallJob::DoWork() //Unregister addon with the manager to ensure nothing tries //to interact with it while we are uninstalling. - CAddonMgr::GetInstance().UnregisterAddon(m_addon->ID()); + if (!CAddonMgr::GetInstance().UnloadAddon(m_addon)) + { + CLog::Log(LOGERROR, "CAddonUnInstallJob[%s]: failed to unload addon.", m_addon->ID().c_str()); + return false; + } if (!DeleteAddon(m_addon->Path())) { diff --git a/xbmc/addons/AddonManager.cpp b/xbmc/addons/AddonManager.cpp index 4bad98d466..ee9ad79c2e 100644 --- a/xbmc/addons/AddonManager.cpp +++ b/xbmc/addons/AddonManager.cpp @@ -357,7 +357,7 @@ bool CAddonMgr::Init() if (!m_database.Open()) CLog::Log(LOGFATAL, "ADDONS: Failed to open database"); - FindAddonsAndNotify(); + FindAddons(); //Ensure required add-ons are installed and enabled for (const auto& id : m_systemAddons) @@ -723,32 +723,36 @@ bool CAddonMgr::FindAddons() m_database.GetBlacklisted(tmp); m_updateBlacklist = std::move(tmp); - SetChanged(); + m_events.Publish(AddonEvents::InstalledChanged()); } return result; } -bool CAddonMgr::FindAddonsAndNotify() -{ - if (!FindAddons()) - return false; - - NotifyObservers(ObservableMessageAddons); - - return true; -} - -void CAddonMgr::UnregisterAddon(const std::string& ID) +bool CAddonMgr::UnloadAddon(const AddonPtr& addon) { CSingleLock lock(m_critSection); if (m_cpluff && m_cp_context) { - m_cpluff->uninstall_plugin(m_cp_context, ID.c_str()); - SetChanged(); - lock.Leave(); - NotifyObservers(ObservableMessageAddons); + if (m_cpluff->uninstall_plugin(m_cp_context, addon->ID().c_str()) == CP_OK) + { + m_events.Publish(AddonEvents::InstalledChanged()); + return true; + } } + return false; +} + +bool CAddonMgr::ReloadAddon(AddonPtr& addon) +{ + CSingleLock lock(m_critSection); + if (!addon ||!m_cpluff || !m_cp_context) + return false; + + m_cpluff->uninstall_plugin(m_cp_context, addon->ID().c_str()); + return FindAddons() + && GetAddon(addon->ID(), addon, ADDON_UNKNOWN, false) + && EnableAddon(addon->ID()); } void CAddonMgr::OnPostUnInstall(const std::string& id) @@ -780,6 +784,18 @@ bool CAddonMgr::IsBlacklisted(const std::string& id) const return m_updateBlacklist.find(id) != m_updateBlacklist.end(); } +void CAddonMgr::UpdateLastUsed(const std::string& id) +{ + auto time = CDateTime::GetCurrentDateTime(); + CJobManager::GetInstance().Submit([this, id, time](){ + { + CSingleLock lock(m_critSection); + m_database.SetLastUsed(id, time); + } + m_events.Publish(AddonEvents::MetadataChanged(id)); + }); +} + static void ResolveDependencies(const std::string& addonId, std::vector<std::string>& needed, std::vector<std::string>& missing) { if (std::find(needed.begin(), needed.end(), addonId) != needed.end()) @@ -809,12 +825,14 @@ bool CAddonMgr::DisableAddon(const std::string& id) if (!m_disabled.insert(id).second) return false; + //success + ADDON::OnDisabled(id); + AddonPtr addon; if (GetAddon(id, addon, ADDON_UNKNOWN, false) && addon != NULL) CEventLog::GetInstance().Add(EventPtr(new CAddonManagementEvent(addon, 24141))); - //success - ADDON::OnDisabled(id); + m_events.Publish(AddonEvents::Disabled(id)); return true; } @@ -833,11 +851,14 @@ bool CAddonMgr::EnableSingle(const std::string& id) CEventLog::GetInstance().Add(EventPtr(new CAddonManagementEvent(addon, 24064))); CLog::Log(LOGDEBUG, "CAddonMgr: enabled %s", addon->ID().c_str()); + m_events.Publish(AddonEvents::Enabled(id)); return true; } bool CAddonMgr::EnableAddon(const std::string& id) { + if (id.empty() || !IsAddonInstalled(id)) + return false; std::vector<std::string> needed; std::vector<std::string> missing; ResolveDependencies(id, needed, missing); @@ -846,6 +867,7 @@ bool CAddonMgr::EnableAddon(const std::string& id) "correctly", dep.c_str(), id.c_str()); for (auto it = needed.rbegin(); it != needed.rend(); ++it) EnableSingle(*it); + return true; } diff --git a/xbmc/addons/AddonManager.h b/xbmc/addons/AddonManager.h index 969de5e9f7..7a2835cc56 100644 --- a/xbmc/addons/AddonManager.h +++ b/xbmc/addons/AddonManager.h @@ -19,14 +19,16 @@ * */ #include "Addon.h" +#include "AddonDatabase.h" +#include "AddonEvents.h" +#include "Repository.h" #include "threads/CriticalSection.h" -#include "utils/Observer.h" +#include "utils/EventStream.h" #include <string> #include <vector> #include <map> #include <deque> -#include "AddonDatabase.h" -#include "Repository.h" + class DllLibCPluff; extern "C" @@ -62,7 +64,7 @@ namespace ADDON * otherwise. Services the generic callbacks available * to all addon variants. */ - class CAddonMgr : public Observable + class CAddonMgr { public: static CAddonMgr &GetInstance(); @@ -75,6 +77,7 @@ namespace ADDON CAddonMgr const& operator=(CAddonMgr const&); virtual ~CAddonMgr(); + CEventStream<AddonEvent>& Events() { return m_events; } IAddonMgrCallback* GetCallbackForType(TYPE type); bool RegisterAddonMgrCallback(TYPE type, IAddonMgrCallback* cb); @@ -135,12 +138,11 @@ namespace ADDON */ bool FindAddons(); - /*! \brief Checks for new / updated add-ons and notifies all observers - \return True if everything went ok, false otherwise - */ - bool FindAddonsAndNotify(); + /*! Unload addon from the system. Returns true if it was unloaded, otherwise false. */ + bool UnloadAddon(const AddonPtr& addon); - void UnregisterAddon(const std::string& ID); + /*! Returns true if the addon was successfully loaded and enabled; otherwise false. */ + bool ReloadAddon(AddonPtr& addon); /*! Hook for clearing internal state after uninstall. */ void OnPostUnInstall(const std::string& id); @@ -182,6 +184,8 @@ namespace ADDON bool RemoveFromUpdateBlacklist(const std::string& id); bool IsBlacklisted(const std::string& id) const; + void UpdateLastUsed(const std::string& id); + /* libcpluff */ std::string GetExtValue(cp_cfg_element_t *base, const char *path) const; @@ -266,6 +270,7 @@ namespace ADDON static std::map<TYPE, IAddonMgrCallback*> m_managers; CCriticalSection m_critSection; CAddonDatabase m_database; + CEventSource<AddonEvent> m_events; std::set<std::string> m_systemAddons; std::set<std::string> m_optionalAddons; }; diff --git a/xbmc/addons/BinaryAddonCache.cpp b/xbmc/addons/BinaryAddonCache.cpp index c2caa5e88a..0c3a2b708b 100644 --- a/xbmc/addons/BinaryAddonCache.cpp +++ b/xbmc/addons/BinaryAddonCache.cpp @@ -33,13 +33,13 @@ CBinaryAddonCache::~CBinaryAddonCache() void CBinaryAddonCache::Init() { m_addonsToCache = {ADDON_AUDIODECODER, ADDON_INPUTSTREAM}; - CAddonMgr::GetInstance().RegisterObserver(this); + CAddonMgr::GetInstance().Events().Subscribe(this, &CBinaryAddonCache::OnEvent); Update(); } void CBinaryAddonCache::Deinit() { - CAddonMgr::GetInstance().UnregisterObserver(this); + CAddonMgr::GetInstance().Events().Unsubscribe(this); } void CBinaryAddonCache::GetAddons(VECADDONS& addons, const TYPE& type) @@ -57,9 +57,10 @@ void CBinaryAddonCache::GetAddons(VECADDONS& addons, const TYPE& type) } } -void CBinaryAddonCache::Notify(const Observable &obs, const ObservableMessage msg) +void CBinaryAddonCache::OnEvent(const AddonEvent& event) { - Update(); + if (typeid(event) == typeid(AddonEvents::InstalledChanged)) + Update(); } void CBinaryAddonCache::Update() diff --git a/xbmc/addons/BinaryAddonCache.h b/xbmc/addons/BinaryAddonCache.h index 164288522f..970069725c 100644 --- a/xbmc/addons/BinaryAddonCache.h +++ b/xbmc/addons/BinaryAddonCache.h @@ -22,23 +22,24 @@ #include "utils/Observer.h" #include "Addon.h" +#include "AddonEvents.h" #include "threads/CriticalSection.h" #include <map> #include <vector> namespace ADDON { -class CBinaryAddonCache : public Observer +class CBinaryAddonCache { public: virtual ~CBinaryAddonCache(); void Init(); void Deinit(); void GetAddons(VECADDONS& addons, const TYPE& type); - virtual void Notify(const Observable &obs, const ObservableMessage msg) override; protected: void Update(); + void OnEvent(const AddonEvent& event); CCriticalSection m_critSection; std::multimap<TYPE, VECADDONS> m_addons; diff --git a/xbmc/addons/CMakeLists.txt b/xbmc/addons/CMakeLists.txt index af1ee00f84..cb4da8cbd7 100644 --- a/xbmc/addons/CMakeLists.txt +++ b/xbmc/addons/CMakeLists.txt @@ -32,6 +32,7 @@ set(SOURCES Addon.cpp set(HEADERS Addon.h AddonBuilder.h + AddonEvents.h BinaryAddonCache.h AddonDatabase.h AddonDll.h diff --git a/xbmc/addons/GUIDialogAddonInfo.cpp b/xbmc/addons/GUIDialogAddonInfo.cpp index e6c23b080e..81f1104a06 100644 --- a/xbmc/addons/GUIDialogAddonInfo.cpp +++ b/xbmc/addons/GUIDialogAddonInfo.cpp @@ -457,7 +457,6 @@ void CGUIDialogAddonInfo::OnEnableDisable() CAddonMgr::GetInstance().EnableAddon(m_localAddon->ID()); UpdateControls(); - g_windowManager.SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE); } void CGUIDialogAddonInfo::OnSettings() diff --git a/xbmc/addons/GUIWindowAddonBrowser.cpp b/xbmc/addons/GUIWindowAddonBrowser.cpp index a79b404cb6..b2909d766a 100644 --- a/xbmc/addons/GUIWindowAddonBrowser.cpp +++ b/xbmc/addons/GUIWindowAddonBrowser.cpp @@ -71,12 +71,18 @@ bool CGUIWindowAddonBrowser::OnMessage(CGUIMessage& message) { case GUI_MSG_WINDOW_DEINIT: { + CRepositoryUpdater::GetInstance().Events().Unsubscribe(this); + CAddonMgr::GetInstance().Events().Unsubscribe(this); + if (m_thumbLoader.IsLoading()) m_thumbLoader.StopThread(); } break; case GUI_MSG_WINDOW_INIT: { + CRepositoryUpdater::GetInstance().Events().Subscribe(this, &CGUIWindowAddonBrowser::OnEvent); + CAddonMgr::GetInstance().Events().Subscribe(this, &CGUIWindowAddonBrowser::OnEvent); + SetProperties(); } break; @@ -163,6 +169,17 @@ class UpdateAddons : public IRunnable } }; +void CGUIWindowAddonBrowser::OnEvent(const ADDON::CRepositoryUpdater::RepositoryUpdated& event) +{ + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE); + g_windowManager.SendThreadMessage(msg); +} + +void CGUIWindowAddonBrowser::OnEvent(const ADDON::AddonEvent& event) +{ + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE); + g_windowManager.SendThreadMessage(msg); +} bool CGUIWindowAddonBrowser::OnClick(int iItem, const std::string &player) { diff --git a/xbmc/addons/GUIWindowAddonBrowser.h b/xbmc/addons/GUIWindowAddonBrowser.h index 0831fd9dd1..b1c7822cb3 100644 --- a/xbmc/addons/GUIWindowAddonBrowser.h +++ b/xbmc/addons/GUIWindowAddonBrowser.h @@ -22,10 +22,12 @@ #include <string> #include <vector> - -#include "addons/Addon.h" -#include "windows/GUIMediaWindow.h" +#include "Addon.h" +#include "AddonEvents.h" +#include "RepositoryUpdater.h" #include "ThumbLoader.h" +#include "windows/GUIMediaWindow.h" + class CFileItem; class CFileItemList; @@ -75,6 +77,8 @@ protected: private: void SetProperties(); void UpdateStatus(const CFileItemPtr& item); + void OnEvent(const ADDON::CRepositoryUpdater::RepositoryUpdated& event); + void OnEvent(const ADDON::AddonEvent& event); CProgramThumbLoader m_thumbLoader; }; diff --git a/xbmc/addons/RepositoryUpdater.cpp b/xbmc/addons/RepositoryUpdater.cpp index 719f8840fc..41bc15c496 100644 --- a/xbmc/addons/RepositoryUpdater.cpp +++ b/xbmc/addons/RepositoryUpdater.cpp @@ -20,7 +20,6 @@ #include "RepositoryUpdater.h" #include "Application.h" -#include "GUIUserMessages.h" #include "addons/AddonInstaller.h" #include "addons/AddonManager.h" #include "addons/AddonSystemSettings.h" @@ -89,8 +88,7 @@ void CRepositoryUpdater::OnJobComplete(unsigned int jobID, bool success, CJob* j ScheduleUpdate(); - CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE); - g_windowManager.SendThreadMessage(msg); + m_events.Publish(RepositoryUpdated{}); } } diff --git a/xbmc/addons/RepositoryUpdater.h b/xbmc/addons/RepositoryUpdater.h index 80afc9133f..9075df185a 100644 --- a/xbmc/addons/RepositoryUpdater.h +++ b/xbmc/addons/RepositoryUpdater.h @@ -23,6 +23,7 @@ #include "dialogs/GUIDialogExtendedProgressBar.h" #include "threads/CriticalSection.h" #include "threads/Timer.h" +#include "utils/EventStream.h" #include "XBDateTime.h" #include <vector> @@ -65,8 +66,13 @@ public: */ CDateTime LastUpdated() const; + virtual void OnSettingChanged(const CSetting* setting) override; + struct RepositoryUpdated { }; + + CEventStream<RepositoryUpdated>& Events() { return m_events; } + private: CRepositoryUpdater(); CRepositoryUpdater(const CRepositoryUpdater&) = delete; @@ -82,5 +88,7 @@ private: CTimer m_timer; CEvent m_doneEvent; std::vector<CRepositoryUpdateJob*> m_jobs; + + CEventSource<RepositoryUpdated> m_events; }; } diff --git a/xbmc/games/controllers/windows/GUIControllerList.cpp b/xbmc/games/controllers/windows/GUIControllerList.cpp index 3a088a6bfa..1fabf9b354 100644 --- a/xbmc/games/controllers/windows/GUIControllerList.cpp +++ b/xbmc/games/controllers/windows/GUIControllerList.cpp @@ -61,17 +61,16 @@ bool CGUIControllerList::Initialize(void) if (m_controllerButton) m_controllerButton->SetVisible(false); + CAddonMgr::GetInstance().Events().Subscribe(this, &CGUIControllerList::OnEvent); Refresh(); - CAddonMgr::GetInstance().RegisterObserver(this); - return m_controllerList != nullptr && m_controllerButton != nullptr; } void CGUIControllerList::Deinitialize(void) { - CAddonMgr::GetInstance().UnregisterObserver(this); + CAddonMgr::GetInstance().Events().Unsubscribe(this); CleanupButtons(); @@ -143,12 +142,11 @@ void CGUIControllerList::ResetController(void) } } -void CGUIControllerList::Notify(const Observable& obs, const ObservableMessage msg) +void CGUIControllerList::OnEvent(const ADDON::AddonEvent& event) { - using namespace KODI::MESSAGING; - - if (msg == ObservableMessageAddons) + if (typeid(event) == typeid(ADDON::AddonEvents::InstalledChanged)) { + using namespace KODI::MESSAGING; CGUIMessage msg(GUI_MSG_REFRESH_LIST, m_guiWindow->GetID(), CONTROL_CONTROLLER_LIST); CApplicationMessenger::GetInstance().SendGUIMessage(msg); } diff --git a/xbmc/games/controllers/windows/GUIControllerList.h b/xbmc/games/controllers/windows/GUIControllerList.h index 44197d50bf..036cea9576 100644 --- a/xbmc/games/controllers/windows/GUIControllerList.h +++ b/xbmc/games/controllers/windows/GUIControllerList.h @@ -20,9 +20,9 @@ #pragma once #include "IConfigurationWindow.h" +#include "addons/AddonEvents.h" #include "addons/Addon.h" #include "games/controllers/ControllerTypes.h" -#include "utils/Observer.h" #include <set> #include <string> @@ -35,8 +35,7 @@ namespace GAME { class CGUIControllerWindow; - class CGUIControllerList : public IControllerList, - public Observer + class CGUIControllerList : public IControllerList { public: CGUIControllerList(CGUIWindow* window, IFeatureList* featureList); @@ -50,9 +49,6 @@ namespace GAME virtual void OnSelect(unsigned int controllerIndex) override; virtual void ResetController(void) override; - // implementation of Observer - virtual void Notify(const Observable& obs, const ObservableMessage msg) override; - private: bool RefreshControllers(void); @@ -63,6 +59,7 @@ namespace GAME void UnregisterController(const std::string& controllerId); void CleanupButtons(void); + void OnEvent(const ADDON::AddonEvent& event); // GUI stuff CGUIWindow* const m_guiWindow; diff --git a/xbmc/games/controllers/windows/GUIControllerWindow.cpp b/xbmc/games/controllers/windows/GUIControllerWindow.cpp index 65bb09f3c8..b8f52a6e48 100644 --- a/xbmc/games/controllers/windows/GUIControllerWindow.cpp +++ b/xbmc/games/controllers/windows/GUIControllerWindow.cpp @@ -151,10 +151,9 @@ bool CGUIControllerWindow::OnMessage(CGUIMessage& message) return CGUIDialog::OnMessage(message); } -void CGUIControllerWindow::Notify(const Observable &obs, const ObservableMessage msg) +void CGUIControllerWindow::OnEvent(const ADDON::CRepositoryUpdater::RepositoryUpdated& event) { - if (msg == ObservableMessageAddons) - UpdateButtons(); + UpdateButtons(); } void CGUIControllerWindow::OnInitWindow(void) @@ -205,14 +204,15 @@ void CGUIControllerWindow::OnInitWindow(void) Close(); } - UpdateButtons(); + // FIXME: not thread safe +// ADDON::CRepositoryUpdater::GetInstance().Events().Subscribe(this, &CGUIControllerWindow::OnEvent); - ADDON::CAddonMgr::GetInstance().RegisterObserver(this); + UpdateButtons(); } void CGUIControllerWindow::OnDeinitWindow(int nextWindowID) { - ADDON::CAddonMgr::GetInstance().UnregisterObserver(this); + ADDON::CRepositoryUpdater::GetInstance().Events().Unsubscribe(this); if (m_controllerList) { diff --git a/xbmc/games/controllers/windows/GUIControllerWindow.h b/xbmc/games/controllers/windows/GUIControllerWindow.h index ca8c442753..835068bac3 100644 --- a/xbmc/games/controllers/windows/GUIControllerWindow.h +++ b/xbmc/games/controllers/windows/GUIControllerWindow.h @@ -19,6 +19,7 @@ */ #pragma once +#include "addons/RepositoryUpdater.h" #include "guilib/GUIDialog.h" #include "utils/Observer.h" @@ -27,8 +28,7 @@ namespace GAME class IControllerList; class IFeatureList; - class CGUIControllerWindow : public CGUIDialog, - public Observer + class CGUIControllerWindow : public CGUIDialog { public: CGUIControllerWindow(void); @@ -37,9 +37,6 @@ namespace GAME // implementation of CGUIControl via CGUIDialog virtual bool OnMessage(CGUIMessage& message) override; - // implementation of Observer - void Notify(const Observable &obs, const ObservableMessage msg) override; - protected: // implementation of CGUIWindow via CGUIDialog virtual void OnInitWindow(void) override; @@ -50,7 +47,7 @@ namespace GAME void OnControllerSelected(unsigned int controllerIndex); void OnFeatureFocused(unsigned int featureIndex); void OnFeatureSelected(unsigned int featureIndex); - + void OnEvent(const ADDON::CRepositoryUpdater::RepositoryUpdated& event); void UpdateButtons(void); // Action for the available button diff --git a/xbmc/interfaces/builtins/AddonBuiltins.cpp b/xbmc/interfaces/builtins/AddonBuiltins.cpp index 446fc97886..18b112dd2d 100644 --- a/xbmc/interfaces/builtins/AddonBuiltins.cpp +++ b/xbmc/interfaces/builtins/AddonBuiltins.cpp @@ -302,7 +302,7 @@ static int UpdateRepos(const std::vector<std::string>& params) */ static int UpdateLocals(const std::vector<std::string>& params) { - CAddonMgr::GetInstance().FindAddonsAndNotify(); + CAddonMgr::GetInstance().FindAddons(); return 0; } diff --git a/xbmc/listproviders/DirectoryProvider.cpp b/xbmc/listproviders/DirectoryProvider.cpp index a13e65e120..d01d6fa07d 100644 --- a/xbmc/listproviders/DirectoryProvider.cpp +++ b/xbmc/listproviders/DirectoryProvider.cpp @@ -36,6 +36,7 @@ #include "settings/Settings.h" #include "threads/SingleLock.h" #include "utils/JobManager.h" +#include "utils/log.h" #include "utils/SortUtils.h" #include "utils/URIUtils.h" #include "utils/Variant.h" @@ -205,7 +206,13 @@ bool CDirectoryProvider::Update(bool forceRefresh) fireJob |= UpdateSort(); fireJob |= UpdateLimit(); if (fireJob) + { + { + CSingleLock lock(m_section); + CLog::Log(LOGDEBUG, "CDirectoryProvider[%s]: refreshing..", m_currentUrl.c_str()); + } FireJob(); + } for (std::vector<CGUIStaticItemPtr>::iterator i = m_items.begin(); i != m_items.end(); ++i) changed |= (*i)->UpdateVisibility(m_parentID); @@ -253,6 +260,19 @@ void CDirectoryProvider::Fetch(std::vector<CGUIListItemPtr> &items) const } } +void CDirectoryProvider::OnEvent(const ADDON::AddonEvent& event) +{ + CSingleLock lock(m_section); + if (URIUtils::IsProtocol(m_currentUrl, "addons")) + { + if (typeid(event) == typeid(ADDON::AddonEvents::Enabled) || + typeid(event) == typeid(ADDON::AddonEvents::Disabled) || + typeid(event) == typeid(ADDON::AddonEvents::InstalledChanged) || + typeid(event) == typeid(ADDON::AddonEvents::MetadataChanged)) + m_updateState = PENDING; + } +} + void CDirectoryProvider::Reset(bool immediately /* = false */) { // cancel any pending jobs @@ -271,7 +291,7 @@ void CDirectoryProvider::Reset(bool immediately /* = false */) m_currentSort.sortOrder = SortOrderAscending; m_currentLimit = 0; m_updateState = OK; - RegisterListProvider(false); + RegisterListProvider(); } } @@ -358,36 +378,41 @@ void CDirectoryProvider::FireJob() m_jobID = CJobManager::GetInstance().AddJob(new CDirectoryJob(m_currentUrl, m_currentSort, m_currentLimit, m_parentID), this); } -void CDirectoryProvider::RegisterListProvider(bool hasLibraryContent) +void CDirectoryProvider::RegisterListProvider() { - if (hasLibraryContent && !m_isAnnounced) + CSingleLock lock(m_section); + if (!m_isAnnounced) { m_isAnnounced = true; CAnnouncementManager::GetInstance().AddAnnouncer(this); + ADDON::CAddonMgr::GetInstance().Events().Subscribe(this, &CDirectoryProvider::OnEvent); } - else if (!hasLibraryContent && m_isAnnounced) + else if (m_isAnnounced) { m_isAnnounced = false; CAnnouncementManager::GetInstance().RemoveAnnouncer(this); + ADDON::CAddonMgr::GetInstance().Events().Unsubscribe(this); } } bool CDirectoryProvider::UpdateURL() { - std::string value(m_url.GetLabel(m_parentID, false)); - if (value == m_currentUrl) - return false; - - m_currentUrl = value; + { + CSingleLock lock(m_section); + std::string value(m_url.GetLabel(m_parentID, false)); + if (value == m_currentUrl) + return false; - // Register this provider only if we have library content - RegisterListProvider(URIUtils::IsLibraryContent(m_currentUrl)); + m_currentUrl = value; + } + RegisterListProvider(); return true; } bool CDirectoryProvider::UpdateLimit() { + CSingleLock lock(m_section); unsigned int value = m_limit.GetIntValue(m_parentID); if (value == m_currentLimit) return false; @@ -399,6 +424,7 @@ bool CDirectoryProvider::UpdateLimit() bool CDirectoryProvider::UpdateSort() { + CSingleLock lock(m_section); SortBy sortMethod(SortUtils::SortMethodFromString(m_sortMethod.GetLabel(m_parentID, false))); SortOrder sortOrder(SortUtils::SortOrderFromString(m_sortOrder.GetLabel(m_parentID, false))); if (sortOrder == SortOrderNone) diff --git a/xbmc/listproviders/DirectoryProvider.h b/xbmc/listproviders/DirectoryProvider.h index a15fdd2370..1b0a7e367c 100644 --- a/xbmc/listproviders/DirectoryProvider.h +++ b/xbmc/listproviders/DirectoryProvider.h @@ -22,7 +22,7 @@ #include <string> #include <vector> - +#include "addons/AddonEvents.h" #include "IListProvider.h" #include "guilib/GUIStaticItem.h" #include "utils/Job.h" @@ -85,8 +85,9 @@ private: CCriticalSection m_section; void FireJob(); - void RegisterListProvider(bool hasLibraryContent); + void RegisterListProvider(); bool UpdateURL(); bool UpdateLimit(); bool UpdateSort(); + void OnEvent(const ADDON::AddonEvent& event); }; diff --git a/xbmc/peripherals/bus/virtual/PeripheralBusAddon.cpp b/xbmc/peripherals/bus/virtual/PeripheralBusAddon.cpp index d6138e04fe..e563222c21 100644 --- a/xbmc/peripherals/bus/virtual/PeripheralBusAddon.cpp +++ b/xbmc/peripherals/bus/virtual/PeripheralBusAddon.cpp @@ -37,14 +37,14 @@ CPeripheralBusAddon::CPeripheralBusAddon(CPeripherals *manager) : CPeripheralBus("PeripBusAddon", manager, PERIPHERAL_BUS_ADDON) { CAddonMgr::GetInstance().RegisterAddonMgrCallback(ADDON_PERIPHERALDLL, this); - CAddonMgr::GetInstance().RegisterObserver(this); + CAddonMgr::GetInstance().Events().Subscribe(this, &CPeripheralBusAddon::OnEvent); UpdateAddons(); } CPeripheralBusAddon::~CPeripheralBusAddon() { - CAddonMgr::GetInstance().UnregisterObserver(this); + CAddonMgr::GetInstance().Events().Unsubscribe(this); CAddonMgr::GetInstance().UnregisterAddonMgrCallback(ADDON_PERIPHERALDLL); // stop everything before destroying any (loaded) addons @@ -293,9 +293,9 @@ bool CPeripheralBusAddon::RequestRemoval(ADDON::AddonPtr addon) return true; } -void CPeripheralBusAddon::Notify(const Observable &obs, const ObservableMessage msg) +void CPeripheralBusAddon::OnEvent(const ADDON::AddonEvent& event) { - if (msg == ObservableMessageAddons) + if (typeid(event) == typeid(AddonEvents::InstalledChanged)) UpdateAddons(); } diff --git a/xbmc/peripherals/bus/virtual/PeripheralBusAddon.h b/xbmc/peripherals/bus/virtual/PeripheralBusAddon.h index 2f09b5acb3..626938db4c 100644 --- a/xbmc/peripherals/bus/virtual/PeripheralBusAddon.h +++ b/xbmc/peripherals/bus/virtual/PeripheralBusAddon.h @@ -34,8 +34,7 @@ namespace PERIPHERALS { class CPeripheralBusAddon : public CPeripheralBus, - public ADDON::IAddonMgrCallback, - public Observer + public ADDON::IAddonMgrCallback { public: CPeripheralBusAddon(CPeripherals *manager); @@ -76,9 +75,6 @@ namespace PERIPHERALS bool RequestRestart(ADDON::AddonPtr addon, bool datachanged) override; bool RequestRemoval(ADDON::AddonPtr addon) override; - // implementation of Observer - void Notify(const Observable &obs, const ObservableMessage msg) override; - bool SplitLocation(const std::string& strLocation, PeripheralAddonPtr& addon, unsigned int& peripheralIndex) const; protected: @@ -89,6 +85,7 @@ namespace PERIPHERALS private: void UpdateAddons(void); + void OnEvent(const ADDON::AddonEvent& event); PeripheralAddonVector m_addons; PeripheralAddonVector m_failedAddons; diff --git a/xbmc/utils/CMakeLists.txt b/xbmc/utils/CMakeLists.txt index c495885fde..ea809a68fd 100644 --- a/xbmc/utils/CMakeLists.txt +++ b/xbmc/utils/CMakeLists.txt @@ -92,6 +92,8 @@ set(HEADERS ActorProtocol.h DatabaseUtils.h EndianSwap.h Environment.h + EventStream.h + EventStreamDetail.h Fanart.h FileOperationJob.h FileUtils.h diff --git a/xbmc/utils/EventStream.h b/xbmc/utils/EventStream.h new file mode 100644 index 0000000000..279a004942 --- /dev/null +++ b/xbmc/utils/EventStream.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2016 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include "EventStreamDetail.h" +#include "JobManager.h" +#include "threads/CriticalSection.h" +#include "threads/SingleLock.h" +#include <algorithm> +#include <memory> +#include <vector> + + +template<typename Event> +class CEventStream +{ +public: + + template<typename A> + void Subscribe(A* owner, void (A::*fn)(const Event&)) + { + auto subscription = std::make_shared<detail::CSubscription<Event, A>>(owner, fn); + CSingleLock lock(m_criticalSection); + m_subscriptions.emplace_back(std::move(subscription)); + } + + template<typename A> + void Unsubscribe(A* obj) + { + std::vector<std::shared_ptr<detail::ISubscription<Event>>> toCancel; + { + CSingleLock lock(m_criticalSection); + auto it = m_subscriptions.begin(); + while (it != m_subscriptions.end()) + { + if ((*it)->IsOwnedBy(obj)) + { + toCancel.push_back(*it); + it = m_subscriptions.erase(it); + } + else + { + ++it; + } + } + } + for (auto& subscription : toCancel) + subscription->Cancel(); + } + +protected: + std::vector<std::shared_ptr<detail::ISubscription<Event>>> m_subscriptions; + CCriticalSection m_criticalSection; +}; + + +template<typename Event> +class CEventSource : public CEventStream<Event> +{ +public: + template<typename A> + void Publish(A event) + { + CSingleLock lock(this->m_criticalSection); + auto& subscriptions = this->m_subscriptions; + auto task = [subscriptions, event](){ + for (auto& s: subscriptions) + s->HandleEvent(event); + }; + lock.Leave(); + CJobManager::GetInstance().Submit(std::move(task)); + } +}; diff --git a/xbmc/utils/EventStreamDetail.h b/xbmc/utils/EventStreamDetail.h new file mode 100644 index 0000000000..68d97c2e07 --- /dev/null +++ b/xbmc/utils/EventStreamDetail.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2016 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ +#pragma once + +#include "threads/CriticalSection.h" +#include "threads/SingleLock.h" + +namespace detail +{ + +template<typename Event> +class ISubscription +{ +public: + virtual void HandleEvent(const Event& event) = 0; + virtual void Cancel() = 0; + virtual bool IsOwnedBy(void* obj) = 0; + virtual ~ISubscription() {} +}; + +template<typename Event, typename Owner> +class CSubscription : public ISubscription<Event> +{ +public: + typedef void (Owner::*Fn)(const Event&); + CSubscription(Owner* owner, Fn fn); + void HandleEvent(const Event& event) override; + void Cancel() override; + bool IsOwnedBy(void *obj) override; + +private: + Owner* m_owner; + Fn m_eventHandler; + CCriticalSection m_criticalSection; +}; + +template<typename Event, typename Owner> +CSubscription<Event, Owner>::CSubscription(Owner* owner, Fn fn) + : m_owner(owner), m_eventHandler(fn) +{} + +template<typename Event, typename Owner> +bool CSubscription<Event, Owner>::IsOwnedBy(void* obj) +{ + CSingleLock lock(m_criticalSection); + return obj != nullptr && obj == m_owner; +} + +template<typename Event, typename Owner> +void CSubscription<Event, Owner>::Cancel() +{ + CSingleLock lock(m_criticalSection); + m_owner = nullptr; +} + +template<typename Event, typename Owner> +void CSubscription<Event, Owner>::HandleEvent(const Event& event) +{ + CSingleLock lock(m_criticalSection); + if (m_owner) + (m_owner->*m_eventHandler)(event); +} +} diff --git a/xbmc/windows/GUIMediaWindow.cpp b/xbmc/windows/GUIMediaWindow.cpp index 075d5317f5..ad9c6bbb2f 100644 --- a/xbmc/windows/GUIMediaWindow.cpp +++ b/xbmc/windows/GUIMediaWindow.cpp @@ -653,16 +653,7 @@ bool CGUIMediaWindow::GetDirectory(const std::string &strDirectory, CFileItemLis CLog::Log(LOGDEBUG," ParentPath = [%s]", CURL::GetRedacted(strParentPath).c_str()); if (pathToUrl.IsProtocol("plugin")) - { - //Record usage - auto& addonId = pathToUrl.GetHostName(); - auto time = CDateTime::GetCurrentDateTime(); - CJobManager::GetInstance().Submit([addonId, time](){ - CAddonDatabase db; - if (db.Open()) - db.SetLastUsed(addonId, time); - }); - } + CAddonMgr::GetInstance().UpdateLastUsed(pathToUrl.GetHostName()); // see if we can load a previously cached folder CFileItemList cachedItems(strDirectory); @@ -930,15 +921,8 @@ bool CGUIMediaWindow::OnClick(int iItem, const std::string &player) { if (!CScriptInvocationManager::GetInstance().Stop(addon->LibPath())) { - auto time = CDateTime::GetCurrentDateTime(); - + CAddonMgr::GetInstance().UpdateLastUsed(addon->ID()); CScriptInvocationManager::GetInstance().ExecuteAsync(addon->LibPath(), addon); - - CJobManager::GetInstance().Submit([addon, time](){ - CAddonDatabase db; - if (db.Open()) - db.SetLastUsed(addon->ID(), time); - }); } return true; } |