diff options
-rw-r--r-- | addons/resource.language.en_gb/resources/strings.po | 8 | ||||
-rw-r--r-- | xbmc/addons/AddonManager.cpp | 40 | ||||
-rw-r--r-- | xbmc/addons/AddonManager.h | 26 | ||||
-rw-r--r-- | xbmc/addons/AddonRepos.cpp | 61 | ||||
-rw-r--r-- | xbmc/addons/AddonRepos.h | 28 | ||||
-rw-r--r-- | xbmc/addons/gui/GUIDialogAddonInfo.cpp | 31 | ||||
-rw-r--r-- | xbmc/addons/gui/GUIDialogAddonInfo.h | 6 | ||||
-rw-r--r-- | xbmc/filesystem/AddonsDirectory.cpp | 41 | ||||
-rw-r--r-- | xbmc/filesystem/AddonsDirectory.h | 9 |
9 files changed, 227 insertions, 23 deletions
diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po index d725435f93..c684e01095 100644 --- a/addons/resource.language.en_gb/resources/strings.po +++ b/addons/resource.language.en_gb/resources/strings.po @@ -22011,7 +22011,13 @@ msgctxt "#39027" msgid "Sortname" msgstr "" -#empty strings from id 39028 to 39029 +#. Text for yes/no dialog when silently uninstalling an addon +#: xbmc/addons/gui/GUIDialogAddonInfo.cpp +msgctxt "#39028" +msgid "Addon \"{0:s}\"[CR]Origin \"{1:s}\"[CR]Version \"{2:s}\"[CR]- will uninstall and be replaced. Would you like to proceed?" +msgstr "" + +#empty string id 39029 #. Media source, a filter and smart playlist rule option #: xbmc/dialogs/GUIDialogMediaFilter.cpp diff --git a/xbmc/addons/AddonManager.cpp b/xbmc/addons/AddonManager.cpp index 2f6201e45f..f9a133474c 100644 --- a/xbmc/addons/AddonManager.cpp +++ b/xbmc/addons/AddonManager.cpp @@ -209,23 +209,42 @@ bool CAddonMgr::ReloadSettings(const std::string &id) return false; } -VECADDONS CAddonMgr::GetAvailableUpdates() const +std::vector<std::shared_ptr<IAddon>> CAddonMgr::GetAvailableUpdates() const +{ + return GetAvailableUpdatesOrOutdatedAddons(false); +} + +std::vector<std::shared_ptr<IAddon>> CAddonMgr::GetOutdatedAddons() const +{ + return GetAvailableUpdatesOrOutdatedAddons(true); +} + +std::vector<std::shared_ptr<IAddon>> CAddonMgr::GetAvailableUpdatesOrOutdatedAddons( + bool returnOutdatedAddons) const { CSingleLock lock(m_critSection); auto start = XbmcThreads::SystemClockMillis(); - VECADDONS updates; - VECADDONS installed; + std::vector<std::shared_ptr<IAddon>> result; + std::vector<std::shared_ptr<IAddon>> installed; CAddonRepos addonRepos(*this); addonRepos.LoadAddonsFromDatabase(m_database); GetAddonsForUpdate(installed); - addonRepos.BuildUpdateList(installed, updates); + if (returnOutdatedAddons) + { + addonRepos.BuildOutdatedList(installed, result); + } + else + { + addonRepos.BuildUpdateList(installed, result); + } - CLog::Log(LOGDEBUG, "CAddonMgr::GetAvailableUpdates took %i ms", XbmcThreads::SystemClockMillis() - start); - return updates; + CLog::Log(LOGDEBUG, "CAddonMgr::GetAvailableUpdatesOrOutdatedAddons took %i ms", + XbmcThreads::SystemClockMillis() - start); + return result; } bool CAddonMgr::HasAvailableUpdates() @@ -814,6 +833,15 @@ bool CAddonMgr::IsAddonInstalled(const std::string& ID) return GetAddon(ID, tmp, ADDON_UNKNOWN, false); } +bool CAddonMgr::IsAddonInstalled(const std::string& ID, + const std::string& origin, + const AddonVersion& version) +{ + AddonPtr tmp; + return (GetAddon(ID, tmp, ADDON_UNKNOWN, false) && tmp && tmp->Origin() == origin && + tmp->Version() == version); +} + bool CAddonMgr::CanAddonBeInstalled(const AddonPtr& addon) { return addon != nullptr && !addon->IsBroken() && !IsAddonInstalled(addon->ID()); diff --git a/xbmc/addons/AddonManager.h b/xbmc/addons/AddonManager.h index 9ab35a32dc..c5c9f28ba6 100644 --- a/xbmc/addons/AddonManager.h +++ b/xbmc/addons/AddonManager.h @@ -123,7 +123,10 @@ namespace ADDON bool ReloadSettings(const std::string &id); /*! Get addons with available updates */ - VECADDONS GetAvailableUpdates() const; + std::vector<std::shared_ptr<IAddon>> GetAvailableUpdates() const; + + /*! Get addons that are outdated */ + std::vector<std::shared_ptr<IAddon>> GetOutdatedAddons() const; /*! Returns true if there is any addon with available updates, otherwise false */ bool HasAvailableUpdates(); @@ -222,6 +225,16 @@ namespace ADDON */ bool IsAddonInstalled(const std::string& ID); + /* \brief Checks whether an addon is installed from a + * particular origin repo and version + * \param ID id of the addon + * \param origin origin repository id + * \param version the version of the addon + */ + bool IsAddonInstalled(const std::string& ID, + const std::string& origin, + const AddonVersion& version); + /* \brief Checks whether an addon can be installed. Broken addons can't be installed. \param addon addon to be checked */ @@ -380,10 +393,21 @@ namespace ADDON VECADDONS m_updateableAddons; + /*! + * \brief returns a vector with either available updates or outdated addons. + * usually called by its wrappers GetAvailableUpdates() or + * GetOutdatedAddons() + * \param[in] true to return outdated addons, false to return available updates + * \return vector filled with either available updates or outdated addons + */ + std::vector<std::shared_ptr<IAddon>> GetAvailableUpdatesOrOutdatedAddons( + bool returnOutdatedAddons) const; + bool GetAddonsInternal(const TYPE& type, VECADDONS& addons, bool enabledOnly, bool checkIncompatible = false) const; + bool EnableSingle(const std::string& id); void FindAddons(ADDON_INFO_LIST& addonmap, const std::string& path); diff --git a/xbmc/addons/AddonRepos.cpp b/xbmc/addons/AddonRepos.cpp index 86f6766341..919d304329 100644 --- a/xbmc/addons/AddonRepos.cpp +++ b/xbmc/addons/AddonRepos.cpp @@ -181,15 +181,29 @@ void CAddonRepos::AddAddonIfLatest( void CAddonRepos::BuildUpdateList(const std::vector<std::shared_ptr<IAddon>>& installed, std::vector<std::shared_ptr<IAddon>>& updates) const { + BuildUpdateOrOutdatedList(installed, updates, false); +} + +void CAddonRepos::BuildOutdatedList(const std::vector<std::shared_ptr<IAddon>>& installed, + std::vector<std::shared_ptr<IAddon>>& outdated) const +{ + BuildUpdateOrOutdatedList(installed, outdated, true); +} + +void CAddonRepos::BuildUpdateOrOutdatedList(const std::vector<std::shared_ptr<IAddon>>& installed, + std::vector<std::shared_ptr<IAddon>>& result, + bool returnOutdatedAddons) const +{ std::shared_ptr<IAddon> update; - CLog::Log(LOGDEBUG, "ADDONS: *** building update list (installed add-ons) ***"); + CLog::Log(LOGDEBUG, "CAddonRepos::{}: Building {} list from installed add-ons", __func__, + returnOutdatedAddons ? "outdated" : "update"); for (const auto& addon : installed) { if (DoAddonUpdateCheck(addon, update)) { - updates.emplace_back(update); + result.emplace_back(returnOutdatedAddons ? addon : update); } } } @@ -348,6 +362,49 @@ void CAddonRepos::GetLatestAddonVersions(std::vector<std::shared_ptr<IAddon>>& a } } +void CAddonRepos::GetLatestAddonVersionsFromAllRepos( + std::vector<std::shared_ptr<IAddon>>& addonList) const +{ + const AddonRepoUpdateMode updateMode = + CAddonSystemSettings::GetInstance().GetAddonRepoUpdateMode(); + + addonList.clear(); + + // first we insert all official addon versions into the resulting vector + + std::transform(m_latestOfficialVersions.begin(), m_latestOfficialVersions.end(), + back_inserter(addonList), + [](const std::pair<std::string, std::shared_ptr<IAddon>>& officialVersion) { + return officialVersion.second; + }); + + // then we insert latest version per addon and repository if they don't exist in the official map + // or installation from ANY_REPOSITORY is allowed and the private version is higher + + for (const auto& repo : m_latestVersionsByRepo) + { + // content of official repos is stored in m_latestVersionsByRepo too + // so we need to filter them out + + if (std::none_of(officialRepoInfos.begin(), officialRepoInfos.end(), + [&](const ADDON::RepoInfo& officialRepo) { + return repo.first == officialRepo.m_repoId; + })) + { + for (const auto& latestAddon : repo.second) + { + const auto& officialVersion = m_latestOfficialVersions.find(latestAddon.first); + if (officialVersion == m_latestOfficialVersions.end() || + (updateMode == AddonRepoUpdateMode::ANY_REPOSITORY && + latestAddon.second->Version() > officialVersion->second->Version())) + { + addonList.emplace_back(latestAddon.second); + } + } + } + } +} + bool CAddonRepos::FindDependency(const std::string& dependsId, const std::shared_ptr<IAddon>& parent, std::shared_ptr<IAddon>& dependencyToInstall, diff --git a/xbmc/addons/AddonRepos.h b/xbmc/addons/AddonRepos.h index 2d49477b26..4c9aae26cf 100644 --- a/xbmc/addons/AddonRepos.h +++ b/xbmc/addons/AddonRepos.h @@ -67,6 +67,16 @@ public: std::vector<std::shared_ptr<IAddon>>& updates) const; /*! + * \brief Build the list of addons that are outdated and have an update + * available depending on defined rules + * \param installed vector of all addons installed on the system that are + * checked for an update + * \param[out] outdated list of addon versions that have an update available + */ + void BuildOutdatedList(const std::vector<std::shared_ptr<IAddon>>& installed, + std::vector<std::shared_ptr<IAddon>>& outdated) const; + + /*! * \brief Checks if the origin-repository of a given addon is defined as official repo * but does not check the origin path (e.g. https://mirrors.kodi.tv ...) * \param addon pointer to addon to be checked @@ -105,13 +115,21 @@ public: /*! * \brief Retrieves the latest official versions of addons to vector. - * Private versions are added obeying the set updateMode. + * Private versions are added obeying updateMode. * (either OFFICIAL_ONLY or ANY_REPOSITORY) * \param[out] addonList retrieved addon list in a vector */ void GetLatestAddonVersions(std::vector<std::shared_ptr<IAddon>>& addonList) const; /*! + * \brief Retrieves the latest official versions of addons to vector. + * Private versions (latest per repository) are added obeying updateMode. + * (either OFFICIAL_ONLY or ANY_REPOSITORY) + * \param[out] addonList retrieved addon list in a vector + */ + void GetLatestAddonVersionsFromAllRepos(std::vector<std::shared_ptr<IAddon>>& addonList) const; + + /*! * \brief Find a dependency to install during an addon install or update * If the dependency cannot be found in official versions we look in the * installing/updating addon's (the parent's) origin repository @@ -141,6 +159,14 @@ public: private: /*! + * \brief Executor for BuildUpdateList() and BuildOutdatedList() + * \sa BuildUpdateList() BuildOutdatedList() + */ + void BuildUpdateOrOutdatedList(const std::vector<std::shared_ptr<IAddon>>& installed, + std::vector<std::shared_ptr<IAddon>>& result, + bool returnOutdatedAddons) const; + + /*! * \brief Load the map of addons * \note this function should only by called from publicly exposed wrappers * \return true on success, false otherwise diff --git a/xbmc/addons/gui/GUIDialogAddonInfo.cpp b/xbmc/addons/gui/GUIDialogAddonInfo.cpp index 4690c2cbc1..e923547083 100644 --- a/xbmc/addons/gui/GUIDialogAddonInfo.cpp +++ b/xbmc/addons/gui/GUIDialogAddonInfo.cpp @@ -79,13 +79,16 @@ bool CGUIDialogAddonInfo::OnMessage(CGUIMessage& message) } if (iControl == CONTROL_BTN_INSTALL) { - if (!m_localAddon) + const auto& itemAddonInfo = m_item->GetAddonInfo(); + if (!CServiceBroker::GetAddonMgr().IsAddonInstalled( + itemAddonInfo->ID(), itemAddonInfo->Origin(), itemAddonInfo->Version())) { OnInstall(); return true; } else { + m_silentUninstall = false; OnUninstall(); return true; } @@ -158,7 +161,9 @@ void CGUIDialogAddonInfo::UpdateControls() if (!m_item) return; - bool isInstalled = NULL != m_localAddon.get(); + const auto& itemAddonInfo = m_item->GetAddonInfo(); + bool isInstalled = CServiceBroker::GetAddonMgr().IsAddonInstalled( + itemAddonInfo->ID(), itemAddonInfo->Origin(), itemAddonInfo->Version()); m_addonEnabled = m_localAddon && !CServiceBroker::GetAddonMgr().IsAddonDisabled(m_localAddon->ID()); bool canDisable = @@ -329,9 +334,27 @@ void CGUIDialogAddonInfo::OnInstall() if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER)) return; - if (m_localAddon || !m_item->HasAddonInfo()) + if (!m_item->HasAddonInfo()) return; + if (m_localAddon) + { + const std::string& header = g_localizeStrings.Get(19098); // Warning! + const std::string text = + StringUtils::Format(g_localizeStrings.Get(39028), m_localAddon->ID(), + m_localAddon->Origin(), m_localAddon->Version().asString()); + + if (CGUIDialogYesNo::ShowAndGetInput(header, text)) + { + m_silentUninstall = true; + OnUninstall(); + } + else + { + return; + } + } + const auto& itemAddonInfo = m_item->GetAddonInfo(); const std::string& addonId = itemAddonInfo->ID(); @@ -429,7 +452,7 @@ void CGUIDialogAddonInfo::OnUninstall() return; // prompt user to be sure - if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{24037}, CVariant{750})) + if (!m_silentUninstall && !CGUIDialogYesNo::ShowAndGetInput(CVariant{24037}, CVariant{750})) return; bool removeData = false; diff --git a/xbmc/addons/gui/GUIDialogAddonInfo.h b/xbmc/addons/gui/GUIDialogAddonInfo.h index a817f1aa79..9a66c4c794 100644 --- a/xbmc/addons/gui/GUIDialogAddonInfo.h +++ b/xbmc/addons/gui/GUIDialogAddonInfo.h @@ -86,4 +86,10 @@ private: CFileItemPtr m_item; ADDON::AddonPtr m_localAddon; bool m_addonEnabled = false; + + /*!< a switch to force @ref OnUninstall() to proceed without user interaction. + * useful for cases like where another repo’s version of an addon must + * be removed before installing a new version. + */ + bool m_silentUninstall = false; }; diff --git a/xbmc/filesystem/AddonsDirectory.cpp b/xbmc/filesystem/AddonsDirectory.cpp index efbe6c1932..3d78efa39c 100644 --- a/xbmc/filesystem/AddonsDirectory.cpp +++ b/xbmc/filesystem/AddonsDirectory.cpp @@ -498,7 +498,7 @@ static void DependencyAddons(const CURL& path, CFileItemList &items) static void OutdatedAddons(const CURL& path, CFileItemList &items) { VECADDONS addons = CServiceBroker::GetAddonMgr().GetAvailableUpdates(); - CAddonsDirectory::GenerateAddonListing(path, addons, items, g_localizeStrings.Get(24043)); + CAddonsDirectory::GenerateAddonListingUpdates(path, addons, items, g_localizeStrings.Get(24043)); if (!items.IsEmpty()) { @@ -537,8 +537,8 @@ static bool Browse(const CURL& path, CFileItemList &items) } database.Close(); - // get all addons - addonRepos.GetLatestAddonVersions(addons); + // get all latest addon versions by repo + addonRepos.GetLatestAddonVersionsFromAllRepos(addons); items.SetProperty("reponame", g_localizeStrings.Get(24087)); items.SetLabel(g_localizeStrings.Get(24087)); @@ -781,9 +781,26 @@ bool CAddonsDirectory::IsRepoDirectory(const CURL& url) void CAddonsDirectory::GenerateAddonListing(const CURL &path, const VECADDONS& addons, CFileItemList &items, const std::string label) { - std::set<std::string> outdated; - for (const auto& addon : CServiceBroker::GetAddonMgr().GetAvailableUpdates()) - outdated.insert(addon->ID()); + GenerateAddonListing(path, addons, items, label, false); +} + +void CAddonsDirectory::GenerateAddonListingUpdates(const CURL& path, + const VECADDONS& addons, + CFileItemList& items, + const std::string label) +{ + GenerateAddonListing(path, addons, items, label, true); +} + +void CAddonsDirectory::GenerateAddonListing(const CURL& path, + const VECADDONS& addons, + CFileItemList& items, + const std::string label, + bool alwaysShowUpdateIcon) +{ + std::set<std::shared_ptr<IAddon>> outdated; + for (const auto& addon : CServiceBroker::GetAddonMgr().GetOutdatedAddons()) + outdated.insert(addon); items.ClearItems(); items.SetContent("addons"); @@ -794,9 +811,17 @@ void CAddonsDirectory::GenerateAddonListing(const CURL &path, itemPath.SetFileName(addon->ID()); CFileItemPtr pItem = FileItemFromAddon(addon, itemPath.Get(), false); - bool installed = CServiceBroker::GetAddonMgr().IsAddonInstalled(addon->ID()); + bool installed = CServiceBroker::GetAddonMgr().IsAddonInstalled(addon->ID(), addon->Origin(), + addon->Version()); bool disabled = CServiceBroker::GetAddonMgr().IsAddonDisabled(addon->ID()); - bool hasUpdate = outdated.find(addon->ID()) != outdated.end(); + + bool hasUpdate = + alwaysShowUpdateIcon || + std::any_of(outdated.begin(), outdated.end(), [&](const std::shared_ptr<IAddon>& i) { + return i->ID() == addon->ID() && i->Origin() == addon->Origin() && + i->Version() == addon->Version(); + }); + bool fromOfficialRepo = CAddonRepos::IsFromOfficialRepo(addon); pItem->SetProperty("Addon.IsInstalled", installed); diff --git a/xbmc/filesystem/AddonsDirectory.h b/xbmc/filesystem/AddonsDirectory.h index 06802cb3e5..0e154af6fd 100644 --- a/xbmc/filesystem/AddonsDirectory.h +++ b/xbmc/filesystem/AddonsDirectory.h @@ -46,6 +46,10 @@ namespace XFILE static bool GetScriptsAndPlugins(const std::string &content, CFileItemList &items); static void GenerateAddonListing(const CURL &path, const ADDON::VECADDONS& addons, CFileItemList &items, const std::string label); + static void GenerateAddonListingUpdates(const CURL& path, + const ADDON::VECADDONS& addons, + CFileItemList& items, + const std::string label); static CFileItemPtr FileItemFromAddon(const ADDON::AddonPtr &addon, const std::string& path, bool folder = false); /*! \brief Returns true if `path` is a path or subpath of the repository directory, otherwise false */ @@ -53,5 +57,10 @@ namespace XFILE private: bool GetSearchResults(const CURL& path, CFileItemList &items); + static void GenerateAddonListing(const CURL& path, + const ADDON::VECADDONS& addons, + CFileItemList& items, + const std::string label, + bool alwaysShowUpdateIcon); }; } |