aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlwin Esch <alwin.esch@web.de>2020-09-13 17:48:29 +0200
committerGitHub <noreply@github.com>2020-09-13 17:48:29 +0200
commit4eee20507b399fc72c59ae5081b19ba382eb2b32 (patch)
tree8a4d49f04df1c8095c10c796b8ae74aea2ccc8a9
parent1cd618c9cfc9a6c57474959a77ae6a8db3013259 (diff)
parent8706a451398b7159fb8ab6398162de61dbe0c891 (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
-rw-r--r--addons/resource.language.en_gb/resources/strings.po71
-rw-r--r--addons/skin.estuary/media/icons/addonstatus/enabled-broken.pngbin0 -> 889 bytes
-rw-r--r--addons/skin.estuary/media/icons/addonstatus/enabled-deprecated.pngbin0 -> 423 bytes
-rw-r--r--addons/skin.estuary/media/icons/addonstatus/enabled-normal.pngbin0 -> 267 bytes
-rw-r--r--addons/skin.estuary/xml/AddonBrowser.xml20
-rw-r--r--addons/skin.estuary/xml/DialogAddonInfo.xml4
-rw-r--r--addons/skin.estuary/xml/Variables.xml10
-rw-r--r--addons/xbmc.addon/metadata.xsd17
-rw-r--r--xbmc/GUIInfoManager.cpp26
-rw-r--r--xbmc/addons/Addon.h6
-rw-r--r--xbmc/addons/AddonDatabase.cpp10
-rw-r--r--xbmc/addons/AddonInstaller.cpp11
-rw-r--r--xbmc/addons/AddonManager.cpp3
-rw-r--r--xbmc/addons/ContextMenus.cpp6
-rw-r--r--xbmc/addons/IAddon.h6
-rw-r--r--xbmc/addons/addoninfo/AddonInfo.h27
-rw-r--r--xbmc/addons/addoninfo/AddonInfoBuilder.cpp26
-rw-r--r--xbmc/addons/addoninfo/AddonInfoBuilder.h6
-rw-r--r--xbmc/addons/gui/CMakeLists.txt2
-rw-r--r--xbmc/addons/gui/GUIDialogAddonInfo.cpp10
-rw-r--r--xbmc/addons/gui/GUIHelpers.cpp46
-rw-r--r--xbmc/addons/gui/GUIHelpers.h38
-rw-r--r--xbmc/addons/gui/GUIWindowAddonBrowser.cpp3
-rw-r--r--xbmc/filesystem/AddonsDirectory.cpp4
-rw-r--r--xbmc/guilib/guiinfo/AddonsGUIInfo.cpp28
-rw-r--r--xbmc/guilib/guiinfo/GUIInfoLabels.h72
-rw-r--r--xbmc/interfaces/json-rpc/AddonsOperations.cpp8
-rw-r--r--xbmc/interfaces/json-rpc/schema/types.json5
-rw-r--r--xbmc/interfaces/json-rpc/schema/version.txt2
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
new file mode 100644
index 0000000000..8b4dd1152b
--- /dev/null
+++ b/addons/skin.estuary/media/icons/addonstatus/enabled-broken.png
Binary files differ
diff --git a/addons/skin.estuary/media/icons/addonstatus/enabled-deprecated.png b/addons/skin.estuary/media/icons/addonstatus/enabled-deprecated.png
new file mode 100644
index 0000000000..0a8294d862
--- /dev/null
+++ b/addons/skin.estuary/media/icons/addonstatus/enabled-deprecated.png
Binary files differ
diff --git a/addons/skin.estuary/media/icons/addonstatus/enabled-normal.png b/addons/skin.estuary/media/icons/addonstatus/enabled-normal.png
new file mode 100644
index 0000000000..c5a89ca6f8
--- /dev/null
+++ b/addons/skin.estuary/media/icons/addonstatus/enabled-normal.png
Binary files differ
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