diff options
author | Alwin Esch <alwin.esch@web.de> | 2020-09-13 17:48:29 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-13 17:48:29 +0200 |
commit | 4eee20507b399fc72c59ae5081b19ba382eb2b32 (patch) | |
tree | 8a4d49f04df1c8095c10c796b8ae74aea2ccc8a9 | |
parent | 1cd618c9cfc9a6c57474959a77ae6a8db3013259 (diff) | |
parent | 8706a451398b7159fb8ab6398162de61dbe0c891 (diff) |
Merge pull request #18286 from AlwinEsch/improve-addon-deprecated-report
[gui][addons] show yes/no dialog about enable of broken addon and improve GUI
29 files changed, 401 insertions, 66 deletions
diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po index cbbfa01a0b..6beae9269e 100644 --- a/addons/resource.language.en_gb/resources/strings.po +++ b/addons/resource.language.en_gb/resources/strings.po @@ -15112,7 +15112,12 @@ msgctxt "#24048" msgid "VideoPlayer InputStream" msgstr "" -#empty string with id 24049 +#. To show addon as deprecated over icon +#: addons/skin.estuary/xml/AddonBrowser.xml +#: addons/skin.estuary/xml/DialogAddonInfo.xml +msgctxt "#24049" +msgid "Add-on has been marked deprecated in repository." +msgstr "" #: unknown msgctxt "#24050" @@ -15355,6 +15360,8 @@ msgctxt "#24095" msgid "Local package cache" msgstr "" +#. To show addon as broken over icon +#: addons/skin.estuary/xml/AddonBrowser.xml #: addons/skin.estuary/xml/DialogAddonInfo.xml msgctxt "#24096" msgid "Add-on has been marked broken in repository." @@ -15715,7 +15722,67 @@ msgctxt "#24163" msgid "Match Apple tvOS Standard (Siri remote)" msgstr "" -#empty strings from id 24164 to 24990 +#. Header text for yes/no dialog about enable of broken addon +#: xbmc/addons/gui/GUIHelpers.cpp +msgctxt "#24164" +msgid "Addon \"{0:s}\" broken" +msgstr "" + +#. Yes/no dialog text with addon broken string value and to ask for use +#: xbmc/addons/gui/GUIHelpers.cpp +msgctxt "#24165" +msgid "Addon marked as broken with following note:[CR][B][I]{0:s}[/I][/B][CR][CR]Do you want to enable?" +msgstr "" + +#. Header text for yes/no dialog about enable of deprecated addon +#: xbmc/addons/gui/GUIHelpers.cpp +msgctxt "#24166" +msgid "Addon \"{0:s}\" deprecated" +msgstr "" + +#. Yes/no dialog text with addon deprecated string value and to ask for use +#: xbmc/addons/gui/GUIHelpers.cpp +msgctxt "#24167" +msgid "Addon marked as deprecated with following note:[CR][B][I]{0:s}[/I][/B][CR][CR]Do you want to enable?" +msgstr "" + +#. Notification event to show addon marked as deprecated. +#: xbmc/addons/AddonInstaller.cpp +msgctxt "#24168" +msgid "Deprecated: {0:s}" +msgstr "" + +#. Lifecycle state name, for normal addons (no special). Used also on skin to compare and identify related state. +#: addons/skin.estuary/xml/AddonBrowser.xml +#: addons/skin.estuary/xml/DialogAddonInfo.xml +#: addons/skin.estuary/xml/Variables.xml +#: xbmc/guilib/guiinfo/AddonsGUIInfo.cpp +msgctxt "#24169" +msgid "Normal" +msgstr "" + +#. Lifecycle state name, for deprecated addons. Used also on skin to compare and identify related state. +#: addons/skin.estuary/xml/AddonBrowser.xml +#: addons/skin.estuary/xml/DialogAddonInfo.xml +#: addons/skin.estuary/xml/Variables.xml +#: xbmc/filesystem/AddonsDirectory.cpp +#: xbmc/guilib/guiinfo/AddonsGUIInfo.cpp +msgctxt "#24170" +msgid "Deprecated" +msgstr "" + +#. Lifecycle state name, for broken and not usable addons. Used also on skin to compare and identify related state. +#: addons/skin.estuary/xml/AddonBrowser.xml +#: addons/skin.estuary/xml/DialogAddonInfo.xml +#: addons/skin.estuary/xml/Variables.xml +#: xbmc/guilib/guiinfo/AddonsGUIInfo.cpp +msgctxt "#24171" +msgid "Broken" +msgstr "" + +#24171-24179 reserved for future use of lifecycle state name (to have continuous chain of GUI) + +#empty strings from id 24180 to 24990 #. Used as error message in add-on browser when add-on repository data could not be downloaded #: xbmc/filesystem/AddonsDirectory.cpp diff --git a/addons/skin.estuary/media/icons/addonstatus/enabled-broken.png b/addons/skin.estuary/media/icons/addonstatus/enabled-broken.png Binary files differnew file mode 100644 index 0000000000..8b4dd1152b --- /dev/null +++ b/addons/skin.estuary/media/icons/addonstatus/enabled-broken.png diff --git a/addons/skin.estuary/media/icons/addonstatus/enabled-deprecated.png b/addons/skin.estuary/media/icons/addonstatus/enabled-deprecated.png Binary files differnew file mode 100644 index 0000000000..0a8294d862 --- /dev/null +++ b/addons/skin.estuary/media/icons/addonstatus/enabled-deprecated.png diff --git a/addons/skin.estuary/media/icons/addonstatus/enabled-normal.png b/addons/skin.estuary/media/icons/addonstatus/enabled-normal.png Binary files differnew file mode 100644 index 0000000000..c5a89ca6f8 --- /dev/null +++ b/addons/skin.estuary/media/icons/addonstatus/enabled-normal.png diff --git a/addons/skin.estuary/xml/AddonBrowser.xml b/addons/skin.estuary/xml/AddonBrowser.xml index 8e243175b2..7703441f08 100644 --- a/addons/skin.estuary/xml/AddonBrowser.xml +++ b/addons/skin.estuary/xml/AddonBrowser.xml @@ -20,6 +20,26 @@ <include content="ListThumbInfoPanel"> <param name="fallback_image" value="DefaultAddon.png" /> </include> + <control type="group"> + <visible>String.IsEqual(ListItem.AddonLifecycleType,$LOCALIZE[24170]) | String.IsEqual(ListItem.AddonLifecycleType,$LOCALIZE[24171])</visible> + <control type="image"> + <left>64</left> + <top>109</top> + <width>472</width> + <height>472</height> + <texture colordiffuse="AAFFFFFF">colors/black.png</texture> + </control> + <control type="textbox"> + <left>85</left> + <top>110</top> + <width>425</width> + <height>470</height> + <align>center</align> + <aligny>center</aligny> + <label>$VAR[AddonLifecycleType]</label> + <font>font36_title</font> + </control> + </control> </control> <include content="TopBar"> <param name="breadcrumbs_label" value="$LOCALIZE[24001]" /> diff --git a/addons/skin.estuary/xml/DialogAddonInfo.xml b/addons/skin.estuary/xml/DialogAddonInfo.xml index 74f829b1e0..43919abf57 100644 --- a/addons/skin.estuary/xml/DialogAddonInfo.xml +++ b/addons/skin.estuary/xml/DialogAddonInfo.xml @@ -213,7 +213,7 @@ <texture fallback="DefaultAddon.png" background="true">$INFO[ListItem.Art(thumb)]</texture> </control> <control type="group"> - <visible>!String.IsEmpty(ListItem.AddonBroken)</visible> + <visible>String.IsEqual(ListItem.AddonLifecycleType,$LOCALIZE[24170]) | String.IsEqual(ListItem.AddonLifecycleType,$LOCALIZE[24171])</visible> <control type="image"> <left>14</left> <top>24</top> @@ -228,7 +228,7 @@ <height>500</height> <align>center</align> <aligny>center</aligny> - <label>$LOCALIZE[24096]</label> + <label>$VAR[AddonLifecycleType]</label> <font>font36_title</font> </control> </control> diff --git a/addons/skin.estuary/xml/Variables.xml b/addons/skin.estuary/xml/Variables.xml index 3ebe1121f0..cac1c268a2 100644 --- a/addons/skin.estuary/xml/Variables.xml +++ b/addons/skin.estuary/xml/Variables.xml @@ -160,13 +160,19 @@ <value condition="Skin.HasSetting(show_profileavatar)">$LOCALIZE[31166]</value> <value>$LOCALIZE[16018]</value> </variable> + <variable name="AddonLifecycleType"> + <value condition="String.IsEqual(ListItem.AddonLifecycleType,$LOCALIZE[24170])">$LOCALIZE[24049]</value> <!-- Deprecated --> + <value condition="String.IsEqual(ListItem.AddonLifecycleType,$LOCALIZE[24171])">$LOCALIZE[24096]</value> <!-- Broken --> + </variable> <variable name="AddonsListIconVar"> - <value condition="!String.IsEmpty(ListItem.AddonBroken)">icons/addonstatus/disable.png</value> + <value condition="[String.IsEqual(ListItem.AddonLifecycleType,$LOCALIZE[24170]) | String.IsEqual(ListItem.AddonLifecycleType,$LOCALIZE[24171])] + !ListItem.Property(addon.isenabled)">icons/addonstatus/disable.png</value> <value condition="ListItem.Property(addon.orphaned)">icons/addonstatus/orphan.png</value> <value condition="ListItem.Property(addon.downloading)">icons/addonstatus/install.png</value> <value condition="ListItem.Property(addon.isinstalled) + !ListItem.Property(addon.isenabled) + Window.IsActive(addonbrowser)">icons/addonstatus/disable.png</value> <value condition="ListItem.Property(addon.hasupdate)">icons/addonstatus/update.png</value> - <value condition="ListItem.Property(addon.isinstalled)">OverlayWatched.png</value> + <value condition="ListItem.Property(addon.isenabled) + String.IsEqual(ListItem.AddonLifecycleType,$LOCALIZE[24169])">icons/addonstatus/enabled-normal.png</value> + <value condition="ListItem.Property(addon.isenabled) + String.IsEqual(ListItem.AddonLifecycleType,$LOCALIZE[24170])">icons/addonstatus/enabled-deprecated.png</value> + <value condition="ListItem.Property(addon.isenabled) + String.IsEqual(ListItem.AddonLifecycleType,$LOCALIZE[24171])">icons/addonstatus/enabled-broken.png</value> <value condition="!ListItem.IsParentFolder">OverlayUnwatched.png</value> </variable> <variable name="ResolutionFlagVar"> diff --git a/addons/xbmc.addon/metadata.xsd b/addons/xbmc.addon/metadata.xsd index e1e35d9468..86a867e919 100644 --- a/addons/xbmc.addon/metadata.xsd +++ b/addons/xbmc.addon/metadata.xsd @@ -14,7 +14,7 @@ <xs:element name="website" type="xs:string" minOccurs="0" maxOccurs="1"/> <xs:element name="source" type="xs:string" minOccurs="0" maxOccurs="1"/> <xs:element name="email" type="xs:string" minOccurs="0" maxOccurs="1"/> - <xs:element name="broken" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="lifecyclestate" type="translatedLifecycleState" minOccurs="0" maxOccurs="unbounded"/> <xs:element name="news" type="xs:string" minOccurs="0" maxOccurs="1"/> <xs:element name="reuselanguageinvoker" type="xs:boolean" minOccurs="0" maxOccurs="1"/> <xs:element name="assets" type="assetsList" minOccurs="0" maxOccurs="1"/> @@ -36,6 +36,14 @@ </xs:extension> </xs:simpleContent> </xs:complexType> + <xs:complexType name="translatedLifecycleState"> + <xs:simpleContent> + <xs:extension base="nonEmptyStringCapped"> + <xs:attribute name="type" type="lifecycleStateType" use="required"/> + <xs:attribute name="lang" type="langIdentifier"/> + </xs:extension> + </xs:simpleContent> + </xs:complexType> <xs:simpleType name="platformType"> <xs:restriction base="xs:string"> <xs:enumeration value="linux"/> @@ -55,6 +63,13 @@ <xs:pattern value="[a-z]{2,3}(_[A-Z]{2}(@\S+)?)?"/> </xs:restriction> </xs:simpleType> + <xs:simpleType name="lifecycleStateType"> + <xs:restriction base="xs:string"> + <xs:enumeration value="normal"/> + <xs:enumeration value="deprecated"/> + <xs:enumeration value="broken"/> + </xs:restriction> + </xs:simpleType> <xs:complexType name="assetsList"> <xs:choice maxOccurs="unbounded"> <xs:element name="icon" type="xs:string" minOccurs="0" maxOccurs="1"/> diff --git a/xbmc/GUIInfoManager.cpp b/xbmc/GUIInfoManager.cpp index ce54175770..e486810daa 100644 --- a/xbmc/GUIInfoManager.cpp +++ b/xbmc/GUIInfoManager.cpp @@ -6070,11 +6070,35 @@ const infomap container_str[] = {{ "property", CONTAINER_PROPERTY }, /// \anchor ListItem_AddonBroken /// _string_, /// @return A message when the addon is marked as broken in the repo. +/// @deprecated but still available\, use \ref ListItem_AddonLifecycleDesc "ListItem.AddonLifecycleDesc" +/// instead /// <p><hr> /// @skinning_v17 **[Infolabel Updated]** \link ListItem_AddonBroken `ListItem.AddonBroken`\endlink /// replaces `ListItem.Property(Addon.Broken)`. /// <p> /// } +/// \table_row3{ <b>`ListItem.AddonLifecycleType`</b>, +/// \anchor ListItem_AddonLifecycleType +/// _string_, +/// @return String name when the addon is marked as special condition in the repo. +/// - <b>Label: 24169 (Normal)</b> - Used if an add-on has no special lifecycle state which is the default state +/// - <b>Label: 24170 (Deprecated)</b> - The add-on should be marked as deprecated but is still usable +/// - <b>Label: 24171 (Broken)</b> - The add-on should marked as broken in the repository +/// <p><hr> +/// @skinning_v19 **[New Infolabel]** \link ListItem_AddonLifecycleType `ListItem.AddonLifecycleType`\endlink +/// replaces `ListItem.AddonBroken`. +/// <p> +/// } +/// \table_row3{ <b>`ListItem.AddonLifecycleDesc`</b>, +/// \anchor ListItem_AddonLifecycleDesc +/// _string_, +/// @return From addon defined message text when it is marked as special condition inside repository. +/// <p><hr> +/// @skinning_v19 **[New Infolabel]** \link ListItem_AddonLifecycleDesc `ListItem.AddonLifecycleDesc``\endlink +/// replaces `ListItem.AddonBroken`. +/// <p> +/// } + /// \table_row3{ <b>`ListItem.AddonType`</b>, /// \anchor ListItem_AddonType /// _string_, @@ -6670,6 +6694,8 @@ const infomap listitem_labels[]= {{ "thumb", LISTITEM_THUMB }, { "addondisclaimer", LISTITEM_ADDON_DISCLAIMER }, { "addonnews", LISTITEM_ADDON_NEWS }, { "addonbroken", LISTITEM_ADDON_BROKEN }, + { "addonlifecycletype", LISTITEM_ADDON_LIFECYCLE_TYPE }, + { "addonlifecycledesc", LISTITEM_ADDON_LIFECYCLE_DESC }, { "addontype", LISTITEM_ADDON_TYPE }, { "addoninstalldate", LISTITEM_ADDON_INSTALL_DATE }, { "addonlastupdated", LISTITEM_ADDON_LAST_UPDATED }, diff --git a/xbmc/addons/Addon.h b/xbmc/addons/Addon.h index e8fed4e5bc..6b72ca62c2 100644 --- a/xbmc/addons/Addon.h +++ b/xbmc/addons/Addon.h @@ -98,7 +98,11 @@ public: ArtMap Art() const override { return m_addonInfo->Art(); } std::vector<std::string> Screenshots() const override { return m_addonInfo->Screenshots(); }; std::string Disclaimer() const override { return m_addonInfo->Disclaimer(); } - std::string Broken() const override { return m_addonInfo->Broken(); } + AddonLifecycleState LifecycleState() const override { return m_addonInfo->LifecycleState(); } + std::string LifecycleStateDescription() const override + { + return m_addonInfo->LifecycleStateDescription(); + } CDateTime InstallDate() const override { return m_addonInfo->InstallDate(); } CDateTime LastUpdated() const override { return m_addonInfo->LastUpdated(); } CDateTime LastUsed() const override { return m_addonInfo->LastUsed(); } diff --git a/xbmc/addons/AddonDatabase.cpp b/xbmc/addons/AddonDatabase.cpp index 6ae473e91d..8ad1e1c0bf 100644 --- a/xbmc/addons/AddonDatabase.cpp +++ b/xbmc/addons/AddonDatabase.cpp @@ -31,7 +31,8 @@ static std::string SerializeMetadata(const IAddon& addon) CVariant variant; variant["author"] = addon.Author(); variant["disclaimer"] = addon.Disclaimer(); - variant["broken"] = addon.Broken(); + variant["lifecycletype"] = static_cast<unsigned int>(addon.LifecycleState()); + variant["lifecycledesc"] = addon.LifecycleStateDescription(); variant["size"] = addon.PackageSize(); variant["path"] = addon.Path(); @@ -81,7 +82,12 @@ static void DeserializeMetadata(const std::string& document, CAddonInfoBuilder:: builder.SetAuthor(variant["author"].asString()); builder.SetDisclaimer(variant["disclaimer"].asString()); - builder.SetBroken(variant["broken"].asString()); + if (variant.isMember("broken")) // Fallback of old + builder.SetLifecycleState(AddonLifecycleState::BROKEN, variant["broken"].asString()); + else + builder.SetLifecycleState( + static_cast<AddonLifecycleState>(variant["lifecycletype"].asUnsignedInteger()), + variant["lifecycledesc"].asString()); builder.SetPackageSize(variant["size"].asUnsignedInteger()); builder.SetPath(variant["path"].asString()); diff --git a/xbmc/addons/AddonInstaller.cpp b/xbmc/addons/AddonInstaller.cpp index 0b26c1ee81..71537808ce 100644 --- a/xbmc/addons/AddonInstaller.cpp +++ b/xbmc/addons/AddonInstaller.cpp @@ -669,12 +669,21 @@ bool CAddonInstallJob::DoWork() CServiceBroker::GetEventLog().Add( EventPtr(new CAddonManagementEvent(m_addon, m_isUpdate ? 24065 : 24084)), notify, false); - if (m_isAutoUpdate && m_addon->IsBroken()) + if (m_isAutoUpdate && m_addon->LifecycleState() == AddonLifecycleState::BROKEN) { CLog::Log(LOGDEBUG, "CAddonInstallJob[%s]: auto-disabling due to being marked as broken", m_addon->ID().c_str()); CServiceBroker::GetAddonMgr().DisableAddon(m_addon->ID(), AddonDisabledReason::USER); CServiceBroker::GetEventLog().Add(EventPtr(new CAddonManagementEvent(m_addon, 24094)), true, false); } + else if (m_addon->LifecycleState() == AddonLifecycleState::DEPRECATED) + { + CLog::Log(LOGDEBUG, "CAddonInstallJob[%s]: installed addon marked as deprecated", + m_addon->ID().c_str()); + std::string text = + StringUtils::Format(g_localizeStrings.Get(24168), m_addon->LifecycleStateDescription()); + CServiceBroker::GetEventLog().Add(EventPtr(new CAddonManagementEvent(m_addon, text)), true, + false); + } // and we're done! MarkFinished(); diff --git a/xbmc/addons/AddonManager.cpp b/xbmc/addons/AddonManager.cpp index 8150789b8f..558f051ee3 100644 --- a/xbmc/addons/AddonManager.cpp +++ b/xbmc/addons/AddonManager.cpp @@ -842,7 +842,8 @@ bool CAddonMgr::IsAddonInstalled(const std::string& ID, bool CAddonMgr::CanAddonBeInstalled(const AddonPtr& addon) { - return addon != nullptr && !addon->IsBroken() && !IsAddonInstalled(addon->ID()); + return addon != nullptr && addon->LifecycleState() != AddonLifecycleState::BROKEN && + !IsAddonInstalled(addon->ID()); } bool CAddonMgr::CanUninstall(const AddonPtr& addon) diff --git a/xbmc/addons/ContextMenus.cpp b/xbmc/addons/ContextMenus.cpp index 2f9e4fd413..5e9d7475e7 100644 --- a/xbmc/addons/ContextMenus.cpp +++ b/xbmc/addons/ContextMenus.cpp @@ -13,7 +13,7 @@ #include "RepositoryUpdater.h" #include "ServiceBroker.h" #include "addons/gui/GUIDialogAddonSettings.h" - +#include "addons/gui/GUIHelpers.h" namespace CONTEXTMENU { @@ -61,6 +61,10 @@ bool CEnableAddon::IsVisible(const CFileItem& item) const bool CEnableAddon::Execute(const CFileItemPtr& item) const { + // Check user want to enable if lifecycle not normal + if (!ADDON::GUI::CHelpers::DialogAddonLifecycleUseAsk(item->GetAddonInfo())) + return false; + return CServiceBroker::GetAddonMgr().EnableAddon(item->GetAddonInfo()->ID()); } diff --git a/xbmc/addons/IAddon.h b/xbmc/addons/IAddon.h index 7e7d81b201..743ba9393f 100644 --- a/xbmc/addons/IAddon.h +++ b/xbmc/addons/IAddon.h @@ -54,7 +54,8 @@ namespace ADDON virtual std::string Author() const =0; virtual std::string Icon() const =0; virtual std::string Disclaimer() const =0; - virtual std::string Broken() const =0; + virtual AddonLifecycleState LifecycleState() const = 0; + virtual std::string LifecycleStateDescription() const = 0; virtual CDateTime InstallDate() const =0; virtual CDateTime LastUpdated() const =0; virtual CDateTime LastUsed() const =0; @@ -84,8 +85,5 @@ namespace ADDON virtual void OnPostInstall(bool update, bool modal) =0; virtual void OnPreUnInstall() =0; virtual void OnPostUnInstall() =0; - - // Derived property - bool IsBroken() const { return !Broken().empty(); } }; }; diff --git a/xbmc/addons/addoninfo/AddonInfo.h b/xbmc/addons/addoninfo/AddonInfo.h index a9a40ed71d..56fa86d0d9 100644 --- a/xbmc/addons/addoninfo/AddonInfo.h +++ b/xbmc/addons/addoninfo/AddonInfo.h @@ -55,6 +55,24 @@ enum class AddonUpdateRule PIN_OLD_VERSION = 2 //!< user downgraded to an older version }; +/*! + * @brief Add-on state defined within addon.xml to report about the current addon + * lifecycle state. + * + * E.g. the add-on is broken and can no longer be used. + * + * XML examples: + * ~~~~~~~~~~~~~{.xml} + * <lifecyclestate type="broken" lang="en_GB">SOME TEXT</lifecyclestate> + * ~~~~~~~~~~~~~ + */ +enum class AddonLifecycleState +{ + NORMAL = 0, //!< Used if an add-on has no special lifecycle state which is the default state + DEPRECATED = 1, //!< the add-on should be marked as deprecated but is still usable + BROKEN = 2, //!< the add-on should marked as broken in the repository +}; + struct DependencyInfo { std::string id; @@ -172,7 +190,11 @@ public: const std::vector<std::string>& Screenshots() const { return m_screenshots; } const std::string& Disclaimer() const { return GetTranslatedText(m_disclaimer); } const std::vector<DependencyInfo>& GetDependencies() const { return m_dependencies; } - const std::string& Broken() const { return m_broken; } + AddonLifecycleState LifecycleState() const { return m_lifecycleState; } + const std::string& LifecycleStateDescription() const + { + return GetTranslatedText(m_lifecycleStateDescription); + } const std::string& Origin() const { return m_origin; } const InfoMap& ExtraInfo() const { return m_extrainfo; } @@ -218,7 +240,8 @@ private: std::vector<std::string> m_screenshots; std::unordered_map<std::string, std::string> m_disclaimer; std::vector<DependencyInfo> m_dependencies; - std::string m_broken; + AddonLifecycleState m_lifecycleState = AddonLifecycleState::NORMAL; + std::unordered_map<std::string, std::string> m_lifecycleStateDescription; CDateTime m_installDate; CDateTime m_lastUpdated; CDateTime m_lastUsed; diff --git a/xbmc/addons/addoninfo/AddonInfoBuilder.cpp b/xbmc/addons/addoninfo/AddonInfoBuilder.cpp index 42b5fc9675..75b61105f6 100644 --- a/xbmc/addons/addoninfo/AddonInfoBuilder.cpp +++ b/xbmc/addons/addoninfo/AddonInfoBuilder.cpp @@ -317,10 +317,32 @@ bool CAddonInfoBuilder::ParseXML(const AddonInfoPtr& addon, const TiXmlElement* if (element && element->GetText() != nullptr) addon->m_forum = element->GetText(); - /* Parse addon.xml "<broken">...</broken>" */ + /* Parse addon.xml "<broken">...</broken>" + * NOTE: Replaced with <lifecyclestate>, available for backward compatibility */ element = child->FirstChildElement("broken"); if (element && element->GetText() != nullptr) - addon->m_broken = element->GetText(); + { + addon->m_lifecycleState = AddonLifecycleState::BROKEN; + addon->m_lifecycleStateDescription.emplace("en_gb", element->GetText()); + } + + /* Parse addon.xml "<lifecyclestate">...</lifecyclestate>" */ + element = child->FirstChildElement("lifecyclestate"); + if (element && element->GetText() != nullptr) + { + const char* lang = element->Attribute("type"); + if (lang) + { + if (strcmp(lang, "broken") == 0) + addon->m_lifecycleState = AddonLifecycleState::BROKEN; + else if (strcmp(lang, "deprecated") == 0) + addon->m_lifecycleState = AddonLifecycleState::DEPRECATED; + else + addon->m_lifecycleState = AddonLifecycleState::NORMAL; + + GetTextList(child, "lifecyclestate", addon->m_lifecycleStateDescription); + } + } /* Parse addon.xml "<language">...</language>" */ element = child->FirstChildElement("language"); diff --git a/xbmc/addons/addoninfo/AddonInfoBuilder.h b/xbmc/addons/addoninfo/AddonInfoBuilder.h index 722c319451..15f6863adc 100644 --- a/xbmc/addons/addoninfo/AddonInfoBuilder.h +++ b/xbmc/addons/addoninfo/AddonInfoBuilder.h @@ -44,7 +44,11 @@ public: void SetArt(std::map<std::string, std::string> art) { m_addonInfo->m_art = std::move(art); } void SetScreenshots(std::vector<std::string> screenshots) { m_addonInfo->m_screenshots = std::move(screenshots); } void SetChangelog(std::string changelog) { m_addonInfo->m_changelog.insert(std::pair<std::string, std::string>("unk", std::move(changelog))); } - void SetBroken(std::string broken) { m_addonInfo->m_broken = std::move(broken); } + void SetLifecycleState(AddonLifecycleState state, std::string description) + { + m_addonInfo->m_lifecycleState = state; + m_addonInfo->m_lifecycleStateDescription.emplace("unk", std::move(description)); + } void SetPath(std::string path) { m_addonInfo->m_path = std::move(path); } void SetLibName(std::string libname) { m_addonInfo->m_libname = std::move(libname); } void SetVersion(AddonVersion version) { m_addonInfo->m_version = std::move(version); } diff --git a/xbmc/addons/gui/CMakeLists.txt b/xbmc/addons/gui/CMakeLists.txt index 2199a5103b..2a9c4632b6 100644 --- a/xbmc/addons/gui/CMakeLists.txt +++ b/xbmc/addons/gui/CMakeLists.txt @@ -1,10 +1,12 @@ set(SOURCES GUIDialogAddonInfo.cpp GUIDialogAddonSettings.cpp + GUIHelpers.cpp GUIViewStateAddonBrowser.cpp GUIWindowAddonBrowser.cpp) set(HEADERS GUIDialogAddonInfo.h GUIDialogAddonSettings.h + GUIHelpers.h GUIViewStateAddonBrowser.h GUIWindowAddonBrowser.h) diff --git a/xbmc/addons/gui/GUIDialogAddonInfo.cpp b/xbmc/addons/gui/GUIDialogAddonInfo.cpp index 4d4067a4b7..f72cf56b35 100644 --- a/xbmc/addons/gui/GUIDialogAddonInfo.cpp +++ b/xbmc/addons/gui/GUIDialogAddonInfo.cpp @@ -17,6 +17,7 @@ #include "addons/AddonManager.h" #include "addons/AddonSystemSettings.h" #include "addons/gui/GUIDialogAddonSettings.h" +#include "addons/gui/GUIHelpers.h" #include "dialogs/GUIDialogContextMenu.h" #include "dialogs/GUIDialogSelect.h" #include "dialogs/GUIDialogYesNo.h" @@ -168,7 +169,8 @@ void CGUIDialogAddonInfo::UpdateControls() m_localAddon && !CServiceBroker::GetAddonMgr().IsAddonDisabled(m_localAddon->ID()); bool canDisable = isInstalled && CServiceBroker::GetAddonMgr().CanAddonBeDisabled(m_localAddon->ID()); - bool canInstall = !isInstalled && !m_item->GetAddonInfo()->IsBroken(); + bool canInstall = + !isInstalled && m_item->GetAddonInfo()->LifecycleState() != AddonLifecycleState::BROKEN; bool canUninstall = m_localAddon && CServiceBroker::GetAddonMgr().CanUninstall(m_localAddon); CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_INSTALL, canInstall || canUninstall); @@ -494,7 +496,13 @@ void CGUIDialogAddonInfo::OnEnableDisable() 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(); } diff --git a/xbmc/addons/gui/GUIHelpers.cpp b/xbmc/addons/gui/GUIHelpers.cpp new file mode 100644 index 0000000000..0a44a6048f --- /dev/null +++ b/xbmc/addons/gui/GUIHelpers.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2005-2020 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 "GUIHelpers.h" + +#include "dialogs/GUIDialogYesNo.h" +#include "guilib/LocalizeStrings.h" + +using namespace ADDON; +using namespace ADDON::GUI; + +bool CHelpers::DialogAddonLifecycleUseAsk(const std::shared_ptr<const IAddon>& addonInfo) +{ + int header_nr; + int text_nr; + switch (addonInfo->LifecycleState()) + { + case AddonLifecycleState::BROKEN: + header_nr = 24164; + text_nr = 24165; + break; + case AddonLifecycleState::DEPRECATED: + header_nr = 24166; + text_nr = 24167; + break; + default: + header_nr = 0; + text_nr = 0; + break; + } + if (header_nr > 0) + { + std::string header = StringUtils::Format(g_localizeStrings.Get(header_nr), addonInfo->ID()); + std::string text = + StringUtils::Format(g_localizeStrings.Get(text_nr), addonInfo->LifecycleStateDescription()); + if (!CGUIDialogYesNo::ShowAndGetInput(header, text)) + return false; + } + + return true; +} diff --git a/xbmc/addons/gui/GUIHelpers.h b/xbmc/addons/gui/GUIHelpers.h new file mode 100644 index 0000000000..32c290d61f --- /dev/null +++ b/xbmc/addons/gui/GUIHelpers.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2005-2020 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. + */ + +#pragma once + +#include "addons/IAddon.h" + +namespace ADDON +{ +namespace GUI +{ + +class CHelpers +{ +public: + /*! + * @brief This shows an Yes/No dialog with information about the add-on if it is + * not in the normal status. + * + * This asks the user whether he really wants to use the add-on and informs with + * text why the other status is. + * + * @note The dialog is currently displayed for @ref AddonLifecycleState::BROKEN + * and @ref AddonLifecycleState::DEPRECATED. + * + * @param[in] addonInfo Information class of the add-on to be checked + * @return True if user activation is desired, false if not + */ + static bool DialogAddonLifecycleUseAsk(const std::shared_ptr<const IAddon>& addonInfo); +}; + +} /* namespace GUI */ +} /* namespace ADDON */ diff --git a/xbmc/addons/gui/GUIWindowAddonBrowser.cpp b/xbmc/addons/gui/GUIWindowAddonBrowser.cpp index a60b5e036d..b80630b7bb 100644 --- a/xbmc/addons/gui/GUIWindowAddonBrowser.cpp +++ b/xbmc/addons/gui/GUIWindowAddonBrowser.cpp @@ -321,7 +321,8 @@ bool CGUIWindowAddonBrowser::GetDirectory(const std::string& strDirectory, CFile { for (int i = items.Size() - 1; i >= 0; i--) { - if (items[i]->GetAddonInfo() && items[i]->GetAddonInfo()->IsBroken()) + if (items[i]->GetAddonInfo() && + items[i]->GetAddonInfo()->LifecycleState() == AddonLifecycleState::BROKEN) { //check if it's installed AddonPtr addon; diff --git a/xbmc/filesystem/AddonsDirectory.cpp b/xbmc/filesystem/AddonsDirectory.cpp index 97f5ad7fe4..afb172444b 100644 --- a/xbmc/filesystem/AddonsDirectory.cpp +++ b/xbmc/filesystem/AddonsDirectory.cpp @@ -853,8 +853,10 @@ void CAddonsDirectory::GenerateAddonListing(const CURL& path, pItem->SetProperty("Addon.Status", g_localizeStrings.Get(24023)); if (hasUpdate) pItem->SetProperty("Addon.Status", g_localizeStrings.Get(24068)); - else if (addon->IsBroken()) + else if (addon->LifecycleState() == AddonLifecycleState::BROKEN) pItem->SetProperty("Addon.Status", g_localizeStrings.Get(24098)); + else if (addon->LifecycleState() == AddonLifecycleState::DEPRECATED) + pItem->SetProperty("Addon.Status", g_localizeStrings.Get(24170)); items.Add(pItem); } diff --git a/xbmc/guilib/guiinfo/AddonsGUIInfo.cpp b/xbmc/guilib/guiinfo/AddonsGUIInfo.cpp index d47b66826f..3c5873a1c1 100644 --- a/xbmc/guilib/guiinfo/AddonsGUIInfo.cpp +++ b/xbmc/guilib/guiinfo/AddonsGUIInfo.cpp @@ -56,8 +56,34 @@ bool CAddonsGUIInfo::GetLabel(std::string& value, const CFileItem *item, int con value = addonInfo->ChangeLog(); return true; case LISTITEM_ADDON_BROKEN: - value = addonInfo->Broken(); + { + // Fallback for old GUI info + if (addonInfo->LifecycleState() == ADDON::AddonLifecycleState::BROKEN) + value = addonInfo->LifecycleStateDescription(); + else + value = ""; return true; + } + case LISTITEM_ADDON_LIFECYCLE_TYPE: + { + const ADDON::AddonLifecycleState state = addonInfo->LifecycleState(); + switch (state) + { + case ADDON::AddonLifecycleState::BROKEN: + value = g_localizeStrings.Get(24171); // "Broken" + break; + case ADDON::AddonLifecycleState::DEPRECATED: + value = g_localizeStrings.Get(24170); // "Deprecated"; + break; + case ADDON::AddonLifecycleState::NORMAL: + default: + value = g_localizeStrings.Get(24169); // "Normal"; + break; + } + return true; + } + case LISTITEM_ADDON_LIFECYCLE_DESC: + value = addonInfo->LifecycleStateDescription(); case LISTITEM_ADDON_TYPE: value = ADDON::CAddonInfo::TranslateType(addonInfo->Type(), true); return true; diff --git a/xbmc/guilib/guiinfo/GUIInfoLabels.h b/xbmc/guilib/guiinfo/GUIInfoLabels.h index 60668a680a..d6ec9abd31 100644 --- a/xbmc/guilib/guiinfo/GUIInfoLabels.h +++ b/xbmc/guilib/guiinfo/GUIInfoLabels.h @@ -887,41 +887,43 @@ #define LISTITEM_ADDON_DESCRIPTION (LISTITEM_START + 168) #define LISTITEM_ADDON_DISCLAIMER (LISTITEM_START + 169) #define LISTITEM_ADDON_BROKEN (LISTITEM_START + 170) -#define LISTITEM_ADDON_TYPE (LISTITEM_START + 171) -#define LISTITEM_ADDON_INSTALL_DATE (LISTITEM_START + 172) -#define LISTITEM_ADDON_LAST_UPDATED (LISTITEM_START + 173) -#define LISTITEM_ADDON_LAST_USED (LISTITEM_START + 174) -#define LISTITEM_STATUS (LISTITEM_START + 175) -#define LISTITEM_ENDTIME_RESUME (LISTITEM_START + 176) -#define LISTITEM_ADDON_ORIGIN (LISTITEM_START + 177) -#define LISTITEM_ADDON_NEWS (LISTITEM_START + 178) -#define LISTITEM_ADDON_SIZE (LISTITEM_START + 179) -#define LISTITEM_EXPIRATION_DATE (LISTITEM_START + 180) -#define LISTITEM_EXPIRATION_TIME (LISTITEM_START + 181) -#define LISTITEM_PROPERTY (LISTITEM_START + 182) -#define LISTITEM_EPG_EVENT_ICON (LISTITEM_START + 183) -#define LISTITEM_HASREMINDERRULE (LISTITEM_START + 184) -#define LISTITEM_HASARCHIVE (LISTITEM_START + 185) -#define LISTITEM_ISPLAYABLE (LISTITEM_START + 186) -#define LISTITEM_FILENAME_NO_EXTENSION (LISTITEM_START + 187) -#define LISTITEM_CURRENTITEM (LISTITEM_START + 188) -#define LISTITEM_IS_NEW (LISTITEM_START + 189) -#define LISTITEM_DISC_TITLE (LISTITEM_START + 190) -#define LISTITEM_IS_BOXSET (LISTITEM_START + 191) -#define LISTITEM_TOTALDISCS (LISTITEM_START + 192) -#define LISTITEM_RELEASEDATE (LISTITEM_START + 193) -#define LISTITEM_ORIGINALDATE (LISTITEM_START + 194) -#define LISTITEM_BPM (LISTITEM_START + 195) -#define LISTITEM_UNIQUEID (LISTITEM_START + 196) -#define LISTITEM_BITRATE (LISTITEM_START + 197) -#define LISTITEM_SAMPLERATE (LISTITEM_START + 198) -#define LISTITEM_MUSICCHANNELS (LISTITEM_START + 199) -#define LISTITEM_IS_PREMIERE (LISTITEM_START + 200) -#define LISTITEM_IS_FINALE (LISTITEM_START + 201) -#define LISTITEM_IS_LIVE (LISTITEM_START + 202) -#define LISTITEM_TVSHOWDBID (LISTITEM_START + 203) -#define LISTITEM_ALBUMSTATUS (LISTITEM_START + 204) -#define LISTITEM_ISAUTOUPDATEABLE (LISTITEM_START + 205) +#define LISTITEM_ADDON_LIFECYCLE_TYPE (LISTITEM_START + 171) +#define LISTITEM_ADDON_LIFECYCLE_DESC (LISTITEM_START + 172) +#define LISTITEM_ADDON_TYPE (LISTITEM_START + 173) +#define LISTITEM_ADDON_INSTALL_DATE (LISTITEM_START + 174) +#define LISTITEM_ADDON_LAST_UPDATED (LISTITEM_START + 175) +#define LISTITEM_ADDON_LAST_USED (LISTITEM_START + 176) +#define LISTITEM_STATUS (LISTITEM_START + 177) +#define LISTITEM_ENDTIME_RESUME (LISTITEM_START + 178) +#define LISTITEM_ADDON_ORIGIN (LISTITEM_START + 179) +#define LISTITEM_ADDON_NEWS (LISTITEM_START + 180) +#define LISTITEM_ADDON_SIZE (LISTITEM_START + 181) +#define LISTITEM_EXPIRATION_DATE (LISTITEM_START + 182) +#define LISTITEM_EXPIRATION_TIME (LISTITEM_START + 183) +#define LISTITEM_PROPERTY (LISTITEM_START + 184) +#define LISTITEM_EPG_EVENT_ICON (LISTITEM_START + 185) +#define LISTITEM_HASREMINDERRULE (LISTITEM_START + 186) +#define LISTITEM_HASARCHIVE (LISTITEM_START + 187) +#define LISTITEM_ISPLAYABLE (LISTITEM_START + 188) +#define LISTITEM_FILENAME_NO_EXTENSION (LISTITEM_START + 189) +#define LISTITEM_CURRENTITEM (LISTITEM_START + 190) +#define LISTITEM_IS_NEW (LISTITEM_START + 191) +#define LISTITEM_DISC_TITLE (LISTITEM_START + 192) +#define LISTITEM_IS_BOXSET (LISTITEM_START + 193) +#define LISTITEM_TOTALDISCS (LISTITEM_START + 194) +#define LISTITEM_RELEASEDATE (LISTITEM_START + 195) +#define LISTITEM_ORIGINALDATE (LISTITEM_START + 196) +#define LISTITEM_BPM (LISTITEM_START + 197) +#define LISTITEM_UNIQUEID (LISTITEM_START + 198) +#define LISTITEM_BITRATE (LISTITEM_START + 199) +#define LISTITEM_SAMPLERATE (LISTITEM_START + 200) +#define LISTITEM_MUSICCHANNELS (LISTITEM_START + 201) +#define LISTITEM_IS_PREMIERE (LISTITEM_START + 202) +#define LISTITEM_IS_FINALE (LISTITEM_START + 203) +#define LISTITEM_IS_LIVE (LISTITEM_START + 204) +#define LISTITEM_TVSHOWDBID (LISTITEM_START + 205) +#define LISTITEM_ALBUMSTATUS (LISTITEM_START + 206) +#define LISTITEM_ISAUTOUPDATEABLE (LISTITEM_START + 207) #define LISTITEM_END (LISTITEM_START + 2500) diff --git a/xbmc/interfaces/json-rpc/AddonsOperations.cpp b/xbmc/interfaces/json-rpc/AddonsOperations.cpp index 9ac5e164d3..dff6317ef9 100644 --- a/xbmc/interfaces/json-rpc/AddonsOperations.cpp +++ b/xbmc/interfaces/json-rpc/AddonsOperations.cpp @@ -247,10 +247,14 @@ static CVariant Serialize(const AddonPtr& addon) info["optional"] = dep.optional; variant["dependencies"].push_back(std::move(info)); } - if (!addon->IsBroken()) + if (addon->LifecycleState() == AddonLifecycleState::BROKEN) + variant["broken"] = addon->LifecycleStateDescription(); + else variant["broken"] = false; + if (addon->LifecycleState() == AddonLifecycleState::DEPRECATED) + variant["deprecated"] = addon->LifecycleStateDescription(); else - variant["broken"] = addon->Broken(); + variant["deprecated"] = false; variant["extrainfo"] = CVariant(CVariant::VariantTypeArray); for (const auto& kv : addon->ExtraInfo()) { diff --git a/xbmc/interfaces/json-rpc/schema/types.json b/xbmc/interfaces/json-rpc/schema/types.json index 8e49251da2..0863c002f2 100644 --- a/xbmc/interfaces/json-rpc/schema/types.json +++ b/xbmc/interfaces/json-rpc/schema/types.json @@ -1622,7 +1622,7 @@ "extends": "Item.Fields.Base", "items": { "type": "string", "enum": [ "name", "version", "summary", "description", "path", "author", "thumbnail", "disclaimer", "fanart", - "dependencies", "broken", "extrainfo", "rating", "enabled", "installed" ] + "dependencies", "broken", "extrainfo", "rating", "enabled", "installed", "deprecated" ] } }, "Addon.Details": { @@ -1659,7 +1659,8 @@ }, "rating": { "type": "integer" }, "enabled": { "type": "boolean" }, - "installed": { "type": "boolean" } + "installed": { "type": "boolean" }, + "deprecated": { "type": [ "boolean", "string" ] } } }, "GUI.Stereoscopy.Mode": { diff --git a/xbmc/interfaces/json-rpc/schema/version.txt b/xbmc/interfaces/json-rpc/schema/version.txt index 56b242e498..19b4e985c7 100644 --- a/xbmc/interfaces/json-rpc/schema/version.txt +++ b/xbmc/interfaces/json-rpc/schema/version.txt @@ -1 +1 @@ -JSONRPC_VERSION 11.13.0
\ No newline at end of file +JSONRPC_VERSION 11.14.0 |