[Windows] Fix driver version detection of AMD RDNA+ GPU on Windows 10
[xbmc.git] / xbmc / addons / gui / GUIDialogAddonInfo.cpp
blob172af8b21675fcbfaed49f8384a4d7ad4e938fad
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "GUIDialogAddonInfo.h"
11 #include "FileItem.h"
12 #include "FileItemList.h"
13 #include "GUIPassword.h"
14 #include "ServiceBroker.h"
15 #include "Util.h"
16 #include "addons/AddonDatabase.h"
17 #include "addons/AddonInstaller.h"
18 #include "addons/AddonManager.h"
19 #include "addons/AddonRepos.h"
20 #include "addons/AddonSystemSettings.h"
21 #include "addons/AudioDecoder.h"
22 #include "addons/ExtsMimeSupportList.h"
23 #include "addons/IAddon.h"
24 #include "addons/addoninfo/AddonInfo.h"
25 #include "addons/addoninfo/AddonType.h"
26 #include "addons/gui/GUIDialogAddonSettings.h"
27 #include "addons/gui/GUIHelpers.h"
28 #include "dialogs/GUIDialogSelect.h"
29 #include "dialogs/GUIDialogYesNo.h"
30 #include "filesystem/Directory.h"
31 #include "games/GameUtils.h"
32 #include "guilib/GUIComponent.h"
33 #include "guilib/GUIWindowManager.h"
34 #include "guilib/LocalizeStrings.h"
35 #include "input/actions/Action.h"
36 #include "input/actions/ActionIDs.h"
37 #include "interfaces/builtins/Builtins.h"
38 #include "messaging/helpers/DialogOKHelper.h"
39 #include "pictures/GUIWindowSlideShow.h"
40 #include "settings/Settings.h"
41 #include "settings/SettingsComponent.h"
42 #include "utils/Digest.h"
43 #include "utils/JobManager.h"
44 #include "utils/StringUtils.h"
45 #include "utils/Variant.h"
46 #include "utils/log.h"
48 #include <functional>
49 #include <memory>
50 #include <sstream>
51 #include <utility>
53 #define CONTROL_BTN_INSTALL 6
54 #define CONTROL_BTN_ENABLE 7
55 #define CONTROL_BTN_UPDATE 8
56 #define CONTROL_BTN_SETTINGS 9
57 #define CONTROL_BTN_DEPENDENCIES 10
58 #define CONTROL_BTN_SELECT 12
59 #define CONTROL_BTN_AUTOUPDATE 13
60 #define CONTROL_BTN_VERSIONS 14
61 #define CONTROL_LIST_SCREENSHOTS 50
63 using namespace KODI;
64 using namespace ADDON;
65 using namespace KODI::ADDONS;
66 using namespace XFILE;
67 using namespace KODI::MESSAGING;
69 CGUIDialogAddonInfo::CGUIDialogAddonInfo(void)
70 : CGUIDialog(WINDOW_DIALOG_ADDON_INFO, "DialogAddonInfo.xml"),
71 m_item(std::make_shared<CFileItem>())
73 m_loadType = KEEP_IN_MEMORY;
76 CGUIDialogAddonInfo::~CGUIDialogAddonInfo(void) = default;
78 bool CGUIDialogAddonInfo::OnMessage(CGUIMessage& message)
80 switch (message.GetMessage())
82 case GUI_MSG_CLICKED:
84 int iControl = message.GetSenderId();
85 if (iControl == CONTROL_BTN_UPDATE)
87 OnUpdate();
88 return true;
90 else if (iControl == CONTROL_BTN_INSTALL)
92 const auto& itemAddonInfo = m_item->GetAddonInfo();
93 if (!CServiceBroker::GetAddonMgr().IsAddonInstalled(
94 itemAddonInfo->ID(), itemAddonInfo->Origin(), itemAddonInfo->Version()))
96 OnInstall();
97 return true;
99 else
101 m_silentUninstall = false;
102 OnUninstall();
103 return true;
106 else if (iControl == CONTROL_BTN_SELECT)
108 OnSelect();
109 return true;
111 else if (iControl == CONTROL_BTN_ENABLE)
113 OnEnableDisable();
114 return true;
116 else if (iControl == CONTROL_BTN_SETTINGS)
118 OnSettings();
119 return true;
121 else if (iControl == CONTROL_BTN_DEPENDENCIES)
123 ShowDependencyList(Reactivate::CHOICE_YES, EntryPoint::SHOW_DEPENDENCIES);
124 return true;
126 else if (iControl == CONTROL_BTN_AUTOUPDATE)
128 OnToggleAutoUpdates();
129 return true;
131 else if (iControl == CONTROL_LIST_SCREENSHOTS)
133 if (message.GetParam1() == ACTION_SELECT_ITEM ||
134 message.GetParam1() == ACTION_MOUSE_LEFT_CLICK)
136 CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), iControl);
137 OnMessage(msg);
138 int start = msg.GetParam1();
139 if (start >= 0 && start < static_cast<int>(m_item->GetAddonInfo()->Screenshots().size()))
140 CGUIWindowSlideShow::RunSlideShow(m_item->GetAddonInfo()->Screenshots(), start);
143 else if (iControl == CONTROL_BTN_VERSIONS)
145 OnSelectVersion();
146 return true;
149 break;
150 default:
151 break;
154 return CGUIDialog::OnMessage(message);
157 bool CGUIDialogAddonInfo::OnAction(const CAction& action)
159 if (action.GetID() == ACTION_SHOW_INFO)
161 Close();
162 return true;
164 return CGUIDialog::OnAction(action);
167 void CGUIDialogAddonInfo::OnInitWindow()
169 CGUIDialog::OnInitWindow();
170 BuildDependencyList();
171 UpdateControls(PerformButtonFocus::CHOICE_YES);
174 void CGUIDialogAddonInfo::UpdateControls(PerformButtonFocus performButtonFocus)
176 if (!m_item)
177 return;
179 const auto& itemAddonInfo = m_item->GetAddonInfo();
180 bool isInstalled = CServiceBroker::GetAddonMgr().IsAddonInstalled(
181 itemAddonInfo->ID(), itemAddonInfo->Origin(), itemAddonInfo->Version());
182 m_addonEnabled =
183 m_localAddon && !CServiceBroker::GetAddonMgr().IsAddonDisabled(m_localAddon->ID());
184 bool canDisable =
185 isInstalled && CServiceBroker::GetAddonMgr().CanAddonBeDisabled(m_localAddon->ID());
186 bool canInstall = !isInstalled && itemAddonInfo->LifecycleState() != AddonLifecycleState::BROKEN;
187 bool canUninstall = m_localAddon && CServiceBroker::GetAddonMgr().CanUninstall(m_localAddon);
189 bool isUpdate = (!isInstalled && CServiceBroker::GetAddonMgr().IsAddonInstalled(
190 itemAddonInfo->ID(), itemAddonInfo->Origin()));
192 bool showUpdateButton = m_localAddon &&
193 CServiceBroker::GetAddonMgr().IsAutoUpdateable(m_localAddon->ID()) &&
194 m_item->GetProperty("Addon.HasUpdate").asBoolean();
196 if (isInstalled)
198 SET_CONTROL_LABEL(CONTROL_BTN_INSTALL, 24037); // uninstall
199 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_INSTALL, canUninstall);
201 else
203 if (isUpdate)
205 SET_CONTROL_LABEL(CONTROL_BTN_INSTALL, 24138); // update
207 else
209 SET_CONTROL_LABEL(CONTROL_BTN_INSTALL, 24038); // install
212 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_INSTALL, canInstall);
213 if (canInstall && performButtonFocus == PerformButtonFocus::CHOICE_YES)
215 SET_CONTROL_FOCUS(CONTROL_BTN_INSTALL, 0);
219 if (showUpdateButton)
221 SET_CONTROL_VISIBLE(CONTROL_BTN_UPDATE);
222 SET_CONTROL_HIDDEN(CONTROL_BTN_VERSIONS);
224 else
226 SET_CONTROL_VISIBLE(CONTROL_BTN_VERSIONS);
227 SET_CONTROL_HIDDEN(CONTROL_BTN_UPDATE);
230 if (m_addonEnabled)
232 SET_CONTROL_LABEL(CONTROL_BTN_ENABLE, 24021);
233 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_ENABLE, canDisable);
235 else
237 SET_CONTROL_LABEL(CONTROL_BTN_ENABLE, 24022);
238 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_ENABLE, isInstalled);
241 bool autoUpdatesOn = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
242 CSettings::SETTING_ADDONS_AUTOUPDATES) == AUTO_UPDATES_ON;
243 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_AUTOUPDATE, isInstalled && autoUpdatesOn);
244 SET_CONTROL_SELECTED(GetID(), CONTROL_BTN_AUTOUPDATE,
245 isInstalled && autoUpdatesOn &&
246 CServiceBroker::GetAddonMgr().IsAutoUpdateable(m_localAddon->ID()));
247 SET_CONTROL_LABEL(CONTROL_BTN_AUTOUPDATE, 21340);
249 const bool active = m_localAddon && CAddonSystemSettings::GetInstance().IsActive(*m_localAddon);
250 CONTROL_ENABLE_ON_CONDITION(
251 CONTROL_BTN_SELECT,
252 m_addonEnabled && (CanShowSupportList() || CanOpen() || CanRun() || (CanUse() && !active)));
254 int label;
255 if (CanShowSupportList())
256 label = 21484;
257 else if (CanUse())
258 label = 21480;
259 else if (CanOpen())
260 label = 21478;
261 else
262 label = 21479;
263 SET_CONTROL_LABEL(CONTROL_BTN_SELECT, label);
265 const bool hasSettings = m_localAddon && m_localAddon->CanHaveAddonOrInstanceSettings();
266 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_SETTINGS, isInstalled && hasSettings);
267 if (isInstalled && hasSettings && performButtonFocus == PerformButtonFocus::CHOICE_YES)
269 SET_CONTROL_FOCUS(CONTROL_BTN_SETTINGS, 0);
272 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_DEPENDENCIES, !m_depsInstalledWithAvailable.empty());
274 CFileItemList items;
275 for (const auto& screenshot : m_item->GetAddonInfo()->Screenshots())
277 auto item = std::make_shared<CFileItem>("");
278 item->SetArt("thumb", screenshot);
279 items.Add(std::move(item));
281 CGUIMessage msg(GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST_SCREENSHOTS, 0, 0, &items);
282 OnMessage(msg);
285 static const std::string LOCAL_CACHE =
286 "\\0_local_cache"; // \0 to give it the lowest priority when sorting
288 int CGUIDialogAddonInfo::AskForVersion(std::vector<std::pair<CAddonVersion, std::string>>& versions)
290 auto dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(
291 WINDOW_DIALOG_SELECT);
292 dialog->Reset();
293 dialog->SetHeading(CVariant{21338});
294 dialog->SetUseDetails(true);
296 for (const auto& versionInfo : versions)
298 CFileItem item(StringUtils::Format(g_localizeStrings.Get(21339), versionInfo.first.asString()));
299 if (m_localAddon && m_localAddon->Version() == versionInfo.first &&
300 m_item->GetAddonInfo()->Origin() == versionInfo.second)
301 item.Select(true);
303 AddonPtr repo;
304 if (versionInfo.second == LOCAL_CACHE)
306 item.SetLabel2(g_localizeStrings.Get(24095));
307 item.SetArt("icon", "DefaultAddonRepository.png");
308 dialog->Add(item);
310 else if (CServiceBroker::GetAddonMgr().GetAddon(versionInfo.second, repo, AddonType::REPOSITORY,
311 OnlyEnabled::CHOICE_YES))
313 item.SetLabel2(repo->Name());
314 item.SetArt("icon", repo->Icon());
315 dialog->Add(item);
319 dialog->Open();
320 return dialog->IsConfirmed() ? dialog->GetSelectedItem() : -1;
323 void CGUIDialogAddonInfo::OnUpdate()
325 const auto& itemAddonInfo = m_item->GetAddonInfo();
326 const std::string& addonId = itemAddonInfo->ID();
327 const std::string& origin = m_item->GetProperty("Addon.ValidUpdateOrigin").asString();
328 const CAddonVersion& version =
329 static_cast<CAddonVersion>(m_item->GetProperty("Addon.ValidUpdateVersion").asString());
331 Close();
332 if (!m_depsInstalledWithAvailable.empty() &&
333 !ShowDependencyList(Reactivate::CHOICE_NO, EntryPoint::UPDATE))
334 return;
336 CAddonInstaller::GetInstance().Install(addonId, version, origin);
339 void CGUIDialogAddonInfo::OnSelectVersion()
341 if (!m_item->HasAddonInfo())
342 return;
344 const std::string& processAddonId = m_item->GetAddonInfo()->ID();
345 EntryPoint entryPoint = m_localAddon ? EntryPoint::UPDATE : EntryPoint::INSTALL;
347 // get all compatible versions of an addon-id regardless of their origin
348 std::vector<std::shared_ptr<IAddon>> compatibleVersions =
349 CServiceBroker::GetAddonMgr().GetCompatibleVersions(processAddonId);
351 std::vector<std::pair<CAddonVersion, std::string>> versions;
352 versions.reserve(compatibleVersions.size());
354 for (const auto& compatibleVersion : compatibleVersions)
355 versions.emplace_back(compatibleVersion->Version(), compatibleVersion->Origin());
357 CAddonDatabase database;
358 database.Open();
360 CFileItemList items;
361 if (XFILE::CDirectory::GetDirectory("special://home/addons/packages/", items, ".zip",
362 DIR_FLAG_NO_FILE_DIRS))
364 for (int i = 0; i < items.Size(); ++i)
366 std::string packageId;
367 std::string versionString;
368 if (CAddonVersion::SplitFileName(packageId, versionString, items[i]->GetLabel()))
370 if (packageId == processAddonId)
372 std::string hash;
373 std::string path(items[i]->GetPath());
374 if (database.GetPackageHash(processAddonId, items[i]->GetPath(), hash))
376 std::string sha256 = CUtil::GetFileDigest(path, KODI::UTILITY::CDigest::Type::SHA256);
378 // don't offer locally cached packages that result in an invalid version.
379 // usually this happens when the package filename gets malformed on the fs
380 // e.g. downloading "http://localhost/a+b.zip" ends up in "a b.zip"
381 const CAddonVersion version(versionString);
382 if (StringUtils::EqualsNoCase(sha256, hash) && !version.empty())
383 versions.emplace_back(version, LOCAL_CACHE);
390 if (versions.empty())
391 HELPERS::ShowOKDialogText(CVariant{21341}, CVariant{21342});
392 else
394 int i = AskForVersion(versions);
395 if (i != -1)
397 Close();
399 if (versions[i].second == LOCAL_CACHE)
401 CAddonInstaller::GetInstance().InstallFromZip(
402 StringUtils::Format("special://home/addons/packages/{}-{}.zip", processAddonId,
403 versions[i].first.asString()));
405 else
407 if (!m_depsInstalledWithAvailable.empty() &&
408 !ShowDependencyList(Reactivate::CHOICE_NO, entryPoint))
409 return;
410 CAddonInstaller::GetInstance().Install(processAddonId, versions[i].first,
411 versions[i].second);
417 void CGUIDialogAddonInfo::OnToggleAutoUpdates()
419 CGUIMessage msg(GUI_MSG_IS_SELECTED, GetID(), CONTROL_BTN_AUTOUPDATE);
420 if (OnMessage(msg))
422 bool selected = msg.GetParam1() == 1;
423 if (selected)
424 CServiceBroker::GetAddonMgr().RemoveAllUpdateRulesFromList(m_localAddon->ID());
425 else
426 CServiceBroker::GetAddonMgr().AddUpdateRuleToList(m_localAddon->ID(),
427 AddonUpdateRule::USER_DISABLED_AUTO_UPDATE);
429 bool showUpdateButton = (selected && m_item->GetProperty("Addon.HasUpdate").asBoolean());
431 if (showUpdateButton)
433 SET_CONTROL_VISIBLE(CONTROL_BTN_UPDATE);
434 SET_CONTROL_HIDDEN(CONTROL_BTN_VERSIONS);
436 else
438 SET_CONTROL_VISIBLE(CONTROL_BTN_VERSIONS);
439 SET_CONTROL_HIDDEN(CONTROL_BTN_UPDATE);
442 CServiceBroker::GetAddonMgr().PublishEventAutoUpdateStateChanged(m_localAddon->ID());
446 void CGUIDialogAddonInfo::OnInstall()
448 if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER))
449 return;
451 if (!m_item->HasAddonInfo())
452 return;
454 const auto& itemAddonInfo = m_item->GetAddonInfo();
455 const std::string& origin = itemAddonInfo->Origin();
457 if (m_localAddon && CAddonSystemSettings::GetInstance().GetAddonRepoUpdateMode() !=
458 AddonRepoUpdateMode::ANY_REPOSITORY)
460 if (m_localAddon->Origin() != origin && m_localAddon->Origin() != ORIGIN_SYSTEM)
462 const std::string& header = g_localizeStrings.Get(19098); // Warning!
463 const std::string origin =
464 !m_localAddon->Origin().empty() ? m_localAddon->Origin() : g_localizeStrings.Get(39029);
465 const std::string text =
466 StringUtils::Format(g_localizeStrings.Get(39028), m_localAddon->Name(), origin,
467 m_localAddon->Version().asString());
469 if (CGUIDialogYesNo::ShowAndGetInput(header, text))
471 m_silentUninstall = true;
472 OnUninstall();
474 else
476 return;
481 const std::string& addonId = itemAddonInfo->ID();
482 const CAddonVersion& version = itemAddonInfo->Version();
484 Close();
485 if (!m_depsInstalledWithAvailable.empty() &&
486 !ShowDependencyList(Reactivate::CHOICE_NO, EntryPoint::INSTALL))
487 return;
489 CAddonInstaller::GetInstance().Install(addonId, version, origin);
492 void CGUIDialogAddonInfo::OnSelect()
494 if (!m_localAddon)
495 return;
497 if (CanShowSupportList())
499 ShowSupportList();
500 return;
503 Close();
505 if (CanOpen() || CanRun())
506 CBuiltins::GetInstance().Execute("RunAddon(" + m_localAddon->ID() + ")");
507 else if (CanUse())
508 CAddonSystemSettings::GetInstance().SetActive(m_localAddon->Type(), m_localAddon->ID());
511 bool CGUIDialogAddonInfo::CanOpen() const
513 return m_localAddon && m_localAddon->Type() == AddonType::PLUGIN;
516 bool CGUIDialogAddonInfo::CanRun() const
518 if (m_localAddon)
520 if (m_localAddon->Type() == AddonType::SCRIPT)
521 return true;
523 if (GAME::CGameUtils::IsStandaloneGame(m_localAddon))
524 return true;
527 return false;
530 bool CGUIDialogAddonInfo::CanUse() const
532 return m_localAddon && (m_localAddon->Type() == AddonType::SKIN ||
533 m_localAddon->Type() == AddonType::SCREENSAVER ||
534 m_localAddon->Type() == AddonType::VISUALIZATION ||
535 m_localAddon->Type() == AddonType::SCRIPT_WEATHER ||
536 m_localAddon->Type() == AddonType::RESOURCE_LANGUAGE ||
537 m_localAddon->Type() == AddonType::RESOURCE_UISOUNDS ||
538 m_localAddon->Type() == AddonType::AUDIOENCODER);
541 bool CGUIDialogAddonInfo::CanShowSupportList() const
543 return m_localAddon && (m_localAddon->Type() == AddonType::AUDIODECODER ||
544 m_localAddon->Type() == AddonType::IMAGEDECODER);
547 bool CGUIDialogAddonInfo::PromptIfDependency(int heading, int line2)
549 if (!m_localAddon)
550 return false;
552 VECADDONS addons;
553 std::vector<std::string> deps;
554 CServiceBroker::GetAddonMgr().GetAddons(addons);
555 for (VECADDONS::const_iterator it = addons.begin(); it != addons.end(); ++it)
557 auto i =
558 std::find_if((*it)->GetDependencies().begin(), (*it)->GetDependencies().end(),
559 [&](const DependencyInfo& other) { return other.id == m_localAddon->ID(); });
560 if (i != (*it)->GetDependencies().end() && !i->optional) // non-optional dependency
561 deps.push_back((*it)->Name());
564 if (!deps.empty())
566 std::string line0 = StringUtils::Format(g_localizeStrings.Get(24046), m_localAddon->Name());
567 std::string line1 = StringUtils::Join(deps, ", ");
568 HELPERS::ShowOKDialogLines(CVariant{heading}, CVariant{std::move(line0)},
569 CVariant{std::move(line1)}, CVariant{line2});
570 return true;
572 return false;
575 void CGUIDialogAddonInfo::OnUninstall()
577 if (!m_localAddon.get())
578 return;
580 if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER))
581 return;
583 // ensure the addon is not a dependency of other installed addons
584 if (PromptIfDependency(24037, 24047))
585 return;
587 // prompt user to be sure
588 if (!m_silentUninstall && !CGUIDialogYesNo::ShowAndGetInput(CVariant{24037}, CVariant{750}))
589 return;
591 bool removeData = false;
592 if (CDirectory::Exists(m_localAddon->Profile()))
593 removeData = CGUIDialogYesNo::ShowAndGetInput(CVariant{24037}, CVariant{39014});
595 CAddonInstaller::GetInstance().UnInstall(m_localAddon, removeData);
597 Close();
600 void CGUIDialogAddonInfo::OnEnableDisable()
602 if (!m_localAddon)
603 return;
605 if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER))
606 return;
608 if (m_addonEnabled)
610 if (PromptIfDependency(24075, 24091))
611 return; //required. can't disable
613 CServiceBroker::GetAddonMgr().DisableAddon(m_localAddon->ID(), AddonDisabledReason::USER);
615 else
617 // Check user want to enable if lifecycle not normal
618 if (!ADDON::GUI::CHelpers::DialogAddonLifecycleUseAsk(m_localAddon))
619 return;
621 CServiceBroker::GetAddonMgr().EnableAddon(m_localAddon->ID());
624 UpdateControls(PerformButtonFocus::CHOICE_NO);
627 void CGUIDialogAddonInfo::OnSettings()
629 CGUIDialogAddonSettings::ShowForAddon(m_localAddon);
632 bool CGUIDialogAddonInfo::ShowDependencyList(Reactivate reactivate, EntryPoint entryPoint)
634 if (entryPoint != EntryPoint::INSTALL || m_showDepDialogOnInstall)
636 auto pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(
637 WINDOW_DIALOG_SELECT);
638 CFileItemList items;
640 for (const auto& it : m_depsInstalledWithAvailable)
642 // All combinations of depAddon and localAddon validity are possible and information
643 // must be displayed even when there is no depAddon.
644 // info_addon is the add-on to take the information to display (name, icon) from. The
645 // version in the repository is preferred because it might contain more recent data.
647 std::shared_ptr<IAddon> infoAddon = it.m_available ? it.m_available : it.m_installed;
649 if (infoAddon)
651 if (entryPoint != EntryPoint::UPDATE || !it.IsInstalledUpToDate())
653 const CFileItemPtr item = std::make_shared<CFileItem>(infoAddon->Name());
654 int messageId = 24180; // minversion only
656 // dep not installed locally, but it is available from a repo!
657 // make sure only non-optional add-ons that meet versionMin are
658 // announced for installation
660 if (!it.m_installed)
662 if (entryPoint != EntryPoint::SHOW_DEPENDENCIES && !it.m_depInfo.optional)
664 if (it.m_depInfo.versionMin <= it.m_available->Version())
666 messageId = 24181; // => install
668 else
670 messageId = 24185; // => not available, only lower versions available in the repos
674 else // dep is installed locally
676 messageId = 24182; // => installed
678 if (!it.IsInstalledUpToDate())
680 messageId = 24183; // => update to
684 if (entryPoint == EntryPoint::SHOW_DEPENDENCIES ||
685 infoAddon->MainType() != AddonType::SCRIPT_MODULE ||
686 !CAddonRepos::IsFromOfficialRepo(infoAddon, CheckAddonPath::CHOICE_NO))
688 item->SetLabel2(StringUtils::Format(
689 g_localizeStrings.Get(messageId), it.m_depInfo.versionMin.asString(),
690 it.m_installed ? it.m_installed->Version().asString() : "",
691 it.m_available ? it.m_available->Version().asString() : "",
692 it.m_depInfo.optional ? g_localizeStrings.Get(24184) : ""));
694 item->SetArt("icon", infoAddon->Icon());
695 item->SetProperty("addon_id", it.m_depInfo.id);
696 items.Add(item);
700 else
702 const CFileItemPtr item = std::make_shared<CFileItem>(it.m_depInfo.id);
703 item->SetLabel2(g_localizeStrings.Get(10005)); // Not available
704 items.Add(item);
708 if (!items.IsEmpty())
710 CFileItemPtr backup_item = GetCurrentListItem();
711 while (true)
713 pDialog->Reset();
714 pDialog->SetHeading(reactivate == Reactivate::CHOICE_YES ? 39024 : 39020);
715 pDialog->SetUseDetails(true);
716 for (auto& it : items)
717 pDialog->Add(*it);
718 pDialog->EnableButton(reactivate == Reactivate::CHOICE_NO, 186);
719 pDialog->SetButtonFocus(true);
720 pDialog->Open();
722 if (pDialog->IsButtonPressed())
723 return true;
725 if (pDialog->IsConfirmed())
727 const CFileItemPtr& item = pDialog->GetSelectedFileItem();
728 std::string addon_id = item->GetProperty("addon_id").asString();
729 std::shared_ptr<IAddon> depAddon;
730 if (CServiceBroker::GetAddonMgr().FindInstallableById(addon_id, depAddon))
732 Close();
733 ShowForItem(std::make_shared<CFileItem>(depAddon));
736 else
737 break;
739 SetItem(backup_item);
740 if (reactivate == Reactivate::CHOICE_YES)
741 Open();
743 return false;
747 return true;
750 void CGUIDialogAddonInfo::ShowSupportList()
752 std::vector<KODI::ADDONS::AddonSupportEntry> list;
753 if (CanShowSupportList())
754 list =
755 CServiceBroker::GetExtsMimeSupportList().GetSupportedExtsAndMimeTypes(m_localAddon->ID());
757 auto pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(
758 WINDOW_DIALOG_SELECT);
759 CFileItemList items;
760 for (const auto& entry : list)
762 // Ignore included extension about track support
763 if (StringUtils::EndsWith(entry.m_name, KODI_ADDON_AUDIODECODER_TRACK_EXT))
764 continue;
766 std::string label;
767 if (entry.m_type == AddonSupportType::Extension)
768 label = StringUtils::Format(g_localizeStrings.Get(21346), entry.m_name);
769 else if (entry.m_type == AddonSupportType::Mimetype)
770 label = StringUtils::Format(g_localizeStrings.Get(21347), entry.m_name);
771 else
772 label = entry.m_name;
774 const CFileItemPtr item = std::make_shared<CFileItem>(label);
775 item->SetLabel2(entry.m_description);
776 if (!entry.m_icon.empty())
777 item->SetArt("icon", entry.m_icon);
778 else if (entry.m_type == AddonSupportType::Extension)
779 item->SetArt("icon", "DefaultExtensionInfo.png");
780 else if (entry.m_type == AddonSupportType::Mimetype)
781 item->SetArt("icon", "DefaultMimetypeInfo.png");
782 item->SetProperty("addon_id", m_localAddon->ID());
783 items.Add(item);
786 pDialog->Reset();
787 pDialog->SetHeading(21485);
788 pDialog->SetUseDetails(true);
789 for (auto& it : items)
790 pDialog->Add(*it);
791 pDialog->SetButtonFocus(true);
792 pDialog->Open();
795 bool CGUIDialogAddonInfo::ShowForItem(const CFileItemPtr& item)
797 if (!item)
798 return false;
800 CGUIDialogAddonInfo* dialog =
801 CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogAddonInfo>(
802 WINDOW_DIALOG_ADDON_INFO);
803 if (!dialog)
804 return false;
805 if (!dialog->SetItem(item))
806 return false;
808 dialog->Open();
809 return true;
812 bool CGUIDialogAddonInfo::SetItem(const CFileItemPtr& item)
814 if (!item || !item->HasAddonInfo())
815 return false;
817 m_item = std::make_shared<CFileItem>(*item);
818 m_localAddon.reset();
819 if (CServiceBroker::GetAddonMgr().GetAddon(item->GetAddonInfo()->ID(), m_localAddon,
820 OnlyEnabled::CHOICE_NO))
822 CLog::Log(LOGDEBUG, "{} - Addon with id {} not found locally.", __FUNCTION__,
823 item->GetAddonInfo()->ID());
825 return true;
828 void CGUIDialogAddonInfo::BuildDependencyList()
830 if (!m_item)
831 return;
833 m_showDepDialogOnInstall = false;
834 m_depsInstalledWithAvailable.clear();
835 m_deps = CServiceBroker::GetAddonMgr().GetDepsRecursive(m_item->GetAddonInfo()->ID(),
836 OnlyEnabledRootAddon::CHOICE_NO);
838 for (const auto& dep : m_deps)
840 std::shared_ptr<IAddon> addonInstalled;
841 std::shared_ptr<IAddon> addonAvailable;
843 // Find add-on in local installation
844 if (!CServiceBroker::GetAddonMgr().GetAddon(dep.id, addonInstalled, OnlyEnabled::CHOICE_YES))
846 addonInstalled = nullptr;
849 // Find add-on in repositories
850 if (!CServiceBroker::GetAddonMgr().FindInstallableById(dep.id, addonAvailable))
852 addonAvailable = nullptr;
855 if (!addonInstalled)
858 // after pushing the install button the dependency install dialog
859 // will be opened only if...
860 // - dependencies are unavailable (for informational purposes) OR
861 // - the dependency is not a script/module OR
862 // - the script/module is not available at an official repo
863 if (!addonAvailable || addonAvailable->MainType() != AddonType::SCRIPT_MODULE ||
864 !CAddonRepos::IsFromOfficialRepo(addonAvailable, CheckAddonPath::CHOICE_NO))
866 m_showDepDialogOnInstall = true;
869 else
872 // only display dialog if updates for already installed dependencies will install
873 if (addonAvailable && addonAvailable->Version() > addonInstalled->Version())
875 m_showDepDialogOnInstall = true;
879 m_depsInstalledWithAvailable.emplace_back(dep, addonInstalled, addonAvailable);
882 std::sort(m_depsInstalledWithAvailable.begin(), m_depsInstalledWithAvailable.end(),
883 [](const auto& a, const auto& b) {
884 // 1. "not installed/available" go to the bottom first
885 const bool depAInstalledOrAvailable =
886 a.m_installed != nullptr || a.m_available != nullptr;
887 const bool depBInstalledOrAvailable =
888 b.m_installed != nullptr || b.m_available != nullptr;
890 if (depAInstalledOrAvailable != depBInstalledOrAvailable)
892 return !depAInstalledOrAvailable;
895 // 2. then optional add-ons to top
896 if (a.m_depInfo.optional != b.m_depInfo.optional)
898 return a.m_depInfo.optional;
901 // 3. addon type asc, except scripts/modules at the bottom
902 const std::shared_ptr<IAddon>& depA = a.m_installed ? a.m_installed : a.m_available;
903 const std::shared_ptr<IAddon>& depB = b.m_installed ? b.m_installed : b.m_available;
905 if (depA && depB)
907 const AddonType typeA = depA->MainType();
908 const AddonType typeB = depB->MainType();
909 if (typeA != typeB)
911 if ((typeA == AddonType::SCRIPT_MODULE) == (typeB == AddonType::SCRIPT_MODULE))
913 // both are scripts/modules or neither one is => sort by addon type asc
914 return typeA < typeB;
916 else
918 // At this point, either:
919 // A is script/module and B is not, or A is not script/module and B is.
920 // the script/module goes to the bottom
921 return typeA != AddonType::SCRIPT_MODULE;
926 // 4. finally order by addon-id
927 return a.m_depInfo.id < b.m_depInfo.id;
931 bool CInstalledWithAvailable::IsInstalledUpToDate() const
933 if (m_installed)
935 if (!m_available || m_available->Version() == m_installed->Version())
937 return true;
941 return false;