diff options
Diffstat (limited to 'xbmc/addons/gui/GUIWindowAddonBrowser.cpp')
-rw-r--r-- | xbmc/addons/gui/GUIWindowAddonBrowser.cpp | 644 |
1 files changed, 644 insertions, 0 deletions
diff --git a/xbmc/addons/gui/GUIWindowAddonBrowser.cpp b/xbmc/addons/gui/GUIWindowAddonBrowser.cpp new file mode 100644 index 0000000..2f522e7 --- /dev/null +++ b/xbmc/addons/gui/GUIWindowAddonBrowser.cpp @@ -0,0 +1,644 @@ +/* + * 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 "GUIWindowAddonBrowser.h" + +#include "ContextMenuManager.h" +#include "FileItem.h" +#include "GUIDialogAddonInfo.h" +#include "GUIUserMessages.h" +#include "LangInfo.h" +#include "ServiceBroker.h" +#include "URL.h" +#include "addons/AddonInstaller.h" +#include "addons/AddonManager.h" +#include "addons/AddonSystemSettings.h" +#include "addons/IAddon.h" +#include "addons/RepositoryUpdater.h" +#include "addons/addoninfo/AddonType.h" +#include "dialogs/GUIDialogBusy.h" +#include "dialogs/GUIDialogFileBrowser.h" +#include "dialogs/GUIDialogSelect.h" +#include "dialogs/GUIDialogYesNo.h" +#include "filesystem/AddonsDirectory.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "input/Key.h" +#include "messaging/helpers/DialogHelper.h" +#include "platform/Platform.h" +#include "settings/MediaSourceSettings.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" +#include "storage/MediaManager.h" +#include "threads/IRunnable.h" +#include "utils/StringUtils.h" +#include "utils/Variant.h" + +#include <utility> + +#define CONTROL_SETTINGS 5 +#define CONTROL_FOREIGNFILTER 7 +#define CONTROL_BROKENFILTER 8 +#define CONTROL_CHECK_FOR_UPDATES 9 + +using namespace ADDON; +using namespace XFILE; + +CGUIWindowAddonBrowser::CGUIWindowAddonBrowser(void) + : CGUIMediaWindow(WINDOW_ADDON_BROWSER, "AddonBrowser.xml") +{ +} + +CGUIWindowAddonBrowser::~CGUIWindowAddonBrowser() = default; + +bool CGUIWindowAddonBrowser::OnMessage(CGUIMessage& message) +{ + switch (message.GetMessage()) + { + case GUI_MSG_WINDOW_DEINIT: + { + CServiceBroker::GetRepositoryUpdater().Events().Unsubscribe(this); + CServiceBroker::GetAddonMgr().Events().Unsubscribe(this); + + if (m_thumbLoader.IsLoading()) + m_thumbLoader.StopThread(); + } + break; + case GUI_MSG_WINDOW_INIT: + { + CServiceBroker::GetRepositoryUpdater().Events().Subscribe(this, + &CGUIWindowAddonBrowser::OnEvent); + CServiceBroker::GetAddonMgr().Events().Subscribe(this, &CGUIWindowAddonBrowser::OnEvent); + + SetProperties(); + } + break; + case GUI_MSG_CLICKED: + { + int iControl = message.GetSenderId(); + if (iControl == CONTROL_FOREIGNFILTER) + { + const std::shared_ptr<CSettings> settings = + CServiceBroker::GetSettingsComponent()->GetSettings(); + settings->ToggleBool(CSettings::SETTING_GENERAL_ADDONFOREIGNFILTER); + settings->Save(); + Refresh(); + return true; + } + else if (iControl == CONTROL_BROKENFILTER) + { + const std::shared_ptr<CSettings> settings = + CServiceBroker::GetSettingsComponent()->GetSettings(); + settings->ToggleBool(CSettings::SETTING_GENERAL_ADDONBROKENFILTER); + settings->Save(); + Refresh(); + return true; + } + else if (iControl == CONTROL_CHECK_FOR_UPDATES) + { + CServiceBroker::GetRepositoryUpdater().CheckForUpdates(true); + return true; + } + else if (iControl == CONTROL_SETTINGS) + { + CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SETTINGS_SYSTEM, + "addons"); + return true; + } + else if (m_viewControl.HasControl(iControl)) // list/thumb control + { + // get selected item + int iItem = m_viewControl.GetSelectedItem(); + int iAction = message.GetParam1(); + + // iItem is checked for validity inside these routines + if (iAction == ACTION_SHOW_INFO) + { + if (!m_vecItems->Get(iItem)->GetProperty("Addon.ID").empty()) + return CGUIDialogAddonInfo::ShowForItem((*m_vecItems)[iItem]); + return false; + } + } + } + break; + case GUI_MSG_NOTIFY_ALL: + { + if (message.GetParam1() == GUI_MSG_UPDATE_ITEM && IsActive() && + message.GetNumStringParams() == 1) + { // update this item + for (int i = 0; i < m_vecItems->Size(); ++i) + { + CFileItemPtr item = m_vecItems->Get(i); + if (item->GetProperty("Addon.ID") == message.GetStringParam()) + { + UpdateStatus(item); + FormatAndSort(*m_vecItems); + return true; + } + } + } + else if (message.GetParam1() == GUI_MSG_UPDATE && IsActive()) + SetProperties(); + } + break; + default: + break; + } + return CGUIMediaWindow::OnMessage(message); +} + +void CGUIWindowAddonBrowser::SetProperties() +{ + auto lastUpdated = CServiceBroker::GetRepositoryUpdater().LastUpdated(); + SetProperty("Updated", lastUpdated.IsValid() ? lastUpdated.GetAsLocalizedDateTime() + : g_localizeStrings.Get(21337)); +} + +class UpdateAddons : public IRunnable +{ + void Run() override + { + for (const auto& addon : CServiceBroker::GetAddonMgr().GetAvailableUpdates()) + CAddonInstaller::GetInstance().InstallOrUpdate(addon->ID(), BackgroundJob::CHOICE_YES, + ModalJob::CHOICE_NO); + } +}; + +class UpdateAllowedAddons : public IRunnable +{ + void Run() override + { + for (const auto& addon : CServiceBroker::GetAddonMgr().GetAvailableUpdates()) + if (CServiceBroker::GetAddonMgr().IsAutoUpdateable(addon->ID())) + CAddonInstaller::GetInstance().InstallOrUpdate(addon->ID(), BackgroundJob::CHOICE_YES, + ModalJob::CHOICE_NO); + } +}; + +void CGUIWindowAddonBrowser::OnEvent(const ADDON::CRepositoryUpdater::RepositoryUpdated& event) +{ + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE); + CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg); +} + +void CGUIWindowAddonBrowser::OnEvent(const ADDON::AddonEvent& event) +{ + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE); + CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg); +} + +void CGUIWindowAddonBrowser::InstallFromZip() +{ + using namespace KODI::MESSAGING::HELPERS; + + if (!CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool( + CSettings::SETTING_ADDONS_ALLOW_UNKNOWN_SOURCES)) + { + if (ShowYesNoDialogText(13106, 36617, 186, 10004) == DialogResponse::CHOICE_YES) + CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow( + WINDOW_SETTINGS_SYSTEM, CSettings::SETTING_ADDONS_ALLOW_UNKNOWN_SOURCES); + } + else + { + // pop up filebrowser to grab an installed folder + VECSOURCES shares = *CMediaSourceSettings::GetInstance().GetSources("files"); + CServiceBroker::GetMediaManager().GetLocalDrives(shares); + CServiceBroker::GetMediaManager().GetNetworkLocations(shares); + std::string path; + if (CGUIDialogFileBrowser::ShowAndGetFile(shares, "*.zip", g_localizeStrings.Get(24041), path)) + { + CAddonInstaller::GetInstance().InstallFromZip(path); + } + } +} + +bool CGUIWindowAddonBrowser::OnClick(int iItem, const std::string& player) +{ + CFileItemPtr item = m_vecItems->Get(iItem); + if (item->GetPath() == "addons://install/") + { + InstallFromZip(); + return true; + } + if (item->GetPath() == "addons://update_all/") + { + UpdateAddons updater; + CGUIDialogBusy::Wait(&updater, 100, true); + return true; + } + if (item->GetPath() == "addons://update_allowed/") + { + UpdateAllowedAddons updater; + CGUIDialogBusy::Wait(&updater, 100, true); + return true; + } + if (!item->m_bIsFolder) + { + // cancel a downloading job + if (item->HasProperty("Addon.Downloading")) + { + if (CGUIDialogYesNo::ShowAndGetInput(CVariant{24000}, item->GetProperty("Addon.Name"), + CVariant{24066}, CVariant{""})) + { + if (CAddonInstaller::GetInstance().Cancel(item->GetProperty("Addon.ID").asString())) + Refresh(); + } + return true; + } + + CGUIDialogAddonInfo::ShowForItem(item); + return true; + } + if (item->IsPath("addons://search/")) + { + Update(item->GetPath()); + return true; + } + + return CGUIMediaWindow::OnClick(iItem, player); +} + +void CGUIWindowAddonBrowser::UpdateButtons() +{ + const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings(); + SET_CONTROL_SELECTED(GetID(), CONTROL_FOREIGNFILTER, + settings->GetBool(CSettings::SETTING_GENERAL_ADDONFOREIGNFILTER)); + SET_CONTROL_SELECTED(GetID(), CONTROL_BROKENFILTER, + settings->GetBool(CSettings::SETTING_GENERAL_ADDONBROKENFILTER)); + CONTROL_ENABLE(CONTROL_CHECK_FOR_UPDATES); + CONTROL_ENABLE(CONTROL_SETTINGS); + + bool allowFilter = CAddonsDirectory::IsRepoDirectory(CURL(m_vecItems->GetPath())); + CONTROL_ENABLE_ON_CONDITION(CONTROL_FOREIGNFILTER, allowFilter); + CONTROL_ENABLE_ON_CONDITION(CONTROL_BROKENFILTER, allowFilter); + + CGUIMediaWindow::UpdateButtons(); +} + +static bool IsForeign(const std::string& languages) +{ + if (languages.empty()) + return false; + + for (const auto& lang : StringUtils::Split(languages, " ")) + { + if (lang == "en" || lang == g_langInfo.GetLocale().GetLanguageCode() || + lang == g_langInfo.GetLocale().ToShortString()) + return false; + + // for backwards compatibility + if (lang == "no" && g_langInfo.GetLocale().ToShortString() == "nb_NO") + return false; + } + return true; +} + +bool CGUIWindowAddonBrowser::GetDirectory(const std::string& strDirectory, CFileItemList& items) +{ + bool result = CGUIMediaWindow::GetDirectory(strDirectory, items); + + if (result && CAddonsDirectory::IsRepoDirectory(CURL(strDirectory))) + { + const std::shared_ptr<CSettings> settings = + CServiceBroker::GetSettingsComponent()->GetSettings(); + if (settings->GetBool(CSettings::SETTING_GENERAL_ADDONFOREIGNFILTER)) + { + int i = 0; + while (i < items.Size()) + { + auto prop = items[i]->GetProperty("Addon.Language"); + if (!prop.isNull() && IsForeign(prop.asString())) + items.Remove(i); + else + ++i; + } + } + if (settings->GetBool(CSettings::SETTING_GENERAL_ADDONBROKENFILTER)) + { + for (int i = items.Size() - 1; i >= 0; i--) + { + if (items[i]->GetAddonInfo() && + items[i]->GetAddonInfo()->LifecycleState() == AddonLifecycleState::BROKEN) + { + //check if it's installed + AddonPtr addon; + if (!CServiceBroker::GetAddonMgr().GetAddon(items[i]->GetProperty("Addon.ID").asString(), + addon, OnlyEnabled::CHOICE_YES)) + items.Remove(i); + } + } + } + } + + for (int i = 0; i < items.Size(); ++i) + UpdateStatus(items[i]); + + return result; +} + +void CGUIWindowAddonBrowser::UpdateStatus(const CFileItemPtr& item) +{ + if (!item || item->m_bIsFolder) + return; + + unsigned int percent; + bool downloadFinshed; + if (CAddonInstaller::GetInstance().GetProgress(item->GetProperty("Addon.ID").asString(), percent, + downloadFinshed)) + { + std::string progress = StringUtils::Format( + !downloadFinshed ? g_localizeStrings.Get(24042) : g_localizeStrings.Get(24044), percent); + item->SetProperty("Addon.Status", progress); + item->SetProperty("Addon.Downloading", true); + } + else + item->ClearProperty("Addon.Downloading"); +} + +bool CGUIWindowAddonBrowser::Update(const std::string& strDirectory, + bool updateFilterPath /* = true */) +{ + if (m_thumbLoader.IsLoading()) + m_thumbLoader.StopThread(); + + if (!CGUIMediaWindow::Update(strDirectory, updateFilterPath)) + return false; + + m_thumbLoader.Load(*m_vecItems); + + return true; +} + +int CGUIWindowAddonBrowser::SelectAddonID(AddonType type, + std::string& addonID, + bool showNone /* = false */, + bool showDetails /* = true */, + bool showInstalled /* = true */, + bool showInstallable /*= false */, + bool showMore /* = true */) +{ + std::vector<AddonType> types; + types.push_back(type); + return SelectAddonID(types, addonID, showNone, showDetails, showInstalled, showInstallable, + showMore); +} + +int CGUIWindowAddonBrowser::SelectAddonID(AddonType type, + std::vector<std::string>& addonIDs, + bool showNone /* = false */, + bool showDetails /* = true */, + bool multipleSelection /* = true */, + bool showInstalled /* = true */, + bool showInstallable /* = false */, + bool showMore /* = true */) +{ + std::vector<AddonType> types; + types.push_back(type); + return SelectAddonID(types, addonIDs, showNone, showDetails, multipleSelection, showInstalled, + showInstallable, showMore); +} + +int CGUIWindowAddonBrowser::SelectAddonID(const std::vector<AddonType>& types, + std::string& addonID, + bool showNone /* = false */, + bool showDetails /* = true */, + bool showInstalled /* = true */, + bool showInstallable /* = false */, + bool showMore /* = true */) +{ + std::vector<std::string> addonIDs; + if (!addonID.empty()) + addonIDs.push_back(addonID); + int retval = SelectAddonID(types, addonIDs, showNone, showDetails, false, showInstalled, + showInstallable, showMore); + if (!addonIDs.empty()) + addonID = addonIDs.at(0); + else + addonID = ""; + return retval; +} + +int CGUIWindowAddonBrowser::SelectAddonID(const std::vector<AddonType>& types, + std::vector<std::string>& addonIDs, + bool showNone /* = false */, + bool showDetails /* = true */, + bool multipleSelection /* = true */, + bool showInstalled /* = true */, + bool showInstallable /* = false */, + bool showMore /* = true */) +{ + // if we shouldn't show neither installed nor installable addons the list will be empty + if (!showInstalled && !showInstallable) + return -1; + + // can't show the "Get More" button if we already show installable addons + if (showInstallable) + showMore = false; + + CGUIDialogSelect* dialog = + CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>( + WINDOW_DIALOG_SELECT); + if (!dialog) + return -1; + + // get rid of any invalid addon types + std::vector<AddonType> validTypes(types.size()); + std::copy_if(types.begin(), types.end(), validTypes.begin(), + [](AddonType type) { return type != AddonType::UNKNOWN; }); + + if (validTypes.empty()) + return -1; + + // get all addons to show + VECADDONS addons; + if (showInstalled) + { + for (std::vector<AddonType>::const_iterator type = validTypes.begin(); type != validTypes.end(); + ++type) + { + VECADDONS typeAddons; + if (*type == AddonType::AUDIO) + CAddonsDirectory::GetScriptsAndPlugins("audio", typeAddons); + else if (*type == AddonType::EXECUTABLE) + CAddonsDirectory::GetScriptsAndPlugins("executable", typeAddons); + else if (*type == AddonType::IMAGE) + CAddonsDirectory::GetScriptsAndPlugins("image", typeAddons); + else if (*type == AddonType::VIDEO) + CAddonsDirectory::GetScriptsAndPlugins("video", typeAddons); + else if (*type == AddonType::GAME) + CAddonsDirectory::GetScriptsAndPlugins("game", typeAddons); + else + CServiceBroker::GetAddonMgr().GetAddons(typeAddons, *type); + + addons.insert(addons.end(), typeAddons.begin(), typeAddons.end()); + } + } + + if (showInstallable || showMore) + { + VECADDONS installableAddons; + if (CServiceBroker::GetAddonMgr().GetInstallableAddons(installableAddons)) + { + for (auto addon = installableAddons.begin(); addon != installableAddons.end();) + { + AddonPtr pAddon = *addon; + + // check if the addon matches one of the provided addon types + bool matchesType = false; + for (std::vector<AddonType>::const_iterator type = validTypes.begin(); + type != validTypes.end(); ++type) + { + if (pAddon->HasType(*type)) + { + matchesType = true; + break; + } + } + + if (matchesType) + { + ++addon; + continue; + } + + addon = installableAddons.erase(addon); + } + + if (showInstallable) + addons.insert(addons.end(), installableAddons.begin(), installableAddons.end()); + else if (showMore) + showMore = !installableAddons.empty(); + } + } + + if (addons.empty() && !showNone) + return -1; + + // turn the addons into items + std::map<std::string, AddonPtr> addonMap; + CFileItemList items; + for (const auto& addon : addons) + { + const CFileItemPtr item(CAddonsDirectory::FileItemFromAddon(addon, addon->ID())); + item->SetLabel2(addon->Summary()); + if (!items.Contains(item->GetPath())) + { + items.Add(item); + addonMap.insert(std::make_pair(item->GetPath(), addon)); + } + } + + if (items.IsEmpty() && !showNone) + return -1; + + std::string heading; + for (std::vector<AddonType>::const_iterator type = validTypes.begin(); type != validTypes.end(); + ++type) + { + if (!heading.empty()) + heading += ", "; + heading += CAddonInfo::TranslateType(*type, true); + } + + dialog->SetHeading(CVariant{std::move(heading)}); + dialog->Reset(); + dialog->SetUseDetails(showDetails); + + if (multipleSelection) + { + showNone = false; + showMore = false; + dialog->EnableButton(true, 186); + } + else if (showMore) + dialog->EnableButton(true, 21452); + + if (showNone) + { + CFileItemPtr item(new CFileItem("", false)); + item->SetLabel(g_localizeStrings.Get(231)); + item->SetLabel2(g_localizeStrings.Get(24040)); + item->SetArt("icon", "DefaultAddonNone.png"); + item->SetSpecialSort(SortSpecialOnTop); + items.Add(item); + } + items.Sort(SortByLabel, SortOrderAscending); + + if (!addonIDs.empty()) + { + for (std::vector<std::string>::const_iterator it = addonIDs.begin(); it != addonIDs.end(); ++it) + { + CFileItemPtr item = items.Get(*it); + if (item) + item->Select(true); + } + } + dialog->SetItems(items); + dialog->SetMultiSelection(multipleSelection); + dialog->Open(); + + // if the "Get More" button has been pressed and we haven't shown the + // installable addons so far show a list of installable addons + if (showMore && dialog->IsButtonPressed()) + return SelectAddonID(types, addonIDs, showNone, showDetails, multipleSelection, false, true, + false); + + if (!dialog->IsConfirmed()) + return 0; + + addonIDs.clear(); + for (int i : dialog->GetSelectedItems()) + { + const CFileItemPtr& item = items.Get(i); + + // check if one of the selected addons needs to be installed + if (showInstallable) + { + std::map<std::string, AddonPtr>::const_iterator itAddon = addonMap.find(item->GetPath()); + if (itAddon != addonMap.end()) + { + const AddonPtr& addon = itAddon->second; + + // if the addon isn't installed we need to install it + if (!CServiceBroker::GetAddonMgr().IsAddonInstalled(addon->ID())) + { + AddonPtr installedAddon; + if (!CAddonInstaller::GetInstance().InstallModal(addon->ID(), installedAddon, + InstallModalPrompt::CHOICE_NO)) + continue; + } + + // if the addon is disabled we need to enable it + if (CServiceBroker::GetAddonMgr().IsAddonDisabled(addon->ID())) + CServiceBroker::GetAddonMgr().EnableAddon(addon->ID()); + } + } + + addonIDs.push_back(item->GetPath()); + } + return 1; +} + +std::string CGUIWindowAddonBrowser::GetStartFolder(const std::string& dir) +{ + if (StringUtils::StartsWith(dir, "addons://")) + { + if (StringUtils::StartsWith(dir, "addons://default_binary_addons_source/")) + { + const bool all = CServiceBroker::GetPlatform().SupportsUserInstalledBinaryAddons(); + std::string startDir = dir; + StringUtils::Replace(startDir, "/default_binary_addons_source/", all ? "/all/" : "/user/"); + return startDir; + } + else + return dir; + } + + return CGUIMediaWindow::GetStartFolder(dir); +} |