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.
9 #include "GUIDialogAddonInfo.h"
12 #include "FileItemList.h"
13 #include "GUIPassword.h"
14 #include "ServiceBroker.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"
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
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())
84 int iControl
= message
.GetSenderId();
85 if (iControl
== CONTROL_BTN_UPDATE
)
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()))
101 m_silentUninstall
= false;
106 else if (iControl
== CONTROL_BTN_SELECT
)
111 else if (iControl
== CONTROL_BTN_ENABLE
)
116 else if (iControl
== CONTROL_BTN_SETTINGS
)
121 else if (iControl
== CONTROL_BTN_DEPENDENCIES
)
123 ShowDependencyList(Reactivate::CHOICE_YES
, EntryPoint::SHOW_DEPENDENCIES
);
126 else if (iControl
== CONTROL_BTN_AUTOUPDATE
)
128 OnToggleAutoUpdates();
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
);
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
)
154 return CGUIDialog::OnMessage(message
);
157 bool CGUIDialogAddonInfo::OnAction(const CAction
& action
)
159 if (action
.GetID() == ACTION_SHOW_INFO
)
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
)
179 const auto& itemAddonInfo
= m_item
->GetAddonInfo();
180 bool isInstalled
= CServiceBroker::GetAddonMgr().IsAddonInstalled(
181 itemAddonInfo
->ID(), itemAddonInfo
->Origin(), itemAddonInfo
->Version());
183 m_localAddon
&& !CServiceBroker::GetAddonMgr().IsAddonDisabled(m_localAddon
->ID());
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();
198 SET_CONTROL_LABEL(CONTROL_BTN_INSTALL
, 24037); // uninstall
199 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_INSTALL
, canUninstall
);
205 SET_CONTROL_LABEL(CONTROL_BTN_INSTALL
, 24138); // update
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
);
226 SET_CONTROL_VISIBLE(CONTROL_BTN_VERSIONS
);
227 SET_CONTROL_HIDDEN(CONTROL_BTN_UPDATE
);
232 SET_CONTROL_LABEL(CONTROL_BTN_ENABLE
, 24021);
233 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_ENABLE
, canDisable
);
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(
252 m_addonEnabled
&& (CanShowSupportList() || CanOpen() || CanRun() || (CanUse() && !active
)));
255 if (CanShowSupportList())
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());
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
);
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
);
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
)
304 if (versionInfo
.second
== LOCAL_CACHE
)
306 item
.SetLabel2(g_localizeStrings
.Get(24095));
307 item
.SetArt("icon", "DefaultAddonRepository.png");
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());
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());
332 if (!m_depsInstalledWithAvailable
.empty() &&
333 !ShowDependencyList(Reactivate::CHOICE_NO
, EntryPoint::UPDATE
))
336 CAddonInstaller::GetInstance().Install(addonId
, version
, origin
);
339 void CGUIDialogAddonInfo::OnSelectVersion()
341 if (!m_item
->HasAddonInfo())
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
;
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
)
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});
394 int i
= AskForVersion(versions
);
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()));
407 if (!m_depsInstalledWithAvailable
.empty() &&
408 !ShowDependencyList(Reactivate::CHOICE_NO
, entryPoint
))
410 CAddonInstaller::GetInstance().Install(processAddonId
, versions
[i
].first
,
417 void CGUIDialogAddonInfo::OnToggleAutoUpdates()
419 CGUIMessage
msg(GUI_MSG_IS_SELECTED
, GetID(), CONTROL_BTN_AUTOUPDATE
);
422 bool selected
= msg
.GetParam1() == 1;
424 CServiceBroker::GetAddonMgr().RemoveAllUpdateRulesFromList(m_localAddon
->ID());
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
);
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
))
451 if (!m_item
->HasAddonInfo())
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;
481 const std::string
& addonId
= itemAddonInfo
->ID();
482 const CAddonVersion
& version
= itemAddonInfo
->Version();
485 if (!m_depsInstalledWithAvailable
.empty() &&
486 !ShowDependencyList(Reactivate::CHOICE_NO
, EntryPoint::INSTALL
))
489 CAddonInstaller::GetInstance().Install(addonId
, version
, origin
);
492 void CGUIDialogAddonInfo::OnSelect()
497 if (CanShowSupportList())
505 if (CanOpen() || CanRun())
506 CBuiltins::GetInstance().Execute("RunAddon(" + m_localAddon
->ID() + ")");
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
520 if (m_localAddon
->Type() == AddonType::SCRIPT
)
523 if (GAME::CGameUtils::IsStandaloneGame(m_localAddon
))
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
)
553 std::vector
<std::string
> deps
;
554 CServiceBroker::GetAddonMgr().GetAddons(addons
);
555 for (VECADDONS::const_iterator it
= addons
.begin(); it
!= addons
.end(); ++it
)
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());
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
});
575 void CGUIDialogAddonInfo::OnUninstall()
577 if (!m_localAddon
.get())
580 if (!g_passwordManager
.CheckMenuLock(WINDOW_ADDON_BROWSER
))
583 // ensure the addon is not a dependency of other installed addons
584 if (PromptIfDependency(24037, 24047))
587 // prompt user to be sure
588 if (!m_silentUninstall
&& !CGUIDialogYesNo::ShowAndGetInput(CVariant
{24037}, CVariant
{750}))
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
);
600 void CGUIDialogAddonInfo::OnEnableDisable()
605 if (!g_passwordManager
.CheckMenuLock(WINDOW_ADDON_BROWSER
))
610 if (PromptIfDependency(24075, 24091))
611 return; //required. can't disable
613 CServiceBroker::GetAddonMgr().DisableAddon(m_localAddon
->ID(), AddonDisabledReason::USER
);
617 // Check user want to enable if lifecycle not normal
618 if (!ADDON::GUI::CHelpers::DialogAddonLifecycleUseAsk(m_localAddon
))
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
);
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
;
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
662 if (entryPoint
!= EntryPoint::SHOW_DEPENDENCIES
&& !it
.m_depInfo
.optional
)
664 if (it
.m_depInfo
.versionMin
<= it
.m_available
->Version())
666 messageId
= 24181; // => install
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
);
702 const CFileItemPtr item
= std::make_shared
<CFileItem
>(it
.m_depInfo
.id
);
703 item
->SetLabel2(g_localizeStrings
.Get(10005)); // Not available
708 if (!items
.IsEmpty())
710 CFileItemPtr backup_item
= GetCurrentListItem();
714 pDialog
->SetHeading(reactivate
== Reactivate::CHOICE_YES
? 39024 : 39020);
715 pDialog
->SetUseDetails(true);
716 for (auto& it
: items
)
718 pDialog
->EnableButton(reactivate
== Reactivate::CHOICE_NO
, 186);
719 pDialog
->SetButtonFocus(true);
722 if (pDialog
->IsButtonPressed())
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
))
733 ShowForItem(std::make_shared
<CFileItem
>(depAddon
));
739 SetItem(backup_item
);
740 if (reactivate
== Reactivate::CHOICE_YES
)
750 void CGUIDialogAddonInfo::ShowSupportList()
752 std::vector
<KODI::ADDONS::AddonSupportEntry
> list
;
753 if (CanShowSupportList())
755 CServiceBroker::GetExtsMimeSupportList().GetSupportedExtsAndMimeTypes(m_localAddon
->ID());
757 auto pDialog
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogSelect
>(
758 WINDOW_DIALOG_SELECT
);
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
))
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
);
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());
787 pDialog
->SetHeading(21485);
788 pDialog
->SetUseDetails(true);
789 for (auto& it
: items
)
791 pDialog
->SetButtonFocus(true);
795 bool CGUIDialogAddonInfo::ShowForItem(const CFileItemPtr
& item
)
800 CGUIDialogAddonInfo
* dialog
=
801 CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogAddonInfo
>(
802 WINDOW_DIALOG_ADDON_INFO
);
805 if (!dialog
->SetItem(item
))
812 bool CGUIDialogAddonInfo::SetItem(const CFileItemPtr
& item
)
814 if (!item
|| !item
->HasAddonInfo())
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());
828 void CGUIDialogAddonInfo::BuildDependencyList()
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;
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;
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
;
907 const AddonType typeA
= depA
->MainType();
908 const AddonType typeB
= depB
->MainType();
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
;
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
935 if (!m_available
|| m_available
->Version() == m_installed
->Version())