Merge pull request #5095 from koying/fixdroidappcrash
[vuplus_xbmc] / xbmc / filesystem / AddonsDirectory.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21
22 #include "AddonsDirectory.h"
23 #include "addons/AddonDatabase.h"
24 #include "DirectoryFactory.h"
25 #include "Directory.h"
26 #include "DirectoryCache.h"
27 #include "FileItem.h"
28 #include "addons/Repository.h"
29 #include "addons/AddonInstaller.h"
30 #include "addons/PluginSource.h"
31 #include "guilib/TextureManager.h"
32 #include "File.h"
33 #include "SpecialProtocol.h"
34 #include "utils/URIUtils.h"
35 #include "utils/StringUtils.h"
36 #include "URL.h"
37
38 using namespace ADDON;
39
40 namespace XFILE
41 {
42
43 CAddonsDirectory::CAddonsDirectory(void)
44 {
45 }
46
47 CAddonsDirectory::~CAddonsDirectory(void)
48 {}
49
50 bool CAddonsDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items)
51 {
52   CStdString path1(strPath);
53   URIUtils::RemoveSlashAtEnd(path1);
54   CURL path(path1);
55   items.ClearProperties();
56
57   items.SetContent("addons");
58
59   VECADDONS addons;
60   // get info from repository
61   bool groupAddons = true;
62   bool reposAsFolders = true;
63   if (path.GetHostName().Equals("enabled"))
64   {
65     CAddonMgr::Get().GetAllAddons(addons, true);
66     items.SetProperty("reponame",g_localizeStrings.Get(24062));
67     items.SetLabel(g_localizeStrings.Get(24062));
68   }
69   else if (path.GetHostName().Equals("disabled"))
70   { // grab all disabled addons, including disabled repositories
71     reposAsFolders = false;
72     groupAddons = false;
73     CAddonMgr::Get().GetAllAddons(addons, false, true);
74     items.SetProperty("reponame",g_localizeStrings.Get(24039));
75     items.SetLabel(g_localizeStrings.Get(24039));
76   }
77   else if (path.GetHostName().Equals("outdated"))
78   {
79     reposAsFolders = false;
80     groupAddons = false;
81     CAddonMgr::Get().GetAllOutdatedAddons(addons);
82     items.SetProperty("reponame",g_localizeStrings.Get(24043));
83     items.SetLabel(g_localizeStrings.Get(24043));
84   }
85   else if (path.GetHostName().Equals("repos"))
86   {
87     groupAddons = false;
88     CAddonMgr::Get().GetAddons(ADDON_REPOSITORY,addons,true);
89     items.SetLabel(g_localizeStrings.Get(24033)); // Get Add-ons
90   }
91   else if (path.GetHostName().Equals("sources"))
92   {
93     return GetScriptsAndPlugins(path.GetFileName(), items);
94   }
95   else if (path.GetHostName().Equals("all"))
96   {
97     CAddonDatabase database;
98     database.Open();
99     database.GetAddons(addons);
100     items.SetProperty("reponame",g_localizeStrings.Get(24032));
101     items.SetLabel(g_localizeStrings.Get(24032));
102   }
103   else if (path.GetHostName().Equals("search"))
104   {
105     CStdString search(path.GetFileName());
106     if (search.empty() && !GetKeyboardInput(16017, search))
107       return false;
108
109     items.SetProperty("reponame",g_localizeStrings.Get(283));
110     items.SetLabel(g_localizeStrings.Get(283));
111
112     CAddonDatabase database;
113     database.Open();
114     database.Search(search, addons);
115     GenerateListing(path, addons, items, true);
116
117     path.SetFileName(search);
118     items.SetPath(path.Get());
119     return true;
120   }
121   else
122   {
123     reposAsFolders = false;
124     AddonPtr addon;
125     CAddonMgr::Get().GetAddon(path.GetHostName(),addon);
126     if (!addon)
127       return false;
128
129     // ensure our repos are up to date
130     CAddonInstaller::Get().UpdateRepos(false, true);
131     CAddonDatabase database;
132     database.Open();
133     database.GetRepository(addon->ID(),addons);
134     items.SetProperty("reponame",addon->Name());
135     items.SetLabel(addon->Name());
136   }
137
138   if (path.GetFileName().empty())
139   {
140     if (groupAddons)
141     {
142       for (int i=ADDON_UNKNOWN+1;i<ADDON_VIZ_LIBRARY;++i)
143       {
144         for (unsigned int j=0;j<addons.size();++j)
145         {
146           if (addons[j]->IsType((TYPE)i))
147           {
148             CFileItemPtr item(new CFileItem(TranslateType((TYPE)i,true)));
149             item->SetPath(URIUtils::AddFileToFolder(strPath,TranslateType((TYPE)i,false)));
150             item->m_bIsFolder = true;
151             CStdString thumb = GetIcon((TYPE)i);
152             if (!thumb.empty() && g_TextureManager.HasTexture(thumb))
153               item->SetArt("thumb", thumb);
154             items.Add(item);
155             break;
156           }
157         }
158       }
159       items.SetPath(strPath);
160       return true;
161     }
162   }
163   else
164   {
165     TYPE type = TranslateType(path.GetFileName());
166     items.SetProperty("addoncategory",TranslateType(type, true));
167     items.SetLabel(TranslateType(type, true));
168     items.SetPath(strPath);
169
170     // FIXME: Categorisation of addons needs adding here
171     for (unsigned int j=0;j<addons.size();++j)
172     {
173       if (!addons[j]->IsType(type))
174         addons.erase(addons.begin()+j--);
175     }
176   }
177
178   items.SetPath(strPath);
179   GenerateListing(path, addons, items, reposAsFolders);
180   // check for available updates
181   if (path.GetHostName().Equals("enabled"))
182   {
183     CAddonDatabase database;
184     database.Open();
185     for (int i=0;i<items.Size();++i)
186     {
187       AddonPtr addon2;
188       database.GetAddon(items[i]->GetProperty("Addon.ID").asString(),addon2);
189       if (addon2 && addon2->Version() > AddonVersion(items[i]->GetProperty("Addon.Version").asString())
190                  && !database.IsAddonBlacklisted(addon2->ID(),addon2->Version().c_str()))
191       {
192         items[i]->SetProperty("Addon.Status",g_localizeStrings.Get(24068));
193         items[i]->SetProperty("Addon.UpdateAvail", true);
194       }
195     }
196   }
197   if (path.GetHostName().Equals("repos") && items.Size() > 1)
198   {
199     CFileItemPtr item(new CFileItem("addons://all/",true));
200     item->SetLabel(g_localizeStrings.Get(24032));
201     items.Add(item);
202   }
203   else if (path.GetHostName().Equals("outdated") && items.Size() > 1)
204   {
205     CFileItemPtr item(new CFileItem("addons://update_all/", true));
206     item->SetLabel(g_localizeStrings.Get(24122));
207     item->SetSpecialSort(SortSpecialOnTop);
208     items.Add(item);
209   }
210
211   return true;
212 }
213
214 void CAddonsDirectory::GenerateListing(CURL &path, VECADDONS& addons, CFileItemList &items, bool reposAsFolders)
215 {
216   CStdString xbmcPath = CSpecialProtocol::TranslatePath("special://xbmc/addons");
217   items.ClearItems();
218   for (unsigned i=0; i < addons.size(); i++)
219   {
220     AddonPtr addon = addons[i];
221     CFileItemPtr pItem;
222     if (reposAsFolders && addon->Type() == ADDON_REPOSITORY)
223       pItem = FileItemFromAddon(addon, "addons://", true);
224     else
225       pItem = FileItemFromAddon(addon, path.Get(), false);
226     AddonPtr addon2;
227     if (CAddonMgr::Get().GetAddon(addon->ID(),addon2))
228       pItem->SetProperty("Addon.Status",g_localizeStrings.Get(305));
229     else if (CAddonMgr::Get().IsAddonDisabled(addon->ID()))
230       pItem->SetProperty("Addon.Status",g_localizeStrings.Get(24023));
231
232     if (addon->Props().broken == "DEPSNOTMET")
233       pItem->SetProperty("Addon.Status",g_localizeStrings.Get(24049));
234     else if (!addon->Props().broken.empty())
235       pItem->SetProperty("Addon.Status",g_localizeStrings.Get(24098));
236     if (addon2 && addon2->Version() < addon->Version())
237     {
238       pItem->SetProperty("Addon.Status",g_localizeStrings.Get(24068));
239       pItem->SetProperty("Addon.UpdateAvail", true);
240     }
241     CAddonDatabase::SetPropertiesFromAddon(addon,pItem);
242     items.Add(pItem);
243   }
244 }
245
246 CFileItemPtr CAddonsDirectory::FileItemFromAddon(const AddonPtr &addon, const CStdString &basePath, bool folder)
247 {
248   if (!addon)
249     return CFileItemPtr();
250
251   // TODO: This can probably be done more efficiently
252   CURL url(basePath);
253   url.SetFileName(addon->ID());
254   CStdString path(url.Get());
255   if (folder)
256     URIUtils::AddSlashAtEnd(path);
257
258   CFileItemPtr item(new CFileItem(path, folder));
259
260   CStdString strLabel(addon->Name());
261   if (url.GetHostName().Equals("search"))
262     strLabel = StringUtils::Format("%s - %s", TranslateType(addon->Type(), true).c_str(), addon->Name().c_str());
263
264   item->SetLabel(strLabel);
265
266   if (!(basePath.Equals("addons://") && addon->Type() == ADDON_REPOSITORY))
267     item->SetLabel2(addon->Version().c_str());
268   item->SetArt("thumb", addon->Icon());
269   item->SetLabelPreformated(true);
270   item->SetIconImage("DefaultAddon.png");
271   if (URIUtils::IsInternetStream(addon->FanArt()) || CFile::Exists(addon->FanArt()))
272     item->SetArt("fanart", addon->FanArt());
273   CAddonDatabase::SetPropertiesFromAddon(addon, item);
274   return item;
275 }
276
277 bool CAddonsDirectory::GetScriptsAndPlugins(const CStdString &content, VECADDONS &addons)
278 {
279   CPluginSource::Content type = CPluginSource::Translate(content);
280   if (type == CPluginSource::UNKNOWN)
281     return false;
282
283   VECADDONS tempAddons;
284   CAddonMgr::Get().GetAddons(ADDON_PLUGIN, tempAddons);
285   for (unsigned i=0; i<tempAddons.size(); i++)
286   {
287     PluginPtr plugin = boost::dynamic_pointer_cast<CPluginSource>(tempAddons[i]);
288     if (plugin && plugin->Provides(type))
289       addons.push_back(tempAddons[i]);
290   }
291   tempAddons.clear();
292   CAddonMgr::Get().GetAddons(ADDON_SCRIPT, tempAddons);
293   for (unsigned i=0; i<tempAddons.size(); i++)
294   {
295     PluginPtr plugin = boost::dynamic_pointer_cast<CPluginSource>(tempAddons[i]);
296     if (plugin && plugin->Provides(type))
297       addons.push_back(tempAddons[i]);
298   }
299   return true;
300 }
301
302 bool CAddonsDirectory::GetScriptsAndPlugins(const CStdString &content, CFileItemList &items)
303 {
304   items.Clear();
305
306   VECADDONS addons;
307   if (!GetScriptsAndPlugins(content, addons))
308     return false;
309
310   for (unsigned i=0; i<addons.size(); i++)
311   {
312     CFileItemPtr item(FileItemFromAddon(addons[i], 
313                       addons[i]->Type()==ADDON_PLUGIN?"plugin://":"script://",
314                       addons[i]->Type() == ADDON_PLUGIN));
315     PluginPtr plugin = boost::dynamic_pointer_cast<CPluginSource>(addons[i]);
316     if (plugin->ProvidesSeveral())
317     {
318       CURL url = item->GetAsUrl();
319       CStdString opt = StringUtils::Format("?content_type=%s",content.c_str());
320       url.SetOptions(opt);
321       item->SetPath(url.Get());
322     }
323     items.Add(item);
324   }
325
326   items.Add(GetMoreItem(content));
327
328   items.SetContent("addons");
329   items.SetLabel(g_localizeStrings.Get(24001)); // Add-ons
330
331   return items.Size() > 0;
332 }
333
334 CFileItemPtr CAddonsDirectory::GetMoreItem(const CStdString &content)
335 {
336   CFileItemPtr item(new CFileItem("addons://more/"+content,false));
337   item->SetLabelPreformated(true);
338   item->SetLabel(g_localizeStrings.Get(21452));
339   item->SetIconImage("DefaultAddon.png");
340   item->SetSpecialSort(SortSpecialOnBottom);
341   return item;
342 }
343   
344 }
345