2 * Copyright (C) 2011-2013 Team XBMC
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)
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.
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/>.
21 #include "AddonInstaller.h"
23 #include "utils/log.h"
24 #include "utils/FileUtils.h"
25 #include "utils/URIUtils.h"
27 #include "guilib/LocalizeStrings.h"
28 #include "filesystem/Directory.h"
29 #include "settings/AdvancedSettings.h"
30 #include "settings/Settings.h"
31 #include "ApplicationMessenger.h"
32 #include "filesystem/FavouritesDirectory.h"
33 #include "utils/JobManager.h"
34 #include "dialogs/GUIDialogYesNo.h"
35 #include "addons/AddonManager.h"
36 #include "addons/Repository.h"
37 #include "guilib/GUIWindowManager.h" // for callback
38 #include "GUIUserMessages.h" // for callback
39 #include "utils/StringUtils.h"
40 #include "dialogs/GUIDialogKaiToast.h"
41 #include "dialogs/GUIDialogProgress.h"
43 #include "pvr/PVRManager.h"
46 using namespace XFILE;
47 using namespace ADDON;
50 struct find_map : public binary_function<CAddonInstaller::JobMap::value_type, unsigned int, bool>
52 bool operator() (CAddonInstaller::JobMap::value_type t, unsigned int id) const
54 return (t.second.jobID == id);
58 CAddonInstaller::CAddonInstaller()
63 CAddonInstaller::~CAddonInstaller()
67 CAddonInstaller &CAddonInstaller::Get()
69 static CAddonInstaller addonInstaller;
70 return addonInstaller;
73 void CAddonInstaller::OnJobComplete(unsigned int jobID, bool success, CJob* job)
76 CAddonMgr::Get().FindAddons();
78 CSingleLock lock(m_critSection);
79 if (strncmp(job->GetType(), "repoupdate", 10) == 0)
80 { // repo job finished
81 m_repoUpdateDone.Set();
87 JobMap::iterator i = find_if(m_downloadJobs.begin(), m_downloadJobs.end(), bind2nd(find_map(), jobID));
88 if (i != m_downloadJobs.end())
89 m_downloadJobs.erase(i);
94 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE);
95 g_windowManager.SendThreadMessage(msg);
98 void CAddonInstaller::OnJobProgress(unsigned int jobID, unsigned int progress, unsigned int total, const CJob *job)
100 CSingleLock lock(m_critSection);
101 JobMap::iterator i = find_if(m_downloadJobs.begin(), m_downloadJobs.end(), bind2nd(find_map(), jobID));
102 if (i != m_downloadJobs.end())
103 { // update job progress
104 i->second.progress = progress;
105 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM);
106 msg.SetStringParam(i->first);
108 g_windowManager.SendThreadMessage(msg);
112 bool CAddonInstaller::IsDownloading() const
114 CSingleLock lock(m_critSection);
115 return !m_downloadJobs.empty();
118 void CAddonInstaller::GetInstallList(VECADDONS &addons) const
120 CSingleLock lock(m_critSection);
121 vector<CStdString> addonIDs;
122 for (JobMap::const_iterator i = m_downloadJobs.begin(); i != m_downloadJobs.end(); ++i)
125 addonIDs.push_back(i->first);
129 CAddonDatabase database;
131 for (vector<CStdString>::iterator it = addonIDs.begin(); it != addonIDs.end();++it)
134 if (database.GetAddon(*it, addon))
135 addons.push_back(addon);
139 bool CAddonInstaller::GetProgress(const CStdString &addonID, unsigned int &percent) const
141 CSingleLock lock(m_critSection);
142 JobMap::const_iterator i = m_downloadJobs.find(addonID);
143 if (i != m_downloadJobs.end())
145 percent = i->second.progress;
151 bool CAddonInstaller::Cancel(const CStdString &addonID)
153 CSingleLock lock(m_critSection);
154 JobMap::iterator i = m_downloadJobs.find(addonID);
155 if (i != m_downloadJobs.end())
157 CJobManager::GetInstance().CancelJob(i->second.jobID);
158 m_downloadJobs.erase(i);
164 bool CAddonInstaller::PromptForInstall(const CStdString &addonID, AddonPtr &addon)
166 if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER))
169 // we assume that addons that are enabled don't get to this routine (i.e. that GetAddon() has been called)
170 if (CAddonMgr::Get().GetAddon(addonID, addon, ADDON_UNKNOWN, false))
171 return false; // addon is installed but disabled, and the user has specifically activated something that needs
172 // the addon - should we enable it?
174 // check we have it available
175 CAddonDatabase database;
177 if (database.GetAddon(addonID, addon))
178 { // yes - ask user if they want it installed
179 if (!CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(24076), g_localizeStrings.Get(24100),
180 addon->Name().c_str(), g_localizeStrings.Get(24101)))
182 if (Install(addonID, true))
184 CGUIDialogProgress *progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
187 progress->SetHeading(13413); // Downloading
188 progress->SetLine(0, "");
189 progress->SetLine(1, addon->Name());
190 progress->SetLine(2, "");
191 progress->SetPercentage(0);
192 progress->StartModal();
195 progress->Progress();
196 unsigned int percent;
197 if (progress->IsCanceled())
202 if (!GetProgress(addonID, percent))
204 progress->SetPercentage(percent);
208 return CAddonMgr::Get().GetAddon(addonID, addon);
214 bool CAddonInstaller::Install(const CStdString &addonID, bool force, const CStdString &referer, bool background)
216 if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER))
220 bool addonInstalled = CAddonMgr::Get().GetAddon(addonID, addon, ADDON_UNKNOWN, false);
221 if (addonInstalled && !force)
224 // check whether we have it available in a repository
225 CAddonDatabase database;
227 if (database.GetAddon(addonID, addon))
230 database.GetRepoForAddon(addonID,repo);
232 CAddonMgr::Get().GetAddon(repo,ptr);
233 RepositoryPtr therepo = boost::dynamic_pointer_cast<CRepository>(ptr);
236 hash = therepo->GetAddonHash(addon);
237 return DoInstall(addon, hash, addonInstalled, referer, background);
242 bool CAddonInstaller::DoInstall(const AddonPtr &addon, const CStdString &hash, bool update, const CStdString &referer, bool background)
244 // check whether we already have the addon installing
245 CSingleLock lock(m_critSection);
246 if (m_downloadJobs.find(addon->ID()) != m_downloadJobs.end())
249 // check whether all the dependencies are available or not
250 if (!CheckDependencies(addon))
252 CGUIDialogKaiToast::QueueNotification(addon->Icon(), addon->Name(), g_localizeStrings.Get(24044), TOAST_DISPLAY_TIME, false);
258 unsigned int jobID = CJobManager::GetInstance().AddJob(new CAddonInstallJob(addon, hash, update, referer), this);
259 m_downloadJobs.insert(make_pair(addon->ID(), CDownloadJob(jobID)));
263 m_downloadJobs.insert(make_pair(addon->ID(), CDownloadJob(0)));
265 CAddonInstallJob job(addon, hash, update, referer);
267 { // TODO: dump something to debug log?
271 JobMap::iterator i = m_downloadJobs.find(addon->ID());
272 m_downloadJobs.erase(i);
277 bool CAddonInstaller::InstallFromZip(const CStdString &path)
279 if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER))
282 // grab the descriptive XML document from the zip, and read it in
284 // BUG: some zip files return a single item (root folder) that we think is stored, so we don't use the zip:// protocol
286 URIUtils::CreateArchivePath(zipDir, "zip", path, "");
287 if (!CDirectory::GetDirectory(zipDir, items) || items.Size() != 1 || !items[0]->m_bIsFolder)
289 CGUIDialogKaiToast::QueueNotification("", path, g_localizeStrings.Get(24045), TOAST_DISPLAY_TIME, false);
293 // TODO: possibly add support for github generated zips here?
294 CStdString archive = URIUtils::AddFileToFolder(items[0]->GetPath(), "addon.xml");
298 if (xml.LoadFile(archive) && CAddonMgr::Get().LoadAddonDescriptionFromMemory(xml.RootElement(), addon))
300 // set the correct path
301 addon->Props().path = items[0]->GetPath();
304 return DoInstall(addon);
306 CGUIDialogKaiToast::QueueNotification("", path, g_localizeStrings.Get(24045), TOAST_DISPLAY_TIME, false);
310 void CAddonInstaller::InstallFromXBMCRepo(const set<CStdString> &addonIDs)
312 // first check we have the our repositories up to date (and wait until we do)
313 UpdateRepos(false, true);
315 // now install the addons
316 for (set<CStdString>::const_iterator i = addonIDs.begin(); i != addonIDs.end(); ++i)
320 bool CAddonInstaller::CheckDependencies(const AddonPtr &addon, CAddonDatabase *database /* = NULL */)
322 std::vector<std::string> preDeps;
323 preDeps.push_back(addon->ID());
324 CAddonDatabase localDB;
327 return CheckDependencies(addon, preDeps, *database);
330 bool CAddonInstaller::CheckDependencies(const AddonPtr &addon,
331 std::vector<std::string>& preDeps, CAddonDatabase &database)
334 return true; // a NULL addon has no dependencies
335 ADDONDEPS deps = addon->GetDeps();
337 for (ADDONDEPS::const_iterator i = deps.begin(); i != deps.end(); ++i)
339 const CStdString &addonID = i->first;
340 const AddonVersion &version = i->second.first;
341 bool optional = i->second.second;
343 bool haveAddon = CAddonMgr::Get().GetAddon(addonID, dep);
344 if ((haveAddon && !dep->MeetsVersion(version)) || (!haveAddon && !optional))
345 { // we have it but our version isn't good enough, or we don't have it and we need it
346 if (!database.GetAddon(addonID, dep) || !dep->MeetsVersion(version))
347 { // we don't have it in a repo, or we have it but the version isn't good enough, so dep isn't satisfied.
348 CLog::Log(LOGDEBUG, "Addon %s requires %s version %s which is not available", addon->ID().c_str(), addonID.c_str(), version.c_str());
353 // at this point we have our dep, or the dep is optional (and we don't have it) so check that it's OK as well
354 // TODO: should we assume that installed deps are OK?
355 if (dep && std::find(preDeps.begin(), preDeps.end(), dep->ID()) == preDeps.end())
357 if (!CheckDependencies(dep, preDeps, database))
362 preDeps.push_back(dep->ID());
369 void CAddonInstaller::UpdateRepos(bool force, bool wait)
371 CSingleLock lock(m_critSection);
375 { // wait for our job to complete
377 CLog::Log(LOGDEBUG, "%s - waiting for repository update job to finish...", __FUNCTION__);
378 m_repoUpdateDone.Wait();
382 // don't run repo update jobs while on the login screen which runs under the master profile
383 if((g_windowManager.GetActiveWindow() & WINDOW_ID_MASK) == WINDOW_LOGIN_SCREEN)
385 if (!force && m_repoUpdateWatch.IsRunning() && m_repoUpdateWatch.GetElapsedSeconds() < 600)
387 m_repoUpdateWatch.StartZero();
389 CAddonMgr::Get().GetAddons(ADDON_REPOSITORY,addons);
390 for (unsigned int i=0;i<addons.size();++i)
392 CAddonDatabase database;
394 CDateTime lastUpdate = database.GetRepoTimestamp(addons[i]->ID());
395 if (force || !lastUpdate.IsValid() || lastUpdate + CDateTimeSpan(0,24,0,0) < CDateTime::GetCurrentDateTime())
397 CLog::Log(LOGDEBUG,"Checking repositories for updates (triggered by %s)",addons[i]->Name().c_str());
398 m_repoUpdateJob = CJobManager::GetInstance().AddJob(new CRepositoryUpdateJob(addons), this);
400 { // wait for our job to complete
402 CLog::Log(LOGDEBUG, "%s - waiting for this repository update job to finish...", __FUNCTION__);
403 m_repoUpdateDone.Wait();
410 bool CAddonInstaller::HasJob(const CStdString& ID) const
412 CSingleLock lock(m_critSection);
413 return m_downloadJobs.find(ID) != m_downloadJobs.end();
416 void CAddonInstaller::PrunePackageCache()
418 std::map<CStdString,CFileItemList*> packs;
419 int64_t size = EnumeratePackageFolder(packs);
420 int64_t limit = (int64_t)g_advancedSettings.m_addonPackageFolderSize*1024*1024;
425 // 1. Remove the largest packages, leaving at least 2 for each add-on
429 for (std::map<CStdString,CFileItemList*>::const_iterator it = packs.begin();
430 it != packs.end();++it)
432 it->second->Sort(SortByLabel, SortOrderDescending);
433 for (int j=2;j<it->second->Size();++j)
434 items.Add(CFileItemPtr(new CFileItem(*it->second->Get(j))));
436 items.Sort(SortBySize, SortOrderDescending);
438 while (size > limit && i < items.Size())
440 size -= items[i]->m_dwSize;
441 db.RemovePackage(items[i]->GetPath());
442 CFileUtils::DeleteItem(items[i++],true);
447 // 2. Remove the oldest packages (leaving least 1 for each add-on)
449 for (std::map<CStdString,CFileItemList*>::iterator it = packs.begin();
450 it != packs.end();++it)
452 if (it->second->Size() > 1)
453 items.Add(CFileItemPtr(new CFileItem(*it->second->Get(1))));
455 items.Sort(SortByDate, SortOrderAscending);
457 while (size > limit && i < items.Size())
459 size -= items[i]->m_dwSize;
460 db.RemovePackage(items[i]->GetPath());
461 CFileUtils::DeleteItem(items[i++],true);
465 for (std::map<CStdString,CFileItemList*>::iterator it = packs.begin();
466 it != packs.end();++it)
470 int64_t CAddonInstaller::EnumeratePackageFolder(std::map<CStdString,CFileItemList*>& result)
473 CDirectory::GetDirectory("special://home/addons/packages/",items,".zip",DIR_FLAG_NO_FILE_DIRS);
475 for (int i=0;i<items.Size();++i)
477 if (items[i]->m_bIsFolder)
479 size += items[i]->m_dwSize;
480 CStdString pack,dummy;
481 AddonVersion::SplitFileName(pack,dummy,items[i]->GetLabel());
482 if (result.find(pack) == result.end())
483 result[pack] = new CFileItemList;
484 result[pack]->Add(CFileItemPtr(new CFileItem(*items[i])));
490 CAddonInstallJob::CAddonInstallJob(const AddonPtr &addon, const CStdString &hash, bool update, const CStdString &referer)
491 : m_addon(addon), m_hash(hash), m_update(update), m_referer(referer)
495 AddonPtr CAddonInstallJob::GetRepoForAddon(const AddonPtr& addon)
497 CAddonDatabase database;
500 database.GetRepoForAddon(addon->ID(), repo);
502 CAddonMgr::Get().GetAddon(repo, repoPtr);
507 bool CAddonInstallJob::DoWork()
509 AddonPtr repoPtr = GetRepoForAddon(m_addon);
510 CStdString installFrom;
511 if (!repoPtr || repoPtr->Props().libname.empty())
513 // Addons are installed by downloading the .zip package on the server to the local
514 // packages folder, then extracting from the local .zip package into the addons folder
515 // Both these functions are achieved by "copying" using the vfs.
517 CStdString dest="special://home/addons/packages/";
518 CStdString package = URIUtils::AddFileToFolder("special://home/addons/packages/",
519 URIUtils::GetFileName(m_addon->Path()));
520 if (URIUtils::HasSlashAtEnd(m_addon->Path()))
521 { // passed in a folder - all we need do is copy it across
522 installFrom = m_addon->Path();
530 // check that we don't already have a valid copy
531 if (!m_hash.empty() && CFile::Exists(package))
533 if (db.GetPackageHash(m_addon->ID(), package, md5) && m_hash != md5)
535 db.RemovePackage(package);
536 CFile::Delete(package);
540 // zip passed in - download + extract
541 if (!CFile::Exists(package))
543 CStdString path(m_addon->Path());
544 if (!m_referer.empty() && URIUtils::IsInternetStream(path))
547 url.SetProtocolOptions(m_referer);
551 if (!DownloadPackage(path, dest))
553 CFile::Delete(package);
558 // at this point we have the package - check that it is valid
561 md5 = CUtil::GetFileMD5(package);
562 if (!md5.Equals(m_hash))
564 CFile::Delete(package);
565 ReportInstallError(m_addon->ID(), URIUtils::GetFileName(package));
566 CLog::Log(LOGERROR, "MD5 mismatch after download %s", package.c_str());
569 db.AddPackage(m_addon->ID(), package, md5);
572 // check the archive as well - should have just a single folder in the root
574 URIUtils::CreateArchivePath(archive,"zip",package,"");
576 CFileItemList archivedFiles;
577 CDirectory::GetDirectory(archive, archivedFiles);
579 if (archivedFiles.Size() != 1 || !archivedFiles[0]->m_bIsFolder)
581 db.RemovePackage(package);
582 CFile::Delete(package);
585 installFrom = archivedFiles[0]->GetPath();
590 // run any pre-install functions
591 bool reloadAddon = OnPreInstall();
594 if (!Install(installFrom, repoPtr))
595 return false; // something went wrong
597 // run any post-install guff
598 OnPostInstall(reloadAddon);
604 bool CAddonInstallJob::DownloadPackage(const CStdString &path, const CStdString &dest)
605 { // need to download/copy the package first
607 list.Add(CFileItemPtr(new CFileItem(path,false)));
608 list[0]->Select(true);
609 SetFileOperation(CFileOperationJob::ActionReplace, list, dest);
610 return CFileOperationJob::DoWork();
613 bool CAddonInstallJob::OnPreInstall()
615 // check whether this is an active skin - we need to unload it if so
616 if (CSettings::Get().GetString("lookandfeel.skin") == m_addon->ID())
618 CApplicationMessenger::Get().ExecBuiltIn("UnloadSkin", true);
622 if (m_addon->Type() == ADDON_SERVICE)
624 bool running = !CAddonMgr::Get().IsAddonDisabled(m_addon->ID()); //grab a current state
625 CAddonMgr::Get().DisableAddon(m_addon->ID(),false); // enable it so we can remove it??
626 // regrab from manager to have the correct path set
628 ADDON::CAddonMgr::Get().GetAddon(m_addon->ID(), addon);
629 boost::shared_ptr<CService> service = boost::dynamic_pointer_cast<CService>(addon);
632 CAddonMgr::Get().RemoveAddon(m_addon->ID()); // remove it
636 if (m_addon->Type() == ADDON_PVRDLL)
638 // stop the pvr manager, so running pvr add-ons are stopped and closed
639 PVR::CPVRManager::Get().Stop();
644 bool CAddonInstallJob::DeleteAddon(const CStdString &addonFolder)
647 list.Add(CFileItemPtr(new CFileItem(addonFolder, true)));
648 list[0]->Select(true);
649 CFileOperationJob job(CFileOperationJob::ActionDelete, list, "");
653 bool CAddonInstallJob::Install(const CStdString &installFrom, const AddonPtr& repo)
655 // The first thing we do is install dependencies
656 ADDONDEPS deps = m_addon->GetDeps();
657 CStdString referer = StringUtils::Format("Referer=%s-%s.zip",m_addon->ID().c_str(),m_addon->Version().c_str());
658 for (ADDONDEPS::iterator it = deps.begin(); it != deps.end(); ++it)
660 if (it->first.Equals("xbmc.metadata"))
663 const CStdString &addonID = it->first;
664 const AddonVersion &version = it->second.first;
665 bool optional = it->second.second;
667 bool haveAddon = CAddonMgr::Get().GetAddon(addonID, dependency);
668 if ((haveAddon && !dependency->MeetsVersion(version)) || (!haveAddon && !optional))
669 { // we have it but our version isn't good enough, or we don't have it and we need it
670 bool force=(dependency != NULL);
671 // dependency is already queued up for install - ::Install will fail
672 // instead we wait until the Job has finished. note that we
673 // recall install on purpose in case prior installation failed
674 if (CAddonInstaller::Get().HasJob(addonID))
676 while (CAddonInstaller::Get().HasJob(addonID))
680 // don't have the addon or the addon isn't new enough - grab it (no new job for these)
681 if (!CAddonInstaller::Get().Install(addonID, force, referer, false))
686 // now that we have all our dependencies, we can install our add-on
690 CStdString s = StringUtils::Format("plugin://%s/?action=install"
691 "&package=%s&version=%s", repo->ID().c_str(),
692 m_addon->ID().c_str(),
693 m_addon->Version().c_str());
694 if (!CDirectory::GetDirectory(s, dummy))
699 CStdString addonFolder(installFrom);
700 URIUtils::RemoveSlashAtEnd(addonFolder);
701 addonFolder = URIUtils::AddFileToFolder("special://home/addons/",
702 URIUtils::GetFileName(addonFolder));
704 CFileItemList install;
705 install.Add(CFileItemPtr(new CFileItem(installFrom, true)));
706 install[0]->Select(true);
707 CFileOperationJob job(CFileOperationJob::ActionReplace, install, "special://home/addons/");
710 if (!job.DoWork() || !CAddonMgr::Get().LoadAddonDescription(addonFolder, addon))
711 { // failed extraction or failed to load addon description
712 CStdString addonID = URIUtils::GetFileName(addonFolder);
713 ReportInstallError(addonID, addonID);
714 CLog::Log(LOGERROR,"Could not read addon description of %s", addonID.c_str());
715 DeleteAddon(addonFolder);
719 // Update the addon manager so that it has the newly installed add-on.
720 CAddonMgr::Get().FindAddons();
725 void CAddonInstallJob::OnPostInstall(bool reloadAddon)
727 if (m_addon->Type() < ADDON_VIZ_LIBRARY && CSettings::Get().GetBool("general.addonnotifications"))
729 CGUIDialogKaiToast::QueueNotification(m_addon->Icon(),
731 g_localizeStrings.Get(m_update ? 24065 : 24064),
732 TOAST_DISPLAY_TIME,false,
735 if (m_addon->Type() == ADDON_SKIN)
737 if (reloadAddon || (!m_update && CGUIDialogYesNo::ShowAndGetInput(m_addon->Name(),
738 g_localizeStrings.Get(24099),"","")))
740 CGUIDialogKaiToast *toast = (CGUIDialogKaiToast *)g_windowManager.GetWindow(WINDOW_DIALOG_KAI_TOAST);
746 if (CSettings::Get().GetString("lookandfeel.skin") == m_addon->ID())
747 CApplicationMessenger::Get().ExecBuiltIn("ReloadSkin", true);
749 CSettings::Get().SetString("lookandfeel.skin",m_addon->ID().c_str());
753 if (m_addon->Type() == ADDON_SERVICE)
755 CAddonMgr::Get().DisableAddon(m_addon->ID(),!reloadAddon); //return it into state it was before OnPreInstall()
756 if (reloadAddon) // reload/start it if it was running
758 // regrab from manager to have the correct path set
760 CAddonMgr::Get().GetAddon(m_addon->ID(), addon);
761 boost::shared_ptr<CService> service = boost::dynamic_pointer_cast<CService>(addon);
767 if (m_addon->Type() == ADDON_REPOSITORY)
770 addons.push_back(m_addon);
771 CJobManager::GetInstance().AddJob(new CRepositoryUpdateJob(addons), &CAddonInstaller::Get());
774 if (m_addon->Type() == ADDON_PVRDLL)
776 // (re)start the pvr manager
777 PVR::CPVRManager::Get().Start(true);
781 void CAddonInstallJob::ReportInstallError(const CStdString& addonID,
782 const CStdString& fileName)
785 CAddonDatabase database;
787 database.GetAddon(addonID, addon);
791 CAddonMgr::Get().GetAddon(addonID, addon2);
792 CGUIDialogKaiToast::QueueNotification(addon->Icon(),
794 g_localizeStrings.Get(addon2 ? 113 : 114),
795 TOAST_DISPLAY_TIME, false);
799 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error,
801 g_localizeStrings.Get(114),
802 TOAST_DISPLAY_TIME, false);
806 CStdString CAddonInstallJob::AddonID() const
808 return (m_addon) ? m_addon->ID() : "";
811 CAddonUnInstallJob::CAddonUnInstallJob(const AddonPtr &addon)
816 bool CAddonUnInstallJob::DoWork()
818 if (m_addon->Type() == ADDON_PVRDLL)
820 // stop the pvr manager, so running pvr add-ons are stopped and closed
821 PVR::CPVRManager::Get().Stop();
823 if (m_addon->Type() == ADDON_SERVICE)
825 boost::shared_ptr<CService> service = boost::dynamic_pointer_cast<CService>(m_addon);
830 AddonPtr repoPtr = CAddonInstallJob::GetRepoForAddon(m_addon);
831 RepositoryPtr therepo = boost::dynamic_pointer_cast<CRepository>(repoPtr);
832 if (therepo && !therepo->Props().libname.empty())
835 CStdString s = StringUtils::Format("plugin://%s/?action=uninstall"
836 "&package=%s", therepo->ID().c_str(), m_addon->ID().c_str());
837 if (!CDirectory::GetDirectory(s, dummy))
842 if (!CAddonInstallJob::DeleteAddon(m_addon->Path()))
851 void CAddonUnInstallJob::OnPostUnInstall()
853 if (m_addon->Type() == ADDON_REPOSITORY)
855 CAddonDatabase database;
857 database.DeleteRepository(m_addon->ID());
862 XFILE::CFavouritesDirectory::Load(items);
863 for (int i=0; i < items.Size(); ++i)
865 if (items[i]->GetPath().find(m_addon->ID()) != std::string::npos)
867 items.Remove(items[i].get());
873 CFavouritesDirectory::Save(items);
875 if (m_addon->Type() == ADDON_PVRDLL)
877 if (CSettings::Get().GetBool("pvrmanager.enabled"))
878 PVR::CPVRManager::Get().Start(true);