summaryrefslogtreecommitdiffstats
path: root/xbmc/addons/gui/GUIDialogAddonInfo.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
commitc04dcc2e7d834218ef2d4194331e383402495ae1 (patch)
tree7333e38d10d75386e60f336b80c2443c1166031d /xbmc/addons/gui/GUIDialogAddonInfo.cpp
parentInitial commit. (diff)
downloadkodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz
kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/addons/gui/GUIDialogAddonInfo.cpp')
-rw-r--r--xbmc/addons/gui/GUIDialogAddonInfo.cpp940
1 files changed, 940 insertions, 0 deletions
diff --git a/xbmc/addons/gui/GUIDialogAddonInfo.cpp b/xbmc/addons/gui/GUIDialogAddonInfo.cpp
new file mode 100644
index 0000000..3acdbf5
--- /dev/null
+++ b/xbmc/addons/gui/GUIDialogAddonInfo.cpp
@@ -0,0 +1,940 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "GUIDialogAddonInfo.h"
+
+#include "FileItem.h"
+#include "GUIPassword.h"
+#include "ServiceBroker.h"
+#include "Util.h"
+#include "addons/AddonDatabase.h"
+#include "addons/AddonInstaller.h"
+#include "addons/AddonManager.h"
+#include "addons/AddonRepos.h"
+#include "addons/AddonSystemSettings.h"
+#include "addons/AudioDecoder.h"
+#include "addons/ExtsMimeSupportList.h"
+#include "addons/IAddon.h"
+#include "addons/addoninfo/AddonInfo.h"
+#include "addons/addoninfo/AddonType.h"
+#include "addons/gui/GUIDialogAddonSettings.h"
+#include "addons/gui/GUIHelpers.h"
+#include "dialogs/GUIDialogSelect.h"
+#include "dialogs/GUIDialogYesNo.h"
+#include "filesystem/Directory.h"
+#include "games/GameUtils.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "input/actions/Action.h"
+#include "input/actions/ActionIDs.h"
+#include "interfaces/builtins/Builtins.h"
+#include "messaging/helpers/DialogOKHelper.h"
+#include "pictures/GUIWindowSlideShow.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/Digest.h"
+#include "utils/JobManager.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+
+#include <functional>
+#include <sstream>
+#include <utility>
+
+#define CONTROL_BTN_INSTALL 6
+#define CONTROL_BTN_ENABLE 7
+#define CONTROL_BTN_UPDATE 8
+#define CONTROL_BTN_SETTINGS 9
+#define CONTROL_BTN_DEPENDENCIES 10
+#define CONTROL_BTN_SELECT 12
+#define CONTROL_BTN_AUTOUPDATE 13
+#define CONTROL_BTN_VERSIONS 14
+#define CONTROL_LIST_SCREENSHOTS 50
+
+using namespace KODI;
+using namespace ADDON;
+using namespace KODI::ADDONS;
+using namespace XFILE;
+using namespace KODI::MESSAGING;
+
+CGUIDialogAddonInfo::CGUIDialogAddonInfo(void)
+ : CGUIDialog(WINDOW_DIALOG_ADDON_INFO, "DialogAddonInfo.xml")
+{
+ m_item = CFileItemPtr(new CFileItem);
+ m_loadType = KEEP_IN_MEMORY;
+}
+
+CGUIDialogAddonInfo::~CGUIDialogAddonInfo(void) = default;
+
+bool CGUIDialogAddonInfo::OnMessage(CGUIMessage& message)
+{
+ switch (message.GetMessage())
+ {
+ case GUI_MSG_CLICKED:
+ {
+ int iControl = message.GetSenderId();
+ if (iControl == CONTROL_BTN_UPDATE)
+ {
+ OnUpdate();
+ return true;
+ }
+ else if (iControl == CONTROL_BTN_INSTALL)
+ {
+ 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;
+ }
+ }
+ else if (iControl == CONTROL_BTN_SELECT)
+ {
+ OnSelect();
+ return true;
+ }
+ else if (iControl == CONTROL_BTN_ENABLE)
+ {
+ OnEnableDisable();
+ return true;
+ }
+ else if (iControl == CONTROL_BTN_SETTINGS)
+ {
+ OnSettings();
+ return true;
+ }
+ else if (iControl == CONTROL_BTN_DEPENDENCIES)
+ {
+ ShowDependencyList(Reactivate::CHOICE_YES, EntryPoint::SHOW_DEPENDENCIES);
+ return true;
+ }
+ else if (iControl == CONTROL_BTN_AUTOUPDATE)
+ {
+ OnToggleAutoUpdates();
+ return true;
+ }
+ else if (iControl == CONTROL_LIST_SCREENSHOTS)
+ {
+ if (message.GetParam1() == ACTION_SELECT_ITEM ||
+ message.GetParam1() == ACTION_MOUSE_LEFT_CLICK)
+ {
+ CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), iControl);
+ OnMessage(msg);
+ int start = msg.GetParam1();
+ if (start >= 0 && start < static_cast<int>(m_item->GetAddonInfo()->Screenshots().size()))
+ CGUIWindowSlideShow::RunSlideShow(m_item->GetAddonInfo()->Screenshots(), start);
+ }
+ }
+ else if (iControl == CONTROL_BTN_VERSIONS)
+ {
+ OnSelectVersion();
+ return true;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return CGUIDialog::OnMessage(message);
+}
+
+bool CGUIDialogAddonInfo::OnAction(const CAction& action)
+{
+ if (action.GetID() == ACTION_SHOW_INFO)
+ {
+ Close();
+ return true;
+ }
+ return CGUIDialog::OnAction(action);
+}
+
+void CGUIDialogAddonInfo::OnInitWindow()
+{
+ CGUIDialog::OnInitWindow();
+ BuildDependencyList();
+ UpdateControls(PerformButtonFocus::CHOICE_YES);
+}
+
+void CGUIDialogAddonInfo::UpdateControls(PerformButtonFocus performButtonFocus)
+{
+ if (!m_item)
+ return;
+
+ 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 =
+ isInstalled && CServiceBroker::GetAddonMgr().CanAddonBeDisabled(m_localAddon->ID());
+ bool canInstall = !isInstalled && itemAddonInfo->LifecycleState() != AddonLifecycleState::BROKEN;
+ bool canUninstall = m_localAddon && CServiceBroker::GetAddonMgr().CanUninstall(m_localAddon);
+
+ bool isUpdate = (!isInstalled && CServiceBroker::GetAddonMgr().IsAddonInstalled(
+ itemAddonInfo->ID(), itemAddonInfo->Origin()));
+
+ bool showUpdateButton = m_localAddon &&
+ CServiceBroker::GetAddonMgr().IsAutoUpdateable(m_localAddon->ID()) &&
+ m_item->GetProperty("Addon.HasUpdate").asBoolean();
+
+ if (isInstalled)
+ {
+ SET_CONTROL_LABEL(CONTROL_BTN_INSTALL, 24037); // uninstall
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_INSTALL, canUninstall);
+ }
+ else
+ {
+ if (isUpdate)
+ {
+ SET_CONTROL_LABEL(CONTROL_BTN_INSTALL, 24138); // update
+ }
+ else
+ {
+ SET_CONTROL_LABEL(CONTROL_BTN_INSTALL, 24038); // install
+ }
+
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_INSTALL, canInstall);
+ if (canInstall && performButtonFocus == PerformButtonFocus::CHOICE_YES)
+ {
+ SET_CONTROL_FOCUS(CONTROL_BTN_INSTALL, 0);
+ }
+ }
+
+ if (showUpdateButton)
+ {
+ SET_CONTROL_VISIBLE(CONTROL_BTN_UPDATE);
+ SET_CONTROL_HIDDEN(CONTROL_BTN_VERSIONS);
+ }
+ else
+ {
+ SET_CONTROL_VISIBLE(CONTROL_BTN_VERSIONS);
+ SET_CONTROL_HIDDEN(CONTROL_BTN_UPDATE);
+ }
+
+ if (m_addonEnabled)
+ {
+ SET_CONTROL_LABEL(CONTROL_BTN_ENABLE, 24021);
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_ENABLE, canDisable);
+ }
+ else
+ {
+ SET_CONTROL_LABEL(CONTROL_BTN_ENABLE, 24022);
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_ENABLE, isInstalled);
+ }
+
+ bool autoUpdatesOn = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
+ CSettings::SETTING_ADDONS_AUTOUPDATES) == AUTO_UPDATES_ON;
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_AUTOUPDATE, isInstalled && autoUpdatesOn);
+ SET_CONTROL_SELECTED(GetID(), CONTROL_BTN_AUTOUPDATE,
+ isInstalled && autoUpdatesOn &&
+ CServiceBroker::GetAddonMgr().IsAutoUpdateable(m_localAddon->ID()));
+ SET_CONTROL_LABEL(CONTROL_BTN_AUTOUPDATE, 21340);
+
+ const bool active = m_localAddon && CAddonSystemSettings::GetInstance().IsActive(*m_localAddon);
+ CONTROL_ENABLE_ON_CONDITION(
+ CONTROL_BTN_SELECT,
+ m_addonEnabled && (CanShowSupportList() || CanOpen() || CanRun() || (CanUse() && !active)));
+
+ int label;
+ if (CanShowSupportList())
+ label = 21484;
+ else if (CanUse())
+ label = 21480;
+ else if (CanOpen())
+ label = 21478;
+ else
+ label = 21479;
+ SET_CONTROL_LABEL(CONTROL_BTN_SELECT, label);
+
+ const bool hasSettings = m_localAddon && m_localAddon->CanHaveAddonOrInstanceSettings();
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_SETTINGS, isInstalled && hasSettings);
+ if (isInstalled && hasSettings && performButtonFocus == PerformButtonFocus::CHOICE_YES)
+ {
+ SET_CONTROL_FOCUS(CONTROL_BTN_SETTINGS, 0);
+ }
+
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_DEPENDENCIES, !m_depsInstalledWithAvailable.empty());
+
+ CFileItemList items;
+ for (const auto& screenshot : m_item->GetAddonInfo()->Screenshots())
+ {
+ auto item = std::make_shared<CFileItem>("");
+ item->SetArt("thumb", screenshot);
+ items.Add(std::move(item));
+ }
+ CGUIMessage msg(GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST_SCREENSHOTS, 0, 0, &items);
+ OnMessage(msg);
+}
+
+static const std::string LOCAL_CACHE =
+ "\\0_local_cache"; // \0 to give it the lowest priority when sorting
+
+int CGUIDialogAddonInfo::AskForVersion(std::vector<std::pair<CAddonVersion, std::string>>& versions)
+{
+ auto dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(
+ WINDOW_DIALOG_SELECT);
+ dialog->Reset();
+ dialog->SetHeading(CVariant{21338});
+ dialog->SetUseDetails(true);
+
+ for (const auto& versionInfo : versions)
+ {
+ CFileItem item(StringUtils::Format(g_localizeStrings.Get(21339), versionInfo.first.asString()));
+ if (m_localAddon && m_localAddon->Version() == versionInfo.first &&
+ m_item->GetAddonInfo()->Origin() == versionInfo.second)
+ item.Select(true);
+
+ AddonPtr repo;
+ if (versionInfo.second == LOCAL_CACHE)
+ {
+ item.SetLabel2(g_localizeStrings.Get(24095));
+ item.SetArt("icon", "DefaultAddonRepository.png");
+ dialog->Add(item);
+ }
+ else if (CServiceBroker::GetAddonMgr().GetAddon(versionInfo.second, repo, AddonType::REPOSITORY,
+ OnlyEnabled::CHOICE_YES))
+ {
+ item.SetLabel2(repo->Name());
+ item.SetArt("icon", repo->Icon());
+ dialog->Add(item);
+ }
+ }
+
+ dialog->Open();
+ return dialog->IsConfirmed() ? dialog->GetSelectedItem() : -1;
+}
+
+void CGUIDialogAddonInfo::OnUpdate()
+{
+ const auto& itemAddonInfo = m_item->GetAddonInfo();
+ const std::string& addonId = itemAddonInfo->ID();
+ const std::string& origin = m_item->GetProperty("Addon.ValidUpdateOrigin").asString();
+ const CAddonVersion& version =
+ static_cast<CAddonVersion>(m_item->GetProperty("Addon.ValidUpdateVersion").asString());
+
+ Close();
+ if (!m_depsInstalledWithAvailable.empty() &&
+ !ShowDependencyList(Reactivate::CHOICE_NO, EntryPoint::UPDATE))
+ return;
+
+ CAddonInstaller::GetInstance().Install(addonId, version, origin);
+}
+
+void CGUIDialogAddonInfo::OnSelectVersion()
+{
+ if (!m_item->HasAddonInfo())
+ return;
+
+ const std::string& processAddonId = m_item->GetAddonInfo()->ID();
+ EntryPoint entryPoint = m_localAddon ? EntryPoint::UPDATE : EntryPoint::INSTALL;
+
+ // get all compatible versions of an addon-id regardless of their origin
+ std::vector<std::shared_ptr<IAddon>> compatibleVersions =
+ CServiceBroker::GetAddonMgr().GetCompatibleVersions(processAddonId);
+
+ std::vector<std::pair<CAddonVersion, std::string>> versions;
+ versions.reserve(compatibleVersions.size());
+
+ for (const auto& compatibleVersion : compatibleVersions)
+ versions.emplace_back(compatibleVersion->Version(), compatibleVersion->Origin());
+
+ CAddonDatabase database;
+ database.Open();
+
+ CFileItemList items;
+ if (XFILE::CDirectory::GetDirectory("special://home/addons/packages/", items, ".zip",
+ DIR_FLAG_NO_FILE_DIRS))
+ {
+ for (int i = 0; i < items.Size(); ++i)
+ {
+ std::string packageId;
+ std::string versionString;
+ if (CAddonVersion::SplitFileName(packageId, versionString, items[i]->GetLabel()))
+ {
+ if (packageId == processAddonId)
+ {
+ std::string hash;
+ std::string path(items[i]->GetPath());
+ if (database.GetPackageHash(processAddonId, items[i]->GetPath(), hash))
+ {
+ std::string sha256 = CUtil::GetFileDigest(path, KODI::UTILITY::CDigest::Type::SHA256);
+
+ // don't offer locally cached packages that result in an invalid version.
+ // usually this happens when the package filename gets malformed on the fs
+ // e.g. downloading "http://localhost/a+b.zip" ends up in "a b.zip"
+ const CAddonVersion version(versionString);
+ if (StringUtils::EqualsNoCase(sha256, hash) && !version.empty())
+ versions.emplace_back(version, LOCAL_CACHE);
+ }
+ }
+ }
+ }
+ }
+
+ if (versions.empty())
+ HELPERS::ShowOKDialogText(CVariant{21341}, CVariant{21342});
+ else
+ {
+ int i = AskForVersion(versions);
+ if (i != -1)
+ {
+ Close();
+
+ if (versions[i].second == LOCAL_CACHE)
+ {
+ CAddonInstaller::GetInstance().InstallFromZip(
+ StringUtils::Format("special://home/addons/packages/{}-{}.zip", processAddonId,
+ versions[i].first.asString()));
+ }
+ else
+ {
+ if (!m_depsInstalledWithAvailable.empty() &&
+ !ShowDependencyList(Reactivate::CHOICE_NO, entryPoint))
+ return;
+ CAddonInstaller::GetInstance().Install(processAddonId, versions[i].first,
+ versions[i].second);
+ }
+ }
+ }
+}
+
+void CGUIDialogAddonInfo::OnToggleAutoUpdates()
+{
+ CGUIMessage msg(GUI_MSG_IS_SELECTED, GetID(), CONTROL_BTN_AUTOUPDATE);
+ if (OnMessage(msg))
+ {
+ bool selected = msg.GetParam1() == 1;
+ if (selected)
+ CServiceBroker::GetAddonMgr().RemoveAllUpdateRulesFromList(m_localAddon->ID());
+ else
+ CServiceBroker::GetAddonMgr().AddUpdateRuleToList(m_localAddon->ID(),
+ AddonUpdateRule::USER_DISABLED_AUTO_UPDATE);
+
+ bool showUpdateButton = (selected && m_item->GetProperty("Addon.HasUpdate").asBoolean());
+
+ if (showUpdateButton)
+ {
+ SET_CONTROL_VISIBLE(CONTROL_BTN_UPDATE);
+ SET_CONTROL_HIDDEN(CONTROL_BTN_VERSIONS);
+ }
+ else
+ {
+ SET_CONTROL_VISIBLE(CONTROL_BTN_VERSIONS);
+ SET_CONTROL_HIDDEN(CONTROL_BTN_UPDATE);
+ }
+
+ CServiceBroker::GetAddonMgr().PublishEventAutoUpdateStateChanged(m_localAddon->ID());
+ }
+}
+
+void CGUIDialogAddonInfo::OnInstall()
+{
+ if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER))
+ return;
+
+ if (!m_item->HasAddonInfo())
+ return;
+
+ const auto& itemAddonInfo = m_item->GetAddonInfo();
+ const std::string& origin = itemAddonInfo->Origin();
+
+ if (m_localAddon && CAddonSystemSettings::GetInstance().GetAddonRepoUpdateMode() !=
+ AddonRepoUpdateMode::ANY_REPOSITORY)
+ {
+ if (m_localAddon->Origin() != origin && m_localAddon->Origin() != ORIGIN_SYSTEM)
+ {
+ const std::string& header = g_localizeStrings.Get(19098); // Warning!
+ const std::string origin =
+ !m_localAddon->Origin().empty() ? m_localAddon->Origin() : g_localizeStrings.Get(39029);
+ const std::string text =
+ StringUtils::Format(g_localizeStrings.Get(39028), m_localAddon->Name(), origin,
+ m_localAddon->Version().asString());
+
+ if (CGUIDialogYesNo::ShowAndGetInput(header, text))
+ {
+ m_silentUninstall = true;
+ OnUninstall();
+ }
+ else
+ {
+ return;
+ }
+ }
+ }
+
+ const std::string& addonId = itemAddonInfo->ID();
+ const CAddonVersion& version = itemAddonInfo->Version();
+
+ Close();
+ if (!m_depsInstalledWithAvailable.empty() &&
+ !ShowDependencyList(Reactivate::CHOICE_NO, EntryPoint::INSTALL))
+ return;
+
+ CAddonInstaller::GetInstance().Install(addonId, version, origin);
+}
+
+void CGUIDialogAddonInfo::OnSelect()
+{
+ if (!m_localAddon)
+ return;
+
+ if (CanShowSupportList())
+ {
+ ShowSupportList();
+ return;
+ }
+
+ Close();
+
+ if (CanOpen() || CanRun())
+ CBuiltins::GetInstance().Execute("RunAddon(" + m_localAddon->ID() + ")");
+ else if (CanUse())
+ CAddonSystemSettings::GetInstance().SetActive(m_localAddon->Type(), m_localAddon->ID());
+}
+
+bool CGUIDialogAddonInfo::CanOpen() const
+{
+ return m_localAddon && m_localAddon->Type() == AddonType::PLUGIN;
+}
+
+bool CGUIDialogAddonInfo::CanRun() const
+{
+ if (m_localAddon)
+ {
+ if (m_localAddon->Type() == AddonType::SCRIPT)
+ return true;
+
+ if (GAME::CGameUtils::IsStandaloneGame(m_localAddon))
+ return true;
+ }
+
+ return false;
+}
+
+bool CGUIDialogAddonInfo::CanUse() const
+{
+ return m_localAddon && (m_localAddon->Type() == AddonType::SKIN ||
+ m_localAddon->Type() == AddonType::SCREENSAVER ||
+ m_localAddon->Type() == AddonType::VISUALIZATION ||
+ m_localAddon->Type() == AddonType::SCRIPT_WEATHER ||
+ m_localAddon->Type() == AddonType::RESOURCE_LANGUAGE ||
+ m_localAddon->Type() == AddonType::RESOURCE_UISOUNDS ||
+ m_localAddon->Type() == AddonType::AUDIOENCODER);
+}
+
+bool CGUIDialogAddonInfo::CanShowSupportList() const
+{
+ return m_localAddon && (m_localAddon->Type() == AddonType::AUDIODECODER ||
+ m_localAddon->Type() == AddonType::IMAGEDECODER);
+}
+
+bool CGUIDialogAddonInfo::PromptIfDependency(int heading, int line2)
+{
+ if (!m_localAddon)
+ return false;
+
+ VECADDONS addons;
+ std::vector<std::string> deps;
+ CServiceBroker::GetAddonMgr().GetAddons(addons);
+ for (VECADDONS::const_iterator it = addons.begin(); it != addons.end(); ++it)
+ {
+ auto i =
+ std::find_if((*it)->GetDependencies().begin(), (*it)->GetDependencies().end(),
+ [&](const DependencyInfo& other) { return other.id == m_localAddon->ID(); });
+ if (i != (*it)->GetDependencies().end() && !i->optional) // non-optional dependency
+ deps.push_back((*it)->Name());
+ }
+
+ if (!deps.empty())
+ {
+ std::string line0 = StringUtils::Format(g_localizeStrings.Get(24046), m_localAddon->Name());
+ std::string line1 = StringUtils::Join(deps, ", ");
+ HELPERS::ShowOKDialogLines(CVariant{heading}, CVariant{std::move(line0)},
+ CVariant{std::move(line1)}, CVariant{line2});
+ return true;
+ }
+ return false;
+}
+
+void CGUIDialogAddonInfo::OnUninstall()
+{
+ if (!m_localAddon.get())
+ return;
+
+ if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER))
+ return;
+
+ // ensure the addon is not a dependency of other installed addons
+ if (PromptIfDependency(24037, 24047))
+ return;
+
+ // prompt user to be sure
+ if (!m_silentUninstall && !CGUIDialogYesNo::ShowAndGetInput(CVariant{24037}, CVariant{750}))
+ return;
+
+ bool removeData = false;
+ if (CDirectory::Exists(m_localAddon->Profile()))
+ removeData = CGUIDialogYesNo::ShowAndGetInput(CVariant{24037}, CVariant{39014});
+
+ CAddonInstaller::GetInstance().UnInstall(m_localAddon, removeData);
+
+ Close();
+}
+
+void CGUIDialogAddonInfo::OnEnableDisable()
+{
+ if (!m_localAddon)
+ return;
+
+ if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER))
+ return;
+
+ if (m_addonEnabled)
+ {
+ if (PromptIfDependency(24075, 24091))
+ return; //required. can't disable
+
+ CServiceBroker::GetAddonMgr().DisableAddon(m_localAddon->ID(), AddonDisabledReason::USER);
+ }
+ else
+ {
+ // Check user want to enable if lifecycle not normal
+ if (!ADDON::GUI::CHelpers::DialogAddonLifecycleUseAsk(m_localAddon))
+ return;
+
+ CServiceBroker::GetAddonMgr().EnableAddon(m_localAddon->ID());
+ }
+
+ UpdateControls(PerformButtonFocus::CHOICE_NO);
+}
+
+void CGUIDialogAddonInfo::OnSettings()
+{
+ CGUIDialogAddonSettings::ShowForAddon(m_localAddon);
+}
+
+bool CGUIDialogAddonInfo::ShowDependencyList(Reactivate reactivate, EntryPoint entryPoint)
+{
+ if (entryPoint != EntryPoint::INSTALL || m_showDepDialogOnInstall)
+ {
+ auto pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(
+ WINDOW_DIALOG_SELECT);
+ CFileItemList items;
+
+ for (const auto& it : m_depsInstalledWithAvailable)
+ {
+ // All combinations of depAddon and localAddon validity are possible and information
+ // must be displayed even when there is no depAddon.
+ // info_addon is the add-on to take the information to display (name, icon) from. The
+ // version in the repository is preferred because it might contain more recent data.
+
+ std::shared_ptr<IAddon> infoAddon = it.m_available ? it.m_available : it.m_installed;
+
+ if (infoAddon)
+ {
+ if (entryPoint != EntryPoint::UPDATE || !it.IsInstalledUpToDate())
+ {
+ const CFileItemPtr item = std::make_shared<CFileItem>(infoAddon->Name());
+ int messageId = 24180; // minversion only
+
+ // dep not installed locally, but it is available from a repo!
+ // make sure only non-optional add-ons that meet versionMin are
+ // announced for installation
+
+ if (!it.m_installed)
+ {
+ if (entryPoint != EntryPoint::SHOW_DEPENDENCIES && !it.m_depInfo.optional)
+ {
+ if (it.m_depInfo.versionMin <= it.m_available->Version())
+ {
+ messageId = 24181; // => install
+ }
+ else
+ {
+ messageId = 24185; // => not available, only lower versions available in the repos
+ }
+ }
+ }
+ else // dep is installed locally
+ {
+ messageId = 24182; // => installed
+
+ if (!it.IsInstalledUpToDate())
+ {
+ messageId = 24183; // => update to
+ }
+ }
+
+ if (entryPoint == EntryPoint::SHOW_DEPENDENCIES ||
+ infoAddon->MainType() != AddonType::SCRIPT_MODULE ||
+ !CAddonRepos::IsFromOfficialRepo(infoAddon, CheckAddonPath::CHOICE_NO))
+ {
+ item->SetLabel2(StringUtils::Format(
+ g_localizeStrings.Get(messageId), it.m_depInfo.versionMin.asString(),
+ it.m_installed ? it.m_installed->Version().asString() : "",
+ it.m_available ? it.m_available->Version().asString() : "",
+ it.m_depInfo.optional ? g_localizeStrings.Get(24184) : ""));
+
+ item->SetArt("icon", infoAddon->Icon());
+ item->SetProperty("addon_id", it.m_depInfo.id);
+ items.Add(item);
+ }
+ }
+ }
+ else
+ {
+ const CFileItemPtr item = std::make_shared<CFileItem>(it.m_depInfo.id);
+ item->SetLabel2(g_localizeStrings.Get(10005)); // Not available
+ items.Add(item);
+ }
+ }
+
+ if (!items.IsEmpty())
+ {
+ CFileItemPtr backup_item = GetCurrentListItem();
+ while (true)
+ {
+ pDialog->Reset();
+ pDialog->SetHeading(reactivate == Reactivate::CHOICE_YES ? 39024 : 39020);
+ pDialog->SetUseDetails(true);
+ for (auto& it : items)
+ pDialog->Add(*it);
+ pDialog->EnableButton(reactivate == Reactivate::CHOICE_NO, 186);
+ pDialog->SetButtonFocus(true);
+ pDialog->Open();
+
+ if (pDialog->IsButtonPressed())
+ return true;
+
+ if (pDialog->IsConfirmed())
+ {
+ const CFileItemPtr& item = pDialog->GetSelectedFileItem();
+ std::string addon_id = item->GetProperty("addon_id").asString();
+ std::shared_ptr<IAddon> depAddon;
+ if (CServiceBroker::GetAddonMgr().FindInstallableById(addon_id, depAddon))
+ {
+ Close();
+ ShowForItem(std::make_shared<CFileItem>(depAddon));
+ }
+ }
+ else
+ break;
+ }
+ SetItem(backup_item);
+ if (reactivate == Reactivate::CHOICE_YES)
+ Open();
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void CGUIDialogAddonInfo::ShowSupportList()
+{
+ std::vector<KODI::ADDONS::AddonSupportEntry> list;
+ if (CanShowSupportList())
+ list =
+ CServiceBroker::GetExtsMimeSupportList().GetSupportedExtsAndMimeTypes(m_localAddon->ID());
+
+ auto pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(
+ WINDOW_DIALOG_SELECT);
+ CFileItemList items;
+ for (const auto& entry : list)
+ {
+ // Ignore included extension about track support
+ if (StringUtils::EndsWith(entry.m_name, KODI_ADDON_AUDIODECODER_TRACK_EXT))
+ continue;
+
+ std::string label;
+ if (entry.m_type == AddonSupportType::Extension)
+ label = StringUtils::Format(g_localizeStrings.Get(21346), entry.m_name);
+ else if (entry.m_type == AddonSupportType::Mimetype)
+ label = StringUtils::Format(g_localizeStrings.Get(21347), entry.m_name);
+ else
+ label = entry.m_name;
+
+ const CFileItemPtr item = std::make_shared<CFileItem>(label);
+ item->SetLabel2(entry.m_description);
+ if (!entry.m_icon.empty())
+ item->SetArt("icon", entry.m_icon);
+ else if (entry.m_type == AddonSupportType::Extension)
+ item->SetArt("icon", "DefaultExtensionInfo.png");
+ else if (entry.m_type == AddonSupportType::Mimetype)
+ item->SetArt("icon", "DefaultMimetypeInfo.png");
+ item->SetProperty("addon_id", m_localAddon->ID());
+ items.Add(item);
+ }
+
+ pDialog->Reset();
+ pDialog->SetHeading(21485);
+ pDialog->SetUseDetails(true);
+ for (auto& it : items)
+ pDialog->Add(*it);
+ pDialog->SetButtonFocus(true);
+ pDialog->Open();
+}
+
+bool CGUIDialogAddonInfo::ShowForItem(const CFileItemPtr& item)
+{
+ if (!item)
+ return false;
+
+ CGUIDialogAddonInfo* dialog =
+ CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogAddonInfo>(
+ WINDOW_DIALOG_ADDON_INFO);
+ if (!dialog)
+ return false;
+ if (!dialog->SetItem(item))
+ return false;
+
+ dialog->Open();
+ return true;
+}
+
+bool CGUIDialogAddonInfo::SetItem(const CFileItemPtr& item)
+{
+ if (!item || !item->HasAddonInfo())
+ return false;
+
+ m_item = std::make_shared<CFileItem>(*item);
+ m_localAddon.reset();
+ if (CServiceBroker::GetAddonMgr().GetAddon(item->GetAddonInfo()->ID(), m_localAddon,
+ OnlyEnabled::CHOICE_NO))
+ {
+ CLog::Log(LOGDEBUG, "{} - Addon with id {} not found locally.", __FUNCTION__,
+ item->GetAddonInfo()->ID());
+ }
+ return true;
+}
+
+void CGUIDialogAddonInfo::BuildDependencyList()
+{
+ if (!m_item)
+ return;
+
+ m_showDepDialogOnInstall = false;
+ m_depsInstalledWithAvailable.clear();
+ m_deps = CServiceBroker::GetAddonMgr().GetDepsRecursive(m_item->GetAddonInfo()->ID(),
+ OnlyEnabledRootAddon::CHOICE_NO);
+
+ for (const auto& dep : m_deps)
+ {
+ std::shared_ptr<IAddon> addonInstalled;
+ std::shared_ptr<IAddon> addonAvailable;
+
+ // Find add-on in local installation
+ if (!CServiceBroker::GetAddonMgr().GetAddon(dep.id, addonInstalled, OnlyEnabled::CHOICE_YES))
+ {
+ addonInstalled = nullptr;
+ }
+
+ // Find add-on in repositories
+ if (!CServiceBroker::GetAddonMgr().FindInstallableById(dep.id, addonAvailable))
+ {
+ addonAvailable = nullptr;
+ }
+
+ if (!addonInstalled)
+ {
+
+ // after pushing the install button the dependency install dialog
+ // will be opened only if...
+ // - dependencies are unavailable (for informational purposes) OR
+ // - the dependency is not a script/module OR
+ // - the script/module is not available at an official repo
+ if (!addonAvailable || addonAvailable->MainType() != AddonType::SCRIPT_MODULE ||
+ !CAddonRepos::IsFromOfficialRepo(addonAvailable, CheckAddonPath::CHOICE_NO))
+ {
+ m_showDepDialogOnInstall = true;
+ }
+ }
+ else
+ {
+
+ // only display dialog if updates for already installed dependencies will install
+ if (addonAvailable && addonAvailable->Version() > addonInstalled->Version())
+ {
+ m_showDepDialogOnInstall = true;
+ }
+ }
+
+ m_depsInstalledWithAvailable.emplace_back(dep, addonInstalled, addonAvailable);
+ }
+
+ std::sort(m_depsInstalledWithAvailable.begin(), m_depsInstalledWithAvailable.end(),
+ [](const auto& a, const auto& b) {
+ // 1. "not installed/available" go to the bottom first
+ const bool depAInstalledOrAvailable =
+ a.m_installed != nullptr || a.m_available != nullptr;
+ const bool depBInstalledOrAvailable =
+ b.m_installed != nullptr || b.m_available != nullptr;
+
+ if (depAInstalledOrAvailable != depBInstalledOrAvailable)
+ {
+ return !depAInstalledOrAvailable;
+ }
+
+ // 2. then optional add-ons to top
+ if (a.m_depInfo.optional != b.m_depInfo.optional)
+ {
+ return a.m_depInfo.optional;
+ }
+
+ // 3. addon type asc, except scripts/modules at the bottom
+ const std::shared_ptr<IAddon>& depA = a.m_installed ? a.m_installed : a.m_available;
+ const std::shared_ptr<IAddon>& depB = b.m_installed ? b.m_installed : b.m_available;
+
+ if (depA && depB)
+ {
+ const AddonType typeA = depA->MainType();
+ const AddonType typeB = depB->MainType();
+ if (typeA != typeB)
+ {
+ if ((typeA == AddonType::SCRIPT_MODULE) == (typeB == AddonType::SCRIPT_MODULE))
+ {
+ // both are scripts/modules or neither one is => sort by addon type asc
+ return typeA < typeB;
+ }
+ else
+ {
+ // At this point, either:
+ // A is script/module and B is not, or A is not script/module and B is.
+ // the script/module goes to the bottom
+ return typeA != AddonType::SCRIPT_MODULE;
+ }
+ }
+ }
+
+ // 4. finally order by addon-id
+ return a.m_depInfo.id < b.m_depInfo.id;
+ });
+}
+
+bool CInstalledWithAvailable::IsInstalledUpToDate() const
+{
+ if (m_installed)
+ {
+ if (!m_available || m_available->Version() == m_installed->Version())
+ {
+ return true;
+ }
+ }
+
+ return false;
+}