diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
commit | c04dcc2e7d834218ef2d4194331e383402495ae1 (patch) | |
tree | 7333e38d10d75386e60f336b80c2443c1166031d /xbmc/profiles | |
parent | Initial commit. (diff) | |
download | kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip |
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/profiles')
-rw-r--r-- | xbmc/profiles/CMakeLists.txt | 7 | ||||
-rw-r--r-- | xbmc/profiles/Profile.cpp | 126 | ||||
-rw-r--r-- | xbmc/profiles/Profile.h | 98 | ||||
-rw-r--r-- | xbmc/profiles/ProfileManager.cpp | 747 | ||||
-rw-r--r-- | xbmc/profiles/ProfileManager.h | 222 | ||||
-rw-r--r-- | xbmc/profiles/dialogs/CMakeLists.txt | 7 | ||||
-rw-r--r-- | xbmc/profiles/dialogs/GUIDialogLockSettings.cpp | 320 | ||||
-rw-r--r-- | xbmc/profiles/dialogs/GUIDialogLockSettings.h | 53 | ||||
-rw-r--r-- | xbmc/profiles/dialogs/GUIDialogProfileSettings.cpp | 397 | ||||
-rw-r--r-- | xbmc/profiles/dialogs/GUIDialogProfileSettings.h | 62 | ||||
-rw-r--r-- | xbmc/profiles/windows/CMakeLists.txt | 5 | ||||
-rw-r--r-- | xbmc/profiles/windows/GUIWindowSettingsProfile.cpp | 269 | ||||
-rw-r--r-- | xbmc/profiles/windows/GUIWindowSettingsProfile.h | 35 |
13 files changed, 2348 insertions, 0 deletions
diff --git a/xbmc/profiles/CMakeLists.txt b/xbmc/profiles/CMakeLists.txt new file mode 100644 index 0000000..1756c0c --- /dev/null +++ b/xbmc/profiles/CMakeLists.txt @@ -0,0 +1,7 @@ +set(SOURCES Profile.cpp + ProfileManager.cpp) + +set(HEADERS Profile.h + ProfileManager.h) + +core_add_library(profiles) diff --git a/xbmc/profiles/Profile.cpp b/xbmc/profiles/Profile.cpp new file mode 100644 index 0000000..5665d25 --- /dev/null +++ b/xbmc/profiles/Profile.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "Profile.h" + +#include "XBDateTime.h" +#include "utils/XMLUtils.h" + +CProfile::CLock::CLock(LockType type, const std::string &password): + code(password) +{ + programs = false; + pictures = false; + files = false; + video = false; + music = false; + settings = LOCK_LEVEL::NONE; + addonManager = false; + games = false; + mode = type; +} + +void CProfile::CLock::Validate() +{ + if (mode != LOCK_MODE_EVERYONE && (code == "-" || code.empty())) + mode = LOCK_MODE_EVERYONE; + + if (code.empty() || mode == LOCK_MODE_EVERYONE) + code = "-"; +} + +CProfile::CProfile(const std::string &directory, const std::string &name, const int id): + m_directory(directory), + m_name(name) +{ + m_id = id; + m_bDatabases = true; + m_bCanWrite = true; + m_bSources = true; + m_bCanWriteSources = true; + m_bAddons = true; +} + +CProfile::~CProfile(void) = default; + +void CProfile::setDate() +{ + const CDateTime now = CDateTime::GetCurrentDateTime(); + std::string strDate = now.GetAsLocalizedDate(false); + std::string strTime = now.GetAsLocalizedTime(TIME_FORMAT_GUESS); + if (strDate.empty() || strTime.empty()) + setDate("-"); + else + setDate(strDate+" - "+strTime); +} + +void CProfile::Load(const TiXmlNode *node, int nextIdProfile) +{ + if (!XMLUtils::GetInt(node, "id", m_id)) + m_id = nextIdProfile; + + XMLUtils::GetString(node, "name", m_name); + XMLUtils::GetPath(node, "directory", m_directory); + XMLUtils::GetPath(node, "thumbnail", m_thumb); + XMLUtils::GetBoolean(node, "hasdatabases", m_bDatabases); + XMLUtils::GetBoolean(node, "canwritedatabases", m_bCanWrite); + XMLUtils::GetBoolean(node, "hassources", m_bSources); + XMLUtils::GetBoolean(node, "canwritesources", m_bCanWriteSources); + XMLUtils::GetBoolean(node, "lockaddonmanager", m_locks.addonManager); + int settings = m_locks.settings; + XMLUtils::GetInt(node, "locksettings", settings); + m_locks.settings = (LOCK_LEVEL::SETTINGS_LOCK)settings; + XMLUtils::GetBoolean(node, "lockfiles", m_locks.files); + XMLUtils::GetBoolean(node, "lockmusic", m_locks.music); + XMLUtils::GetBoolean(node, "lockvideo", m_locks.video); + XMLUtils::GetBoolean(node, "lockpictures", m_locks.pictures); + XMLUtils::GetBoolean(node, "lockprograms", m_locks.programs); + XMLUtils::GetBoolean(node, "lockgames", m_locks.games); + + int lockMode = m_locks.mode; + XMLUtils::GetInt(node, "lockmode", lockMode); + m_locks.mode = (LockType)lockMode; + if (m_locks.mode > LOCK_MODE_QWERTY || m_locks.mode < LOCK_MODE_EVERYONE) + m_locks.mode = LOCK_MODE_EVERYONE; + + XMLUtils::GetString(node, "lockcode", m_locks.code); + XMLUtils::GetString(node, "lastdate", m_date); +} + +void CProfile::Save(TiXmlNode *root) const +{ + TiXmlElement profileNode("profile"); + TiXmlNode *node = root->InsertEndChild(profileNode); + + XMLUtils::SetInt(node, "id", m_id); + XMLUtils::SetString(node, "name", m_name); + XMLUtils::SetPath(node, "directory", m_directory); + XMLUtils::SetPath(node, "thumbnail", m_thumb); + XMLUtils::SetBoolean(node, "hasdatabases", m_bDatabases); + XMLUtils::SetBoolean(node, "canwritedatabases", m_bCanWrite); + XMLUtils::SetBoolean(node, "hassources", m_bSources); + XMLUtils::SetBoolean(node, "canwritesources", m_bCanWriteSources); + XMLUtils::SetBoolean(node, "lockaddonmanager", m_locks.addonManager); + XMLUtils::SetInt(node, "locksettings", m_locks.settings); + XMLUtils::SetBoolean(node, "lockfiles", m_locks.files); + XMLUtils::SetBoolean(node, "lockmusic", m_locks.music); + XMLUtils::SetBoolean(node, "lockvideo", m_locks.video); + XMLUtils::SetBoolean(node, "lockpictures", m_locks.pictures); + XMLUtils::SetBoolean(node, "lockprograms", m_locks.programs); + XMLUtils::SetBoolean(node, "lockgames", m_locks.games); + + XMLUtils::SetInt(node, "lockmode", m_locks.mode); + XMLUtils::SetString(node,"lockcode", m_locks.code); + XMLUtils::SetString(node, "lastdate", m_date); +} + +void CProfile::SetLocks(const CProfile::CLock &locks) +{ + m_locks = locks; + m_locks.Validate(); +} diff --git a/xbmc/profiles/Profile.h b/xbmc/profiles/Profile.h new file mode 100644 index 0000000..42d4fba --- /dev/null +++ b/xbmc/profiles/Profile.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "LockType.h" +#include "SettingsLock.h" + +#include <string> +#include <vector> + +class TiXmlNode; + +class CProfile +{ +public: + /*! \brief Class for handling lock status + */ + class CLock + { + public: + CLock(LockType type = LOCK_MODE_EVERYONE, const std::string &password = ""); + void Validate(); + + LockType mode; + std::string code; + bool addonManager; + LOCK_LEVEL::SETTINGS_LOCK settings; + bool music; + bool video; + bool files; + bool pictures; + bool programs; + bool games; + }; + + CProfile(const std::string &directory = "", const std::string &name = "", const int id = -1); + ~CProfile(void); + + void Load(const TiXmlNode *node, int nextIdProfile); + void Save(TiXmlNode *root) const; + + const std::string& getDate() const { return m_date;} + int getId() const { return m_id; } + const std::string& getName() const { return m_name;} + const std::string& getDirectory() const { return m_directory;} + const std::string& getThumb() const { return m_thumb;} + const std::string& getLockCode() const { return m_locks.code;} + LockType getLockMode() const { return m_locks.mode; } + + bool hasDatabases() const { return m_bDatabases; } + bool canWriteDatabases() const { return m_bCanWrite; } + bool hasSources() const { return m_bSources; } + bool canWriteSources() const { return m_bCanWriteSources; } + bool hasAddons() const { return m_bAddons; } + /** + \brief Returns which settings levels are locked for the current profile + \sa LOCK_LEVEL::SETTINGS_LOCK + */ + LOCK_LEVEL::SETTINGS_LOCK settingsLockLevel() const { return m_locks.settings; } + bool addonmanagerLocked() const { return m_locks.addonManager; } + bool musicLocked() const { return m_locks.music; } + bool videoLocked() const { return m_locks.video; } + bool picturesLocked() const { return m_locks.pictures; } + bool filesLocked() const { return m_locks.files; } + bool programsLocked() const { return m_locks.programs; } + bool gamesLocked() const { return m_locks.games; } + const CLock &GetLocks() const { return m_locks; } + + void setName(const std::string& name) {m_name = name;} + void setDirectory(const std::string& directory) {m_directory = directory;} + void setDate(const std::string& strDate) { m_date = strDate;} + void setDate(); + void setThumb(const std::string& thumb) {m_thumb = thumb;} + void setDatabases(bool bHas) { m_bDatabases = bHas; } + void setWriteDatabases(bool bCan) { m_bCanWrite = bCan; } + void setSources(bool bHas) { m_bSources = bHas; } + void setWriteSources(bool bCan) { m_bCanWriteSources = bCan; } + void SetLocks(const CLock &locks); + +private: + std::string m_directory; + int m_id; + std::string m_name; + std::string m_date; + std::string m_thumb; + bool m_bDatabases; + bool m_bCanWrite; + bool m_bSources; + bool m_bCanWriteSources; + bool m_bAddons; + CLock m_locks; +}; diff --git a/xbmc/profiles/ProfileManager.cpp b/xbmc/profiles/ProfileManager.cpp new file mode 100644 index 0000000..e4e5bec --- /dev/null +++ b/xbmc/profiles/ProfileManager.cpp @@ -0,0 +1,747 @@ +/* + * Copyright (C) 2013-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "ProfileManager.h" + +#include "DatabaseManager.h" +#include "FileItem.h" +#include "GUIInfoManager.h" +#include "GUIPassword.h" +#include "PasswordManager.h" +#include "ServiceBroker.h" +#include "Util.h" +#include "addons/Skin.h" +#include "application/Application.h" //! @todo Remove me +#include "application/ApplicationComponents.h" +#include "application/ApplicationPowerHandling.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "dialogs/GUIDialogYesNo.h" +#include "events/EventLog.h" +#include "events/EventLogManager.h" +#include "filesystem/Directory.h" +#include "filesystem/DirectoryCache.h" +#include "filesystem/File.h" +#include "filesystem/SpecialProtocol.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "input/InputManager.h" +#include "music/MusicLibraryQueue.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" +#include "settings/lib/SettingsManager.h" +#include "threads/SingleLock.h" + +#include <algorithm> +#include <mutex> +#include <string> +#include <vector> +#if !defined(TARGET_WINDOWS) && defined(HAS_DVD_DRIVE) +#include "storage/DetectDVDType.h" +#endif +#include "ContextMenuManager.h" //! @todo Remove me +#include "PlayListPlayer.h" //! @todo Remove me +#include "addons/AddonManager.h" //! @todo Remove me +#include "addons/Service.h" //! @todo Remove me +#include "application/Application.h" //! @todo Remove me +#include "favourites/FavouritesService.h" //! @todo Remove me +#include "guilib/StereoscopicsManager.h" //! @todo Remove me +#include "interfaces/json-rpc/JSONRPC.h" //! @todo Remove me +#include "network/Network.h" //! @todo Remove me +#include "network/NetworkServices.h" //! @todo Remove me +#include "pvr/PVRManager.h" //! @todo Remove me +#include "utils/FileUtils.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" +#include "utils/Variant.h" +#include "utils/XMLUtils.h" +#include "utils/log.h" +#include "video/VideoLibraryQueue.h" //! @todo Remove me +#include "weather/WeatherManager.h" //! @todo Remove me + +//! @todo +//! eventually the profile should dictate where special://masterprofile/ is +//! but for now it makes sense to leave all the profile settings in a user +//! writeable location like special://masterprofile/ +#define PROFILES_FILE "special://masterprofile/profiles.xml" + +#define XML_PROFILES "profiles" +#define XML_AUTO_LOGIN "autologin" +#define XML_LAST_LOADED "lastloaded" +#define XML_LOGIN_SCREEN "useloginscreen" +#define XML_NEXTID "nextIdProfile" +#define XML_PROFILE "profile" + +using namespace XFILE; + +static CProfile EmptyProfile; + +CProfileManager::CProfileManager() : m_eventLogs(new CEventLogManager) +{ +} + +CProfileManager::~CProfileManager() = default; + +void CProfileManager::Initialize(const std::shared_ptr<CSettings>& settings) +{ + m_settings = settings; + + if (m_settings->IsLoaded()) + OnSettingsLoaded(); + + m_settings->GetSettingsManager()->RegisterSettingsHandler(this); + + std::set<std::string> settingSet = { + CSettings::SETTING_EVENTLOG_SHOW + }; + + m_settings->GetSettingsManager()->RegisterCallback(this, settingSet); +} + +void CProfileManager::Uninitialize() +{ + m_settings->GetSettingsManager()->UnregisterCallback(this); + m_settings->GetSettingsManager()->UnregisterSettingsHandler(this); +} + +void CProfileManager::OnSettingsLoaded() +{ + // check them all + std::string strDir = m_settings->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH); + if (strDir == "set default" || strDir.empty()) + { + strDir = "special://profile/playlists/"; + m_settings->SetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH, strDir); + } + + CDirectory::Create(strDir); + CDirectory::Create(URIUtils::AddFileToFolder(strDir,"music")); + CDirectory::Create(URIUtils::AddFileToFolder(strDir,"video")); + CDirectory::Create(URIUtils::AddFileToFolder(strDir,"mixed")); +} + +void CProfileManager::OnSettingsSaved() const +{ + // save mastercode + Save(); +} + +void CProfileManager::OnSettingsCleared() +{ + Clear(); +} + +bool CProfileManager::Load() +{ + bool ret = true; + const std::string file = PROFILES_FILE; + + std::unique_lock<CCriticalSection> lock(m_critical); + + // clear out our profiles + m_profiles.clear(); + + if (CFile::Exists(file)) + { + CXBMCTinyXML profilesDoc; + if (profilesDoc.LoadFile(file)) + { + const TiXmlElement *rootElement = profilesDoc.RootElement(); + if (rootElement && StringUtils::EqualsNoCase(rootElement->Value(), XML_PROFILES)) + { + XMLUtils::GetUInt(rootElement, XML_LAST_LOADED, m_lastUsedProfile); + XMLUtils::GetBoolean(rootElement, XML_LOGIN_SCREEN, m_usingLoginScreen); + XMLUtils::GetInt(rootElement, XML_AUTO_LOGIN, m_autoLoginProfile); + XMLUtils::GetInt(rootElement, XML_NEXTID, m_nextProfileId); + + std::string defaultDir("special://home/userdata"); + if (!CDirectory::Exists(defaultDir)) + defaultDir = "special://xbmc/userdata"; + + const TiXmlElement* pProfile = rootElement->FirstChildElement(XML_PROFILE); + while (pProfile) + { + CProfile profile(defaultDir); + profile.Load(pProfile, GetNextProfileId()); + AddProfile(profile); + + pProfile = pProfile->NextSiblingElement(XML_PROFILE); + } + } + else + { + CLog::Log(LOGERROR, "CProfileManager: error loading {}, no <profiles> node", file); + ret = false; + } + } + else + { + CLog::Log(LOGERROR, "CProfileManager: error loading {}, Line {}\n{}", file, + profilesDoc.ErrorRow(), profilesDoc.ErrorDesc()); + ret = false; + } + } + if (!ret) + { + CLog::Log(LOGERROR, + "Failed to load profile - might be corrupted - falling back to master profile"); + m_profiles.clear(); + CFile::Delete(file); + + ret = true; + } + + if (m_profiles.empty()) + { // add the master user + CProfile profile("special://masterprofile/", "Master user", 0); + AddProfile(profile); + } + + // check the validity of the previous profile index + if (m_lastUsedProfile >= m_profiles.size()) + m_lastUsedProfile = 0; + + SetCurrentProfileId(m_lastUsedProfile); + + // check the validity of the auto login profile index + if (m_autoLoginProfile < -1 || m_autoLoginProfile >= (int)m_profiles.size()) + m_autoLoginProfile = -1; + else if (m_autoLoginProfile >= 0) + SetCurrentProfileId(m_autoLoginProfile); + + // the login screen runs as the master profile, so if we're using this, we need to ensure + // we switch to the master profile + if (m_usingLoginScreen) + SetCurrentProfileId(0); + + return ret; +} + +bool CProfileManager::Save() const +{ + const std::string file = PROFILES_FILE; + + std::unique_lock<CCriticalSection> lock(m_critical); + + CXBMCTinyXML xmlDoc; + TiXmlElement xmlRootElement(XML_PROFILES); + TiXmlNode *pRoot = xmlDoc.InsertEndChild(xmlRootElement); + if (pRoot == nullptr) + return false; + + XMLUtils::SetInt(pRoot, XML_LAST_LOADED, m_currentProfile); + XMLUtils::SetBoolean(pRoot, XML_LOGIN_SCREEN, m_usingLoginScreen); + XMLUtils::SetInt(pRoot, XML_AUTO_LOGIN, m_autoLoginProfile); + XMLUtils::SetInt(pRoot, XML_NEXTID, m_nextProfileId); + + for (const auto& profile : m_profiles) + profile.Save(pRoot); + + // save the file + return xmlDoc.SaveFile(file); +} + +void CProfileManager::Clear() +{ + std::unique_lock<CCriticalSection> lock(m_critical); + m_usingLoginScreen = false; + m_profileLoadedForLogin = false; + m_previousProfileLoadedForLogin = false; + m_lastUsedProfile = 0; + m_nextProfileId = 0; + SetCurrentProfileId(0); + m_profiles.clear(); +} + +void CProfileManager::PrepareLoadProfile(unsigned int profileIndex) +{ + CContextMenuManager &contextMenuManager = CServiceBroker::GetContextMenuManager(); + ADDON::CServiceAddonManager &serviceAddons = CServiceBroker::GetServiceAddons(); + PVR::CPVRManager &pvrManager = CServiceBroker::GetPVRManager(); + CNetworkBase &networkManager = CServiceBroker::GetNetwork(); + + contextMenuManager.Deinit(); + + serviceAddons.Stop(); + + // stop PVR related services + pvrManager.Stop(); + + if (profileIndex != 0 || !IsMasterProfile()) + networkManager.NetworkMessage(CNetworkBase::SERVICES_DOWN, 1); +} + +bool CProfileManager::LoadProfile(unsigned int index) +{ + PrepareLoadProfile(index); + + if (index == 0 && IsMasterProfile()) + { + CGUIWindow* pWindow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_HOME); + if (pWindow) + pWindow->ResetControlStates(); + + UpdateCurrentProfileDate(); + FinalizeLoadProfile(); + + return true; + } + + std::unique_lock<CCriticalSection> lock(m_critical); + // check if the index is valid or not + if (index >= m_profiles.size()) + return false; + + // check if the profile is already active + if (m_currentProfile == index) + return true; + + // save any settings of the currently used skin but only if the (master) + // profile hasn't just been loaded as a temporary profile for login + if (g_SkinInfo != nullptr && !m_previousProfileLoadedForLogin) + g_SkinInfo->SaveSettings(); + + // @todo: why is m_settings not used here? + const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings(); + + // unload any old settings + settings->Unload(); + + SetCurrentProfileId(index); + m_previousProfileLoadedForLogin = false; + + // load the new settings + if (!settings->Load()) + { + CLog::Log(LOGFATAL, "CProfileManager: unable to load settings for profile \"{}\"", + m_profiles.at(index).getName()); + return false; + } + settings->SetLoaded(); + + CreateProfileFolders(); + + CServiceBroker::GetDatabaseManager().Initialize(); + CServiceBroker::GetInputManager().LoadKeymaps(); + + CServiceBroker::GetInputManager().SetMouseEnabled(settings->GetBool(CSettings::SETTING_INPUT_ENABLEMOUSE)); + + CGUIComponent* gui = CServiceBroker::GetGUI(); + if (gui) + { + CGUIInfoManager& infoMgr = gui->GetInfoManager(); + infoMgr.ResetCache(); + infoMgr.GetInfoProviders().GetGUIControlsInfoProvider().ResetContainerMovingCache(); + infoMgr.GetInfoProviders().GetLibraryInfoProvider().ResetLibraryBools(); + } + + if (m_currentProfile != 0) + { + CXBMCTinyXML doc; + if (doc.LoadFile(URIUtils::AddFileToFolder(GetUserDataFolder(), "guisettings.xml"))) + { + settings->LoadSetting(doc.RootElement(), CSettings::SETTING_MASTERLOCK_MAXRETRIES); + settings->LoadSetting(doc.RootElement(), CSettings::SETTING_MASTERLOCK_STARTUPLOCK); + } + } + + CPasswordManager::GetInstance().Clear(); + + // to set labels - shares are reloaded +#if !defined(TARGET_WINDOWS) && defined(HAS_DVD_DRIVE) + MEDIA_DETECT::CDetectDVDMedia::UpdateState(); +#endif + + // init windows + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_WINDOW_RESET); + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg); + + CUtil::DeleteDirectoryCache(); + g_directoryCache.Clear(); + + lock.unlock(); + + UpdateCurrentProfileDate(); + FinalizeLoadProfile(); + + m_profileLoadedForLogin = false; + + return true; +} + +void CProfileManager::FinalizeLoadProfile() +{ + CContextMenuManager &contextMenuManager = CServiceBroker::GetContextMenuManager(); + ADDON::CServiceAddonManager &serviceAddons = CServiceBroker::GetServiceAddons(); + PVR::CPVRManager &pvrManager = CServiceBroker::GetPVRManager(); + CNetworkBase &networkManager = CServiceBroker::GetNetwork(); + ADDON::CAddonMgr &addonManager = CServiceBroker::GetAddonMgr(); + CWeatherManager &weatherManager = CServiceBroker::GetWeatherManager(); + CFavouritesService &favouritesManager = CServiceBroker::GetFavouritesService(); + PLAYLIST::CPlayListPlayer &playlistManager = CServiceBroker::GetPlaylistPlayer(); + CStereoscopicsManager &stereoscopicsManager = CServiceBroker::GetGUI()->GetStereoscopicsManager(); + + if (m_lastUsedProfile != m_currentProfile) + { + playlistManager.ClearPlaylist(PLAYLIST::TYPE_VIDEO); + playlistManager.ClearPlaylist(PLAYLIST::TYPE_MUSIC); + playlistManager.SetCurrentPlaylist(PLAYLIST::TYPE_NONE); + } + + networkManager.NetworkMessage(CNetworkBase::SERVICES_UP, 1); + + // reload the add-ons, or we will first load all add-ons from the master account without checking disabled status + addonManager.ReInit(); + + // let CApplication know that we are logging into a new profile + g_application.SetLoggingIn(true); + + if (!g_application.LoadLanguage(true)) + { + CLog::Log(LOGFATAL, "Unable to load language for profile \"{}\"", + GetCurrentProfile().getName()); + return; + } + + weatherManager.Refresh(); + + JSONRPC::CJSONRPC::Initialize(); + + // Restart context menu manager + contextMenuManager.Init(); + + // Restart PVR services if we are not just loading the master profile for the login screen + if (m_previousProfileLoadedForLogin || m_currentProfile != 0 || m_lastUsedProfile == 0) + pvrManager.Init(); + + favouritesManager.ReInit(GetProfileUserDataFolder()); + + // Start these operations only when a profile is loaded, not on the login screen + if (!m_profileLoadedForLogin || (m_profileLoadedForLogin && m_lastUsedProfile == 0)) + { + serviceAddons.Start(); + g_application.UpdateLibraries(); + } + + stereoscopicsManager.Initialize(); + + // Load initial window + int firstWindow = g_SkinInfo->GetFirstWindow(); + + CServiceBroker::GetGUI()->GetWindowManager().ChangeActiveWindow(firstWindow); + + //the user interfaces has been fully initialized, let everyone know + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, WINDOW_SETTINGS_PROFILES, 0, GUI_MSG_UI_READY); + CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg); +} + +void CProfileManager::LogOff() +{ + CNetworkBase &networkManager = CServiceBroker::GetNetwork(); + + g_application.StopPlaying(); + + if (CMusicLibraryQueue::GetInstance().IsScanningLibrary()) + CMusicLibraryQueue::GetInstance().StopLibraryScanning(); + + if (CVideoLibraryQueue::GetInstance().IsRunning()) + CVideoLibraryQueue::GetInstance().CancelAllJobs(); + + // Stop PVR services + CServiceBroker::GetPVRManager().Stop(); + + networkManager.NetworkMessage(CNetworkBase::SERVICES_DOWN, 1); + + LoadMasterProfileForLogin(); + + g_passwordManager.bMasterUser = false; + + auto& components = CServiceBroker::GetAppComponents(); + const auto appPower = components.GetComponent<CApplicationPowerHandling>(); + appPower->WakeUpScreenSaverAndDPMS(); + CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_LOGIN_SCREEN, {}, false); + + if (!CServiceBroker::GetNetwork().GetServices().StartEventServer()) // event server could be needed in some situations + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, g_localizeStrings.Get(33102), g_localizeStrings.Get(33100)); +} + +bool CProfileManager::DeleteProfile(unsigned int index) +{ + std::unique_lock<CCriticalSection> lock(m_critical); + const CProfile *profile = GetProfile(index); + if (profile == NULL) + return false; + + CGUIDialogYesNo* dlgYesNo = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogYesNo>(WINDOW_DIALOG_YES_NO); + if (dlgYesNo == NULL) + return false; + + const std::string& str = g_localizeStrings.Get(13201); + dlgYesNo->SetHeading(CVariant{13200}); + dlgYesNo->SetLine(0, CVariant{StringUtils::Format(str, profile->getName())}); + dlgYesNo->SetLine(1, CVariant{""}); + dlgYesNo->SetLine(2, CVariant{""}); + dlgYesNo->Open(); + + if (!dlgYesNo->IsConfirmed()) + return false; + + // fall back to master profile if necessary + if ((int)index == m_autoLoginProfile) + m_autoLoginProfile = 0; + + // delete profile + std::string strDirectory = profile->getDirectory(); + m_profiles.erase(m_profiles.begin() + index); + + // fall back to master profile if necessary + if (index == m_currentProfile) + { + LoadProfile(0); + m_settings->Save(); + } + + CFileItemPtr item = CFileItemPtr(new CFileItem(URIUtils::AddFileToFolder(GetUserDataFolder(), strDirectory))); + item->SetPath(URIUtils::AddFileToFolder(GetUserDataFolder(), strDirectory + "/")); + item->m_bIsFolder = true; + item->Select(true); + + CGUIComponent *gui = CServiceBroker::GetGUI(); + if (gui && gui->ConfirmDelete(item->GetPath())) + CFileUtils::DeleteItem(item); + + return Save(); +} + +void CProfileManager::CreateProfileFolders() +{ + CDirectory::Create(GetDatabaseFolder()); + CDirectory::Create(GetCDDBFolder()); + CDirectory::Create(GetLibraryFolder()); + + // create Thumbnails/* + CDirectory::Create(GetThumbnailsFolder()); + CDirectory::Create(GetVideoThumbFolder()); + CDirectory::Create(GetBookmarksThumbFolder()); + CDirectory::Create(GetSavestatesFolder()); + for (size_t hex = 0; hex < 16; hex++) + CDirectory::Create( + URIUtils::AddFileToFolder(GetThumbnailsFolder(), StringUtils::Format("{:x}", hex))); + + CDirectory::Create("special://profile/addon_data"); + CDirectory::Create("special://profile/keymaps"); +} + +const CProfile& CProfileManager::GetMasterProfile() const +{ + std::unique_lock<CCriticalSection> lock(m_critical); + if (!m_profiles.empty()) + return m_profiles[0]; + + CLog::Log(LOGERROR, "{}: master profile doesn't exist", __FUNCTION__); + return EmptyProfile; +} + +const CProfile& CProfileManager::GetCurrentProfile() const +{ + std::unique_lock<CCriticalSection> lock(m_critical); + if (m_currentProfile < m_profiles.size()) + return m_profiles[m_currentProfile]; + + CLog::Log(LOGERROR, "CProfileManager: current profile index ({0}) is outside of the valid range ({1})", m_currentProfile, m_profiles.size()); + return EmptyProfile; +} + +const CProfile* CProfileManager::GetProfile(unsigned int index) const +{ + std::unique_lock<CCriticalSection> lock(m_critical); + if (index < m_profiles.size()) + return &m_profiles[index]; + + return NULL; +} + +CProfile* CProfileManager::GetProfile(unsigned int index) +{ + std::unique_lock<CCriticalSection> lock(m_critical); + if (index < m_profiles.size()) + return &m_profiles[index]; + + return NULL; +} + +int CProfileManager::GetProfileIndex(const std::string &name) const +{ + std::unique_lock<CCriticalSection> lock(m_critical); + for (int i = 0; i < static_cast<int>(m_profiles.size()); i++) + { + if (StringUtils::EqualsNoCase(m_profiles[i].getName(), name)) + return i; + } + + return -1; +} + +void CProfileManager::AddProfile(const CProfile &profile) +{ + { + std::unique_lock<CCriticalSection> lock(m_critical); + // data integrity check - covers off migration from old profiles.xml, + // incrementing of the m_nextIdProfile,and bad data coming in + m_nextProfileId = std::max(m_nextProfileId, profile.getId() + 1); + + m_profiles.push_back(profile); + } + Save(); +} + +void CProfileManager::UpdateCurrentProfileDate() +{ + std::unique_lock<CCriticalSection> lock(m_critical); + if (m_currentProfile < m_profiles.size()) + { + m_profiles[m_currentProfile].setDate(); + CSingleExit exit(m_critical); + Save(); + } +} + +void CProfileManager::LoadMasterProfileForLogin() +{ + std::unique_lock<CCriticalSection> lock(m_critical); + // save the previous user + m_lastUsedProfile = m_currentProfile; + if (m_currentProfile != 0) + { + // determines that the (master) profile has only been loaded for login + m_profileLoadedForLogin = true; + + LoadProfile(0); + + // remember that the (master) profile has only been loaded for login + m_previousProfileLoadedForLogin = true; + } +} + +bool CProfileManager::GetProfileName(const unsigned int profileId, std::string& name) const +{ + std::unique_lock<CCriticalSection> lock(m_critical); + const CProfile *profile = GetProfile(profileId); + if (!profile) + return false; + + name = profile->getName(); + return true; +} + +std::string CProfileManager::GetUserDataFolder() const +{ + return GetMasterProfile().getDirectory(); +} + +std::string CProfileManager::GetProfileUserDataFolder() const +{ + if (m_currentProfile == 0) + return GetUserDataFolder(); + + return URIUtils::AddFileToFolder(GetUserDataFolder(), GetCurrentProfile().getDirectory()); +} + +std::string CProfileManager::GetDatabaseFolder() const +{ + if (GetCurrentProfile().hasDatabases()) + return URIUtils::AddFileToFolder(GetProfileUserDataFolder(), "Database"); + + return URIUtils::AddFileToFolder(GetUserDataFolder(), "Database"); +} + +std::string CProfileManager::GetCDDBFolder() const +{ + return URIUtils::AddFileToFolder(GetDatabaseFolder(), "CDDB"); +} + +std::string CProfileManager::GetThumbnailsFolder() const +{ + if (GetCurrentProfile().hasDatabases()) + return URIUtils::AddFileToFolder(GetProfileUserDataFolder(), "Thumbnails"); + + return URIUtils::AddFileToFolder(GetUserDataFolder(), "Thumbnails"); +} + +std::string CProfileManager::GetVideoThumbFolder() const +{ + return URIUtils::AddFileToFolder(GetThumbnailsFolder(), "Video"); +} + +std::string CProfileManager::GetBookmarksThumbFolder() const +{ + return URIUtils::AddFileToFolder(GetVideoThumbFolder(), "Bookmarks"); +} + +std::string CProfileManager::GetLibraryFolder() const +{ + if (GetCurrentProfile().hasDatabases()) + return URIUtils::AddFileToFolder(GetProfileUserDataFolder(), "library"); + + return URIUtils::AddFileToFolder(GetUserDataFolder(), "library"); +} + +std::string CProfileManager::GetSavestatesFolder() const +{ + if (GetCurrentProfile().hasDatabases()) + return URIUtils::AddFileToFolder(GetProfileUserDataFolder(), "Savestates"); + + return URIUtils::AddFileToFolder(GetUserDataFolder(), "Savestates"); +} + +std::string CProfileManager::GetSettingsFile() const +{ + if (m_currentProfile == 0) + return "special://masterprofile/guisettings.xml"; + + return "special://profile/guisettings.xml"; +} + +std::string CProfileManager::GetUserDataItem(const std::string& strFile) const +{ + std::string path; + path = "special://profile/" + strFile; + + // check if item exists in the profile (either for folder or + // for a file (depending on slashAtEnd of strFile) otherwise + // return path to masterprofile + if ((URIUtils::HasSlashAtEnd(path) && !CDirectory::Exists(path)) || !CFile::Exists(path)) + path = "special://masterprofile/" + strFile; + + return path; +} + +CEventLog& CProfileManager::GetEventLog() +{ + return m_eventLogs->GetEventLog(GetCurrentProfileId()); +} + +void CProfileManager::OnSettingAction(const std::shared_ptr<const CSetting>& setting) +{ + if (setting == nullptr) + return; + + const std::string& settingId = setting->GetId(); + if (settingId == CSettings::SETTING_EVENTLOG_SHOW) + GetEventLog().ShowFullEventLog(); +} + +void CProfileManager::SetCurrentProfileId(unsigned int profileId) +{ + { + std::unique_lock<CCriticalSection> lock(m_critical); + m_currentProfile = profileId; + CSpecialProtocol::SetProfilePath(GetProfileUserDataFolder()); + } + Save(); +} diff --git a/xbmc/profiles/ProfileManager.h b/xbmc/profiles/ProfileManager.h new file mode 100644 index 0000000..e394518 --- /dev/null +++ b/xbmc/profiles/ProfileManager.h @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2013-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "profiles/Profile.h" +#include "settings/lib/ISettingCallback.h" +#include "settings/lib/ISettingsHandler.h" +#include "threads/CriticalSection.h" + +#include <memory> +#include <stdint.h> +#include <vector> + +class CEventLog; +class CEventLogManager; +class CSettings; +class TiXmlNode; + +class CProfileManager : protected ISettingsHandler, + protected ISettingCallback +{ +public: + CProfileManager(); + CProfileManager(const CProfileManager&) = delete; + CProfileManager& operator=(CProfileManager const&) = delete; + ~CProfileManager() override; + + void Initialize(const std::shared_ptr<CSettings>& settings); + void Uninitialize(); + + void OnSettingsLoaded() override; + void OnSettingsSaved() const override; + void OnSettingsCleared() override; + + bool Load(); + /*! \brief Load the user profile information from disk + Loads the profiles.xml file and creates the list of profiles. + If no profiles exist, a master user is created. Should be called + after special://masterprofile/ has been defined. + \param file XML file to load. + */ + + bool Save() const; + /*! \brief Save the user profile information to disk + Saves the list of profiles to the profiles.xml file. + \param file XML file to save. + \return true on success, false on failure to save + */ + + void Clear(); + + bool LoadProfile(unsigned int index); + void LogOff(); + + bool DeleteProfile(unsigned int index); + + void CreateProfileFolders(); + + /*! \brief Retrieve the master profile + \return const reference to the master profile + */ + const CProfile& GetMasterProfile() const; + + /*! \brief Retrieve the current profile + \return const reference to the current profile + */ + const CProfile& GetCurrentProfile() const; + + /*! \brief Retrieve the profile from an index + \param unsigned index of the profile to retrieve + \return const pointer to the profile, NULL if the index is invalid + */ + const CProfile* GetProfile(unsigned int index) const; + + /*! \brief Retrieve the profile from an index + \param unsigned index of the profile to retrieve + \return pointer to the profile, NULL if the index is invalid + */ + CProfile* GetProfile(unsigned int index); + + /*! \brief Retrieve index of a particular profile by name + \param name name of the profile index to retrieve + \return index of this profile, -1 if invalid. + */ + int GetProfileIndex(const std::string &name) const; + + /*! \brief Retrieve the number of profiles + \return number of profiles + */ + size_t GetNumberOfProfiles() const { return m_profiles.size(); } + + /*! \brief Add a new profile + \param profile CProfile to add + */ + void AddProfile(const CProfile &profile); + + /*! \brief Are we using the login screen? + \return true if we're using the login screen, false otherwise + */ + bool UsingLoginScreen() const { return m_usingLoginScreen; } + + /*! \brief Toggle login screen use on and off + Toggles the login screen state + */ + void ToggleLoginScreen() + { + m_usingLoginScreen = !m_usingLoginScreen; + Save(); + } + + /*! \brief Are we the master user? + \return true if the current profile is the master user, false otherwise + */ + bool IsMasterProfile() const { return m_currentProfile == 0; } + + /*! \brief Update the date of the current profile + */ + void UpdateCurrentProfileDate(); + + /*! \brief Load the master user for the purposes of logging in + Loads the master user. Identical to LoadProfile(0) but doesn't + update the last logged in details + */ + void LoadMasterProfileForLogin(); + + /*! \brief Retrieve the last used profile index + \return the last used profile that logged in. Does not count the + master user during login. + */ + uint32_t GetLastUsedProfileIndex() const { return m_lastUsedProfile; } + + /*! \brief Retrieve the current profile index + \return the index of the currently logged in profile. + */ + uint32_t GetCurrentProfileIndex() const { return m_currentProfile; } + + /*! \brief Retrieve the next id to use for a new profile + \return the unique <id> to be used when creating a new profile + */ + int GetNextProfileId() const { return m_nextProfileId; } + + int GetCurrentProfileId() const { return GetCurrentProfile().getId(); } + + /*! \brief Retrieve the autologin profile id + Retrieves the autologin profile id. When set to -1, then the last + used profile will be loaded + \return the id to the autologin profile + */ + int GetAutoLoginProfileId() const { return m_autoLoginProfile; } + + /*! \brief Retrieve the autologin profile id + Retrieves the autologin profile id. When set to -1, then the last + used profile will be loaded + \return the id to the autologin profile + */ + void SetAutoLoginProfileId(const int profileId) + { + m_autoLoginProfile = profileId; + Save(); + } + + /*! \brief Retrieve the name of a particular profile by index + \param profileId profile index for which to retrieve the name + \param name will hold the name of the profile when a valid profile index has been provided + \return false if profileId is an invalid index, true if the name parameter is set + */ + bool GetProfileName(const unsigned int profileId, std::string& name) const; + + std::string GetUserDataFolder() const; + std::string GetProfileUserDataFolder() const; + std::string GetDatabaseFolder() const; + std::string GetCDDBFolder() const; + std::string GetThumbnailsFolder() const; + std::string GetVideoThumbFolder() const; + std::string GetBookmarksThumbFolder() const; + std::string GetLibraryFolder() const; + std::string GetSavestatesFolder() const; + std::string GetSettingsFile() const; + + // uses HasSlashAtEnd to determine if a directory or file was meant + std::string GetUserDataItem(const std::string& strFile) const; + + // Event log access + CEventLog &GetEventLog(); + +protected: + // implementation of ISettingCallback + void OnSettingAction(const std::shared_ptr<const CSetting>& setting) override; + +private: + /*! \brief Set the current profile id and update the special://profile path + \param profileId profile index + */ + void SetCurrentProfileId(unsigned int profileId); + + void PrepareLoadProfile(unsigned int profileIndex); + void FinalizeLoadProfile(); + + // Construction parameters + std::shared_ptr<CSettings> m_settings; + + std::vector<CProfile> m_profiles; + bool m_usingLoginScreen = false; + bool m_profileLoadedForLogin = false; + bool m_previousProfileLoadedForLogin = false; + int m_autoLoginProfile = -1; + unsigned int m_lastUsedProfile = 0; + unsigned int m_currentProfile = + 0; // do not modify directly, use SetCurrentProfileId() function instead + int m_nextProfileId = + 0; // for tracking the next available id to give to a new profile to ensure id's are not re-used + mutable CCriticalSection m_critical; + + // Event properties + std::unique_ptr<CEventLogManager> m_eventLogs; +}; diff --git a/xbmc/profiles/dialogs/CMakeLists.txt b/xbmc/profiles/dialogs/CMakeLists.txt new file mode 100644 index 0000000..e7fc216 --- /dev/null +++ b/xbmc/profiles/dialogs/CMakeLists.txt @@ -0,0 +1,7 @@ +set(SOURCES GUIDialogLockSettings.cpp + GUIDialogProfileSettings.cpp) + +set(HEADERS GUIDialogLockSettings.h + GUIDialogProfileSettings.h) + +core_add_library(profiles_dialogs) diff --git a/xbmc/profiles/dialogs/GUIDialogLockSettings.cpp b/xbmc/profiles/dialogs/GUIDialogLockSettings.cpp new file mode 100644 index 0000000..52eabc4 --- /dev/null +++ b/xbmc/profiles/dialogs/GUIDialogLockSettings.cpp @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "GUIDialogLockSettings.h" + +#include "ServiceBroker.h" +#include "URL.h" +#include "dialogs/GUIDialogGamepad.h" +#include "dialogs/GUIDialogNumeric.h" +#include "dialogs/GUIDialogSelect.h" +#include "favourites/FavouritesService.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIKeyboardFactory.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "settings/lib/Setting.h" +#include "settings/lib/SettingSection.h" +#include "settings/windows/GUIControlSettings.h" +#include "utils/StringUtils.h" +#include "utils/log.h" + +#include <utility> + +#define SETTING_USERNAME "user.name" +#define SETTING_PASSWORD "user.password" +#define SETTING_PASSWORD_REMEMBER "user.rememberpassword" + +#define SETTING_LOCKCODE "lock.code" +#define SETTING_LOCK_MUSIC "lock.music" +#define SETTING_LOCK_VIDEOS "lock.videos" +#define SETTING_LOCK_PICTURES "lock.pictures" +#define SETTING_LOCK_PROGRAMS "lock.programs" +#define SETTING_LOCK_FILEMANAGER "lock.filemanager" +#define SETTING_LOCK_SETTINGS "lock.settings" +#define SETTING_LOCK_ADDONMANAGER "lock.addonmanager" + +CGUIDialogLockSettings::CGUIDialogLockSettings() + : CGUIDialogSettingsManualBase(WINDOW_DIALOG_LOCK_SETTINGS, "DialogSettings.xml"), + m_saveUserDetails(NULL) +{ } + +CGUIDialogLockSettings::~CGUIDialogLockSettings() = default; + +bool CGUIDialogLockSettings::ShowAndGetLock(LockType &lockMode, std::string &password, int header /* = 20091 */) +{ + CProfile::CLock locks(lockMode, password); + if (!ShowAndGetLock(locks, header, false, false)) + return false; + + locks.Validate(); + lockMode = locks.mode; + password = locks.code; + + return true; +} + +bool CGUIDialogLockSettings::ShowAndGetLock(CProfile::CLock &locks, int buttonLabel /* = 20091 */, bool conditional /* = false */, bool details /* = true */) +{ + CGUIDialogLockSettings *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogLockSettings>(WINDOW_DIALOG_LOCK_SETTINGS); + if (dialog == NULL) + return false; + + dialog->m_locks = locks; + dialog->m_buttonLabel = buttonLabel; + dialog->m_getUser = false; + dialog->m_conditionalDetails = conditional; + dialog->m_details = details; + dialog->Open(); + + if (!dialog->m_changed) + return false; + + locks = dialog->m_locks; + + // changed lock settings for certain sections (e.g. video, audio, or pictures) + // => refresh favourites due to possible visibility changes + CServiceBroker::GetFavouritesService().RefreshFavourites(); + + return true; +} + +bool CGUIDialogLockSettings::ShowAndGetUserAndPassword(std::string &user, std::string &password, const std::string &url, bool *saveUserDetails) +{ + CGUIDialogLockSettings *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogLockSettings>(WINDOW_DIALOG_LOCK_SETTINGS); + if (dialog == NULL) + return false; + + dialog->m_getUser = true; + dialog->m_locks.code = password; + dialog->m_user = user; + dialog->m_url = url; + dialog->m_saveUserDetails = saveUserDetails; + dialog->Open(); + + if (!dialog->m_changed) + return false; + + user = dialog->m_user; + password = dialog->m_locks.code; + return true; +} + +void CGUIDialogLockSettings::OnSettingChanged(const std::shared_ptr<const CSetting>& setting) +{ + if (setting == NULL) + return; + + CGUIDialogSettingsManualBase::OnSettingChanged(setting); + + const std::string &settingId = setting->GetId(); + if (settingId == SETTING_USERNAME) + m_user = std::static_pointer_cast<const CSettingString>(setting)->GetValue(); + else if (settingId == SETTING_PASSWORD) + m_locks.code = std::static_pointer_cast<const CSettingString>(setting)->GetValue(); + else if (settingId == SETTING_PASSWORD_REMEMBER) + *m_saveUserDetails = std::static_pointer_cast<const CSettingBool>(setting)->GetValue(); + else if (settingId == SETTING_LOCK_MUSIC) + m_locks.music = std::static_pointer_cast<const CSettingBool>(setting)->GetValue(); + else if (settingId == SETTING_LOCK_VIDEOS) + m_locks.video = std::static_pointer_cast<const CSettingBool>(setting)->GetValue(); + else if (settingId == SETTING_LOCK_PICTURES) + m_locks.pictures = std::static_pointer_cast<const CSettingBool>(setting)->GetValue(); + else if (settingId == SETTING_LOCK_PROGRAMS) + m_locks.programs = std::static_pointer_cast<const CSettingBool>(setting)->GetValue(); + else if (settingId == SETTING_LOCK_FILEMANAGER) + m_locks.files = std::static_pointer_cast<const CSettingBool>(setting)->GetValue(); + else if (settingId == SETTING_LOCK_SETTINGS) + m_locks.settings = static_cast<LOCK_LEVEL::SETTINGS_LOCK>(std::static_pointer_cast<const CSettingInt>(setting)->GetValue()); + else if (settingId == SETTING_LOCK_ADDONMANAGER) + m_locks.addonManager = std::static_pointer_cast<const CSettingBool>(setting)->GetValue(); + + m_changed = true; +} + +void CGUIDialogLockSettings::OnSettingAction(const std::shared_ptr<const CSetting>& setting) +{ + if (setting == NULL) + return; + + CGUIDialogSettingsManualBase::OnSettingAction(setting); + + const std::string &settingId = setting->GetId(); + if (settingId == SETTING_LOCKCODE) + { + CGUIDialogSelect* dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT); + if (!dialog) + return; + + dialog->Reset(); + dialog->SetHeading(CVariant{12360}); + dialog->Add(g_localizeStrings.Get(1223)); + dialog->Add(g_localizeStrings.Get(12337)); + dialog->Add(g_localizeStrings.Get(12338)); + dialog->Add(g_localizeStrings.Get(12339)); + dialog->SetSelected(GetLockModeLabel()); + dialog->Open(); + + std::string newPassword; + LockType iLockMode = LOCK_MODE_UNKNOWN; + bool bResult = false; + switch (dialog->GetSelectedItem()) + { + case 0: + iLockMode = LOCK_MODE_EVERYONE; //Disabled! Need check routine!!! + bResult = true; + break; + + case 1: + iLockMode = LOCK_MODE_NUMERIC; + bResult = CGUIDialogNumeric::ShowAndVerifyNewPassword(newPassword); + break; + + case 2: + iLockMode = LOCK_MODE_GAMEPAD; + bResult = CGUIDialogGamepad::ShowAndVerifyNewPassword(newPassword); + break; + + case 3: + iLockMode = LOCK_MODE_QWERTY; + bResult = CGUIKeyboardFactory::ShowAndVerifyNewPassword(newPassword); + break; + + default: + break; + } + + if (bResult) + { + if (iLockMode == LOCK_MODE_EVERYONE) + newPassword = "-"; + m_locks.code = newPassword; + if (m_locks.code == "-") + iLockMode = LOCK_MODE_EVERYONE; + m_locks.mode = iLockMode; + + SetSettingLockCodeLabel(); + SetDetailSettingsEnabled(m_locks.mode != LOCK_MODE_EVERYONE); + m_changed = true; + } + } +} + +void CGUIDialogLockSettings::OnCancel() +{ + m_changed = false; + + CGUIDialogSettingsManualBase::OnCancel(); +} + +void CGUIDialogLockSettings::SetupView() +{ + CGUIDialogSettingsManualBase::SetupView(); + + // set the title + if (m_getUser) + SetHeading(StringUtils::Format(g_localizeStrings.Get(20152), CURL::Decode(m_url))); + else + { + SetHeading(20066); + SetSettingLockCodeLabel(); + SetDetailSettingsEnabled(m_locks.mode != LOCK_MODE_EVERYONE); + } + SET_CONTROL_HIDDEN(CONTROL_SETTINGS_CUSTOM_BUTTON); + SET_CONTROL_LABEL(CONTROL_SETTINGS_OKAY_BUTTON, 186); + SET_CONTROL_LABEL(CONTROL_SETTINGS_CANCEL_BUTTON, 222); +} + +void CGUIDialogLockSettings::InitializeSettings() +{ + CGUIDialogSettingsManualBase::InitializeSettings(); + + const std::shared_ptr<CSettingCategory> category = AddCategory("locksettings", -1); + if (category == NULL) + { + CLog::Log(LOGERROR, "CGUIDialogLockSettings: unable to setup settings"); + return; + } + + const std::shared_ptr<CSettingGroup> group = AddGroup(category); + if (group == NULL) + { + CLog::Log(LOGERROR, "CGUIDialogLockSettings: unable to setup settings"); + return; + } + + if (m_getUser) + { + AddEdit(group, SETTING_USERNAME, 20142, SettingLevel::Basic, m_user); + AddEdit(group, SETTING_PASSWORD, 12326, SettingLevel::Basic, m_locks.code, false, true); + if (m_saveUserDetails != NULL) + AddToggle(group, SETTING_PASSWORD_REMEMBER, 13423, SettingLevel::Basic, *m_saveUserDetails); + + return; + } + + AddButton(group, SETTING_LOCKCODE, m_buttonLabel, SettingLevel::Basic); + + if (m_details) + { + const std::shared_ptr<CSettingGroup> groupDetails = AddGroup(category); + if (groupDetails == NULL) + { + CLog::Log(LOGERROR, "CGUIDialogLockSettings: unable to setup settings"); + return; + } + + AddToggle(groupDetails, SETTING_LOCK_MUSIC, 20038, SettingLevel::Basic, m_locks.music); + AddToggle(groupDetails, SETTING_LOCK_VIDEOS, 20039, SettingLevel::Basic, m_locks.video); + AddToggle(groupDetails, SETTING_LOCK_PICTURES, 20040, SettingLevel::Basic, m_locks.pictures); + AddToggle(groupDetails, SETTING_LOCK_PROGRAMS, 20041, SettingLevel::Basic, m_locks.programs); + AddToggle(groupDetails, SETTING_LOCK_FILEMANAGER, 20042, SettingLevel::Basic, m_locks.files); + + TranslatableIntegerSettingOptions settingsLevelOptions; + settingsLevelOptions.push_back(TranslatableIntegerSettingOption(106, LOCK_LEVEL::NONE)); + settingsLevelOptions.push_back(TranslatableIntegerSettingOption(593, LOCK_LEVEL::ALL)); + settingsLevelOptions.push_back(TranslatableIntegerSettingOption(10037, LOCK_LEVEL::STANDARD)); + settingsLevelOptions.push_back(TranslatableIntegerSettingOption(10038, LOCK_LEVEL::ADVANCED)); + settingsLevelOptions.push_back(TranslatableIntegerSettingOption(10039, LOCK_LEVEL::EXPERT)); + AddList(groupDetails, SETTING_LOCK_SETTINGS, 20043, SettingLevel::Basic, static_cast<int>(m_locks.settings), settingsLevelOptions, 20043); + + AddToggle(groupDetails, SETTING_LOCK_ADDONMANAGER, 24090, SettingLevel::Basic, m_locks.addonManager); + } + + m_changed = false; +} + +std::string CGUIDialogLockSettings::GetLockModeLabel() +{ + return g_localizeStrings.Get(m_locks.mode == LOCK_MODE_EVERYONE ? 1223 : 12336 + m_locks.mode); +} + +void CGUIDialogLockSettings::SetDetailSettingsEnabled(bool enabled) +{ + if (!m_details) + return; + + enabled |= !m_conditionalDetails; + GetSettingControl(SETTING_LOCK_MUSIC)->GetSetting()->SetEnabled(enabled); + GetSettingControl(SETTING_LOCK_VIDEOS)->GetSetting()->SetEnabled(enabled); + GetSettingControl(SETTING_LOCK_PICTURES)->GetSetting()->SetEnabled(enabled); + GetSettingControl(SETTING_LOCK_PROGRAMS)->GetSetting()->SetEnabled(enabled); + GetSettingControl(SETTING_LOCK_FILEMANAGER)->GetSetting()->SetEnabled(enabled); + GetSettingControl(SETTING_LOCK_SETTINGS)->GetSetting()->SetEnabled(enabled); + GetSettingControl(SETTING_LOCK_ADDONMANAGER)->GetSetting()->SetEnabled(enabled); +} + +void CGUIDialogLockSettings::SetSettingLockCodeLabel() +{ + // adjust label2 of the lock code setting button + if (m_locks.mode > LOCK_MODE_QWERTY) + m_locks.mode = LOCK_MODE_EVERYONE; + BaseSettingControlPtr settingControl = GetSettingControl(SETTING_LOCKCODE); + if (settingControl != NULL && settingControl->GetControl() != NULL) + SET_CONTROL_LABEL2(settingControl->GetID(), GetLockModeLabel()); +} diff --git a/xbmc/profiles/dialogs/GUIDialogLockSettings.h b/xbmc/profiles/dialogs/GUIDialogLockSettings.h new file mode 100644 index 0000000..bdcaed7 --- /dev/null +++ b/xbmc/profiles/dialogs/GUIDialogLockSettings.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "profiles/Profile.h" +#include "settings/dialogs/GUIDialogSettingsManualBase.h" + +class CGUIDialogLockSettings : public CGUIDialogSettingsManualBase +{ +public: + CGUIDialogLockSettings(); + ~CGUIDialogLockSettings() override; + + static bool ShowAndGetLock(LockType &lockMode, std::string &password, int header = 20091); + static bool ShowAndGetLock(CProfile::CLock &locks, int buttonLabel = 20091, bool conditional = false, bool details = true); + static bool ShowAndGetUserAndPassword(std::string &user, std::string &password, const std::string &url, bool *saveUserDetails); + +protected: + // implementations of ISettingCallback + void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override; + void OnSettingAction(const std::shared_ptr<const CSetting>& setting) override; + + // specialization of CGUIDialogSettingsBase + bool AllowResettingSettings() const override { return false; } + bool Save() override { return true; } + void OnCancel() override; + void SetupView() override; + + // specialization of CGUIDialogSettingsManualBase + void InitializeSettings() override; + +private: + std::string GetLockModeLabel(); + void SetDetailSettingsEnabled(bool enabled); + void SetSettingLockCodeLabel(); + + bool m_changed = false; + + CProfile::CLock m_locks; + std::string m_user; + std::string m_url; + bool m_details = true; + bool m_conditionalDetails = false; + bool m_getUser = false; + bool* m_saveUserDetails; + int m_buttonLabel = 20091; +}; diff --git a/xbmc/profiles/dialogs/GUIDialogProfileSettings.cpp b/xbmc/profiles/dialogs/GUIDialogProfileSettings.cpp new file mode 100644 index 0000000..d7ce0da --- /dev/null +++ b/xbmc/profiles/dialogs/GUIDialogProfileSettings.cpp @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "GUIDialogProfileSettings.h" + +#include "FileItem.h" +#include "GUIPassword.h" +#include "ServiceBroker.h" +#include "Util.h" +#include "dialogs/GUIDialogFileBrowser.h" +#include "dialogs/GUIDialogYesNo.h" +#include "filesystem/Directory.h" +#include "filesystem/File.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIKeyboardFactory.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "profiles/ProfileManager.h" +#include "profiles/dialogs/GUIDialogLockSettings.h" +#include "settings/SettingsComponent.h" +#include "settings/lib/Setting.h" +#include "settings/windows/GUIControlSettings.h" +#include "storage/MediaManager.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" +#include "utils/Variant.h" +#include "utils/log.h" + +#include <cassert> +#include <utility> + +#define SETTING_PROFILE_NAME "profile.name" +#define SETTING_PROFILE_IMAGE "profile.image" +#define SETTING_PROFILE_DIRECTORY "profile.directory" +#define SETTING_PROFILE_LOCKS "profile.locks" +#define SETTING_PROFILE_MEDIA "profile.media" +#define SETTING_PROFILE_MEDIA_SOURCES "profile.mediasources" + +CGUIDialogProfileSettings::CGUIDialogProfileSettings() + : CGUIDialogSettingsManualBase(WINDOW_DIALOG_PROFILE_SETTINGS, "DialogSettings.xml") +{ } + +CGUIDialogProfileSettings::~CGUIDialogProfileSettings() = default; + +bool CGUIDialogProfileSettings::ShowForProfile(unsigned int iProfile, bool firstLogin) +{ + const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager(); + + if (firstLogin && iProfile > profileManager->GetNumberOfProfiles()) + return false; + + CGUIDialogProfileSettings *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogProfileSettings>(WINDOW_DIALOG_PROFILE_SETTINGS); + if (dialog == NULL) + return false; + + dialog->m_needsSaving = false; + dialog->m_isDefault = iProfile == 0; + dialog->m_showDetails = !firstLogin; + + const CProfile *profile = profileManager->GetProfile(iProfile); + if (profile == NULL) + { + dialog->m_name.clear(); + dialog->m_dbMode = 2; + dialog->m_sourcesMode = 2; + dialog->m_locks = CProfile::CLock(); + + bool bLock = profileManager->GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser; + dialog->m_locks.addonManager = bLock; + dialog->m_locks.settings = (bLock) ? LOCK_LEVEL::ALL : LOCK_LEVEL::NONE; + dialog->m_locks.files = bLock; + + dialog->m_directory.clear(); + dialog->m_thumb.clear(); + + // prompt for a name + std::string profileName; + if (!CGUIKeyboardFactory::ShowAndGetInput(profileName, CVariant{g_localizeStrings.Get(20093)}, false) || profileName.empty()) + return false; + dialog->m_name = profileName; + + // create a default path + std::string defaultDir = URIUtils::AddFileToFolder("profiles", CUtil::MakeLegalFileName(dialog->m_name)); + URIUtils::AddSlashAtEnd(defaultDir); + XFILE::CDirectory::Create(URIUtils::AddFileToFolder("special://masterprofile/", defaultDir)); + + // prompt for the user to change it if they want + std::string userDir = defaultDir; + if (GetProfilePath(userDir, false)) // can't be the master user + { + if (!URIUtils::PathHasParent(userDir, defaultDir)) // user chose a different folder + XFILE::CDirectory::Remove(URIUtils::AddFileToFolder("special://masterprofile/", defaultDir)); + } + dialog->m_directory = userDir; + dialog->m_needsSaving = true; + } + else + { + dialog->m_name = profile->getName(); + dialog->m_thumb = profile->getThumb(); + dialog->m_directory = profile->getDirectory(); + dialog->m_dbMode = profile->canWriteDatabases() ? 0 : 1; + if (profile->hasDatabases()) + dialog->m_dbMode += 2; + dialog->m_sourcesMode = profile->canWriteSources() ? 0 : 1; + if (profile->hasSources()) + dialog->m_sourcesMode += 2; + + dialog->m_locks = profile->GetLocks(); + } + + dialog->Open(); + if (dialog->m_needsSaving) + { + if (iProfile >= profileManager->GetNumberOfProfiles()) + { + if (dialog->m_name.empty() || dialog->m_directory.empty()) + return false; + + /*std::string strLabel; + strLabel.Format(g_localizeStrings.Get(20047),dialog->m_strName); + if (!CGUIDialogYesNo::ShowAndGetInput(20058, strLabel, dialog->m_strDirectory, "")) + { + CDirectory::Remove(URIUtils::AddFileToFolder(profileManager.GetUserDataFolder(), dialog->m_strDirectory)); + return false; + }*/ + + // check for old profile settings + CProfile profile(dialog->m_directory, dialog->m_name, profileManager->GetNextProfileId()); + profileManager->AddProfile(profile); + bool exists = XFILE::CFile::Exists(URIUtils::AddFileToFolder("special://masterprofile/", dialog->m_directory, "guisettings.xml")); + + if (exists && !CGUIDialogYesNo::ShowAndGetInput(CVariant{20058}, CVariant{20104})) + exists = false; + + if (!exists) + { + // copy masterprofile guisettings to new profile guisettings + // If the user selects 'start fresh', do nothing as a fresh + // guisettings.xml will be created on first profile use. + if (CGUIDialogYesNo::ShowAndGetInput(CVariant{20058}, CVariant{20048}, CVariant{""}, CVariant{""}, CVariant{20044}, CVariant{20064})) + { + XFILE::CFile::Copy(URIUtils::AddFileToFolder("special://masterprofile/", "guisettings.xml"), + URIUtils::AddFileToFolder("special://masterprofile/", dialog->m_directory, "guisettings.xml")); + } + } + + exists = XFILE::CFile::Exists(URIUtils::AddFileToFolder("special://masterprofile/", dialog->m_directory, "sources.xml")); + if (exists && !CGUIDialogYesNo::ShowAndGetInput(CVariant{20058}, CVariant{20106})) + exists = false; + + if (!exists) + { + if ((dialog->m_sourcesMode & 2) == 2) + // prompt user to copy masterprofile's sources.xml file + // If 'start fresh' (no) is selected, do nothing. + if (CGUIDialogYesNo::ShowAndGetInput(CVariant{20058}, CVariant{20071}, CVariant{""}, CVariant{""}, CVariant{20044}, CVariant{20064})) + { + XFILE::CFile::Copy(URIUtils::AddFileToFolder("special://masterprofile/", "sources.xml"), + URIUtils::AddFileToFolder("special://masterprofile/", dialog->m_directory, "sources.xml")); + } + } + } + + /*if (!dialog->m_bIsNewUser) + if (!CGUIDialogYesNo::ShowAndGetInput(20067, 20103)) + return false;*/ + + CProfile *profile = profileManager->GetProfile(iProfile); + assert(profile); + profile->setName(dialog->m_name); + profile->setDirectory(dialog->m_directory); + profile->setThumb(dialog->m_thumb); + profile->setWriteDatabases(!((dialog->m_dbMode & 1) == 1)); + profile->setWriteSources(!((dialog->m_sourcesMode & 1) == 1)); + profile->setDatabases((dialog->m_dbMode & 2) == 2); + profile->setSources((dialog->m_sourcesMode & 2) == 2); + profile->SetLocks(dialog->m_locks); + profileManager->Save(); + + return true; + } + + return dialog->m_needsSaving; +} + +void CGUIDialogProfileSettings::OnWindowLoaded() +{ + CGUIDialogSettingsManualBase::OnWindowLoaded(); +} + +void CGUIDialogProfileSettings::OnSettingChanged(const std::shared_ptr<const CSetting>& setting) +{ + if (setting == NULL) + return; + + CGUIDialogSettingsManualBase::OnSettingChanged(setting); + + const std::string &settingId = setting->GetId(); + if (settingId == SETTING_PROFILE_NAME) + { + m_name = std::static_pointer_cast<const CSettingString>(setting)->GetValue(); + } + else if (settingId == SETTING_PROFILE_MEDIA) + m_dbMode = std::static_pointer_cast<const CSettingInt>(setting)->GetValue(); + else if (settingId == SETTING_PROFILE_MEDIA_SOURCES) + m_sourcesMode = std::static_pointer_cast<const CSettingInt>(setting)->GetValue(); + + m_needsSaving = true; +} + +void CGUIDialogProfileSettings::OnSettingAction(const std::shared_ptr<const CSetting>& setting) +{ + if (setting == NULL) + return; + + CGUIDialogSettingsManualBase::OnSettingAction(setting); + + const std::string &settingId = setting->GetId(); + if (settingId == SETTING_PROFILE_IMAGE) + { + VECSOURCES shares; + CServiceBroker::GetMediaManager().GetLocalDrives(shares); + + CFileItemList items; + if (!m_thumb.empty()) + { + CFileItemPtr item(new CFileItem("thumb://Current", false)); + item->SetArt("thumb", m_thumb); + item->SetLabel(g_localizeStrings.Get(20016)); + items.Add(item); + } + + CFileItemPtr item(new CFileItem("thumb://None", false)); + item->SetArt("thumb", "DefaultUser.png"); + item->SetLabel(g_localizeStrings.Get(20018)); + items.Add(item); + + std::string thumb; + if (CGUIDialogFileBrowser::ShowAndGetImage(items, shares, g_localizeStrings.Get(1030), thumb) && + !StringUtils::EqualsNoCase(thumb, "thumb://Current")) + { + m_needsSaving = true; + m_thumb = StringUtils::EqualsNoCase(thumb, "thumb://None") ? "" : thumb; + + UpdateProfileImage(); + } + } + else if (settingId == SETTING_PROFILE_DIRECTORY) + { + if (!GetProfilePath(m_directory, m_isDefault)) + return; + + m_needsSaving = true; + updateProfileDirectory(); + } + else if (settingId == SETTING_PROFILE_LOCKS) + { + if (m_showDetails) + { + const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager(); + + if (profileManager->GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE && !m_isDefault) + { + if (CGUIDialogYesNo::ShowAndGetInput(CVariant{20066}, CVariant{20118})) + g_passwordManager.SetMasterLockMode(false); + if (profileManager->GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE) + return; + } + if (CGUIDialogLockSettings::ShowAndGetLock(m_locks, m_isDefault ? 12360 : 20068, + profileManager->GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE || m_isDefault)) + m_needsSaving = true; + } + else + { + if (CGUIDialogLockSettings::ShowAndGetLock(m_locks, m_isDefault ? 12360 : 20068, false, false)) + m_needsSaving = true; + } + } +} + +void CGUIDialogProfileSettings::OnCancel() +{ + m_needsSaving = false; + + CGUIDialogSettingsManualBase::OnCancel(); +} + +void CGUIDialogProfileSettings::SetupView() +{ + CGUIDialogSettingsManualBase::SetupView(); + + // set the heading + SetHeading(!m_showDetails ? 20255 : 20067); + + SET_CONTROL_HIDDEN(CONTROL_SETTINGS_CUSTOM_BUTTON); + SET_CONTROL_LABEL(CONTROL_SETTINGS_OKAY_BUTTON, 186); + SET_CONTROL_LABEL(CONTROL_SETTINGS_CANCEL_BUTTON, 222); + + // set the profile image and directory + UpdateProfileImage(); + updateProfileDirectory(); +} + +void CGUIDialogProfileSettings::InitializeSettings() +{ + CGUIDialogSettingsManualBase::InitializeSettings(); + + const std::shared_ptr<CSettingCategory> category = AddCategory("profilesettings", -1); + if (category == NULL) + { + CLog::Log(LOGERROR, "CGUIDialogProfileSettings: unable to setup settings"); + return; + } + + const std::shared_ptr<CSettingGroup> group = AddGroup(category); + if (group == NULL) + { + CLog::Log(LOGERROR, "CGUIDialogProfileSettings: unable to setup settings"); + return; + } + + const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager(); + + AddEdit(group, SETTING_PROFILE_NAME, 20093, SettingLevel::Basic, m_name); + AddButton(group, SETTING_PROFILE_IMAGE, 20065, SettingLevel::Basic); + + if (!m_isDefault && m_showDetails) + AddButton(group, SETTING_PROFILE_DIRECTORY, 20070, SettingLevel::Basic); + + if (m_showDetails || + (m_locks.mode == LOCK_MODE_EVERYONE && profileManager->GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE)) + AddButton(group, SETTING_PROFILE_LOCKS, 20066, SettingLevel::Basic); + + if (!m_isDefault && m_showDetails) + { + const std::shared_ptr<CSettingGroup> groupMedia = AddGroup(category); + if (groupMedia == NULL) + { + CLog::Log(LOGERROR, "CGUIDialogProfileSettings: unable to setup settings"); + return; + } + + TranslatableIntegerSettingOptions entries; + entries.push_back(TranslatableIntegerSettingOption(20062, 0)); + entries.push_back(TranslatableIntegerSettingOption(20063, 1)); + entries.push_back(TranslatableIntegerSettingOption(20061, 2)); + if (profileManager->GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE) + entries.push_back(TranslatableIntegerSettingOption(20107, 3)); + + AddSpinner(groupMedia, SETTING_PROFILE_MEDIA, 20060, SettingLevel::Basic, m_dbMode, entries); + AddSpinner(groupMedia, SETTING_PROFILE_MEDIA_SOURCES, 20094, SettingLevel::Basic, m_sourcesMode, entries); + } +} + +bool CGUIDialogProfileSettings::GetProfilePath(std::string &directory, bool isDefault) +{ + VECSOURCES shares; + CMediaSource share; + share.strName = g_localizeStrings.Get(13200); + share.strPath = "special://masterprofile/profiles/"; + shares.push_back(share); + + std::string strDirectory; + if (directory.empty()) + strDirectory = share.strPath; + else + strDirectory = URIUtils::AddFileToFolder("special://masterprofile/", directory); + + if (!CGUIDialogFileBrowser::ShowAndGetDirectory(shares, g_localizeStrings.Get(657), strDirectory, true)) + return false; + + directory = strDirectory; + if (!isDefault) + directory.erase(0, 24); + + return true; +} + +void CGUIDialogProfileSettings::UpdateProfileImage() +{ + BaseSettingControlPtr settingControl = GetSettingControl(SETTING_PROFILE_IMAGE); + if (settingControl != NULL && settingControl->GetControl() != NULL) + SET_CONTROL_LABEL2(settingControl->GetID(), URIUtils::GetFileName(m_thumb)); +} + +void CGUIDialogProfileSettings::updateProfileDirectory() +{ + BaseSettingControlPtr settingControl = GetSettingControl(SETTING_PROFILE_DIRECTORY); + if (settingControl != NULL && settingControl->GetControl() != NULL) + SET_CONTROL_LABEL2(settingControl->GetID(), m_directory); +} diff --git a/xbmc/profiles/dialogs/GUIDialogProfileSettings.h b/xbmc/profiles/dialogs/GUIDialogProfileSettings.h new file mode 100644 index 0000000..5b6f14e --- /dev/null +++ b/xbmc/profiles/dialogs/GUIDialogProfileSettings.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "profiles/Profile.h" +#include "settings/dialogs/GUIDialogSettingsManualBase.h" + +#include <string> + +class CGUIDialogProfileSettings : public CGUIDialogSettingsManualBase +{ +public: + CGUIDialogProfileSettings(); + ~CGUIDialogProfileSettings() override; + + static bool ShowForProfile(unsigned int iProfile, bool firstLogin = false); + +protected: + // specializations of CGUIWindow + void OnWindowLoaded() override; + + // implementations of ISettingCallback + void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override; + void OnSettingAction(const std::shared_ptr<const CSetting>& setting) override; + + // specialization of CGUIDialogSettingsBase + bool AllowResettingSettings() const override { return false; } + bool Save() override { return true; } + void OnCancel() override; + void SetupView() override; + + // specialization of CGUIDialogSettingsManualBase + void InitializeSettings() override; + + /*! \brief Prompt for a change in profile path + \param directory Current directory for the profile, new profile directory will be returned here + \param isDefault whether this is the default profile or not + \return true if the profile path has been changed, false otherwise. + */ + static bool GetProfilePath(std::string &directory, bool isDefault); + + void UpdateProfileImage(); + void updateProfileDirectory(); + + bool m_needsSaving = false; + std::string m_name; + std::string m_thumb; + std::string m_directory; + int m_sourcesMode; + int m_dbMode; + bool m_isDefault; + bool m_isNewUser; + bool m_showDetails; + + CProfile::CLock m_locks; +}; diff --git a/xbmc/profiles/windows/CMakeLists.txt b/xbmc/profiles/windows/CMakeLists.txt new file mode 100644 index 0000000..232d0aa --- /dev/null +++ b/xbmc/profiles/windows/CMakeLists.txt @@ -0,0 +1,5 @@ +set(SOURCES GUIWindowSettingsProfile.cpp) + +set(HEADERS GUIWindowSettingsProfile.h) + +core_add_library(profiles_windows) diff --git a/xbmc/profiles/windows/GUIWindowSettingsProfile.cpp b/xbmc/profiles/windows/GUIWindowSettingsProfile.cpp new file mode 100644 index 0000000..05f1c09 --- /dev/null +++ b/xbmc/profiles/windows/GUIWindowSettingsProfile.cpp @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "GUIWindowSettingsProfile.h" + +#include "FileItem.h" +#include "ServiceBroker.h" +#include "dialogs/GUIDialogContextMenu.h" +#include "dialogs/GUIDialogSelect.h" +#include "filesystem/Directory.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIMessage.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "input/Key.h" +#include "messaging/ApplicationMessenger.h" +#include "profiles/Profile.h" +#include "profiles/ProfileManager.h" +#include "profiles/dialogs/GUIDialogProfileSettings.h" +#include "settings/SettingsComponent.h" +#include "utils/URIUtils.h" +#include "utils/Variant.h" +#include "windows/GUIWindowFileManager.h" + +using namespace XFILE; + +#define CONTROL_PROFILES 2 +#define CONTROL_LOGINSCREEN 4 +#define CONTROL_AUTOLOGIN 5 + +CGUIWindowSettingsProfile::CGUIWindowSettingsProfile(void) + : CGUIWindow(WINDOW_SETTINGS_PROFILES, "SettingsProfile.xml") +{ + m_listItems = new CFileItemList; + m_loadType = KEEP_IN_MEMORY; +} + +CGUIWindowSettingsProfile::~CGUIWindowSettingsProfile(void) +{ + delete m_listItems; +} + +int CGUIWindowSettingsProfile::GetSelectedItem() +{ + CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_PROFILES); + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg); + + return msg.GetParam1(); +} + +void CGUIWindowSettingsProfile::OnPopupMenu(int iItem) +{ + const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager(); + + if (iItem == (int)profileManager->GetNumberOfProfiles()) + return; + + // popup the context menu + CContextButtons choices; + choices.Add(1, 20092); // Load profile + if (iItem > 0) + choices.Add(2, 117); // Delete + + int choice = CGUIDialogContextMenu::ShowAndGetChoice(choices); + if (choice == 1) + { + CServiceBroker::GetAppMessenger()->PostMsg(TMSG_LOADPROFILE, iItem); + return; + } + + if (choice == 2) + { + if (profileManager->DeleteProfile(iItem)) + iItem--; + } + + LoadList(); + CGUIMessage msg(GUI_MSG_ITEM_SELECT, GetID(),CONTROL_PROFILES,iItem); + OnMessage(msg); +} + +bool CGUIWindowSettingsProfile::OnMessage(CGUIMessage& message) +{ + switch ( message.GetMessage() ) + { + case GUI_MSG_WINDOW_DEINIT: + { + CGUIWindow::OnMessage(message); + ClearListItems(); + return true; + } + break; + + case GUI_MSG_CLICKED: + { + int iControl = message.GetSenderId(); + if (iControl == CONTROL_PROFILES) + { + int iAction = message.GetParam1(); + if ( + iAction == ACTION_SELECT_ITEM || + iAction == ACTION_MOUSE_LEFT_CLICK || + iAction == ACTION_CONTEXT_MENU || + iAction == ACTION_MOUSE_RIGHT_CLICK + ) + { + const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager(); + + CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_PROFILES); + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg); + int iItem = msg.GetParam1(); + if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK) + { + //contextmenu + if (iItem <= static_cast<int>(profileManager->GetNumberOfProfiles()) - 1) + { + OnPopupMenu(iItem); + } + return true; + } + else if (iItem < static_cast<int>(profileManager->GetNumberOfProfiles())) + { + if (CGUIDialogProfileSettings::ShowForProfile(iItem)) + { + LoadList(); + CGUIMessage msg(GUI_MSG_ITEM_SELECT, GetID(), 2,iItem); + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg); + + return true; + } + + return false; + } + else if (iItem > static_cast<int>(profileManager->GetNumberOfProfiles()) - 1) + { + CDirectory::Create(URIUtils::AddFileToFolder(profileManager->GetUserDataFolder(),"profiles")); + if (CGUIDialogProfileSettings::ShowForProfile(profileManager->GetNumberOfProfiles())) + { + LoadList(); + CGUIMessage msg(GUI_MSG_ITEM_SELECT, GetID(), 2,iItem); + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg); + return true; + } + + return false; + } + } + } + else if (iControl == CONTROL_LOGINSCREEN) + { + const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager(); + + profileManager->ToggleLoginScreen(); + profileManager->Save(); + return true; + } + else if (iControl == CONTROL_AUTOLOGIN) + { + const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager(); + + int currentId = profileManager->GetAutoLoginProfileId(); + int profileId; + if (GetAutoLoginProfileChoice(profileId) && (currentId != profileId)) + { + profileManager->SetAutoLoginProfileId(profileId); + profileManager->Save(); + } + return true; + } + } + break; + } + + return CGUIWindow::OnMessage(message); +} + +void CGUIWindowSettingsProfile::LoadList() +{ + ClearListItems(); + + const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager(); + + for (unsigned int i = 0; i < profileManager->GetNumberOfProfiles(); i++) + { + const CProfile *profile = profileManager->GetProfile(i); + CFileItemPtr item(new CFileItem(profile->getName())); + item->SetLabel2(profile->getDate()); + item->SetArt("thumb", profile->getThumb()); + item->SetOverlayImage(profile->getLockMode() == LOCK_MODE_EVERYONE ? CGUIListItem::ICON_OVERLAY_NONE : CGUIListItem::ICON_OVERLAY_LOCKED); + m_listItems->Add(item); + } + { + CFileItemPtr item(new CFileItem(g_localizeStrings.Get(20058))); + m_listItems->Add(item); + } + CGUIMessage msg(GUI_MSG_LABEL_BIND, GetID(), CONTROL_PROFILES, 0, 0, m_listItems); + OnMessage(msg); + + if (profileManager->UsingLoginScreen()) + { + CONTROL_SELECT(CONTROL_LOGINSCREEN); + } + else + { + CONTROL_DESELECT(CONTROL_LOGINSCREEN); + } +} + +void CGUIWindowSettingsProfile::ClearListItems() +{ + CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_PROFILES); + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg); + + m_listItems->Clear(); +} + +void CGUIWindowSettingsProfile::OnInitWindow() +{ + LoadList(); + CGUIWindow::OnInitWindow(); +} + +bool CGUIWindowSettingsProfile::GetAutoLoginProfileChoice(int &iProfile) +{ + CGUIDialogSelect *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT); + if (!dialog) return false; + + const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager(); + + // add items + // "Last used profile" option comes first, so up indices by 1 + int autoLoginProfileId = profileManager->GetAutoLoginProfileId() + 1; + CFileItemList items; + CFileItemPtr item(new CFileItem()); + item->SetLabel(g_localizeStrings.Get(37014)); // Last used profile + item->SetArt("icon", "DefaultUser.png"); + items.Add(item); + + for (unsigned int i = 0; i < profileManager->GetNumberOfProfiles(); i++) + { + const CProfile *profile = profileManager->GetProfile(i); + const std::string& locked = g_localizeStrings.Get(profile->getLockMode() > 0 ? 20166 : 20165); + CFileItemPtr item(new CFileItem(profile->getName())); + item->SetLabel2(locked); // lock setting + std::string thumb = profile->getThumb(); + if (thumb.empty()) + thumb = "DefaultUser.png"; + item->SetArt("icon", thumb); + items.Add(item); + } + + dialog->SetHeading(CVariant{20093}); // Profile name + dialog->Reset(); + dialog->SetUseDetails(true); + dialog->SetItems(items); + dialog->SetSelected(autoLoginProfileId); + dialog->Open(); + + if (dialog->IsButtonPressed() || dialog->GetSelectedItem() < 0) + return false; // user cancelled + iProfile = dialog->GetSelectedItem() - 1; + + return true; +} diff --git a/xbmc/profiles/windows/GUIWindowSettingsProfile.h b/xbmc/profiles/windows/GUIWindowSettingsProfile.h new file mode 100644 index 0000000..0057305 --- /dev/null +++ b/xbmc/profiles/windows/GUIWindowSettingsProfile.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "guilib/GUIWindow.h" + +class CFileItemList; + +class CGUIWindowSettingsProfile : + public CGUIWindow +{ +public: + CGUIWindowSettingsProfile(void); + ~CGUIWindowSettingsProfile(void) override; + bool OnMessage(CGUIMessage& message) override; + +protected: + void OnInitWindow() override; + CFileItemList *m_listItems; + + void OnPopupMenu(int iItem); + void DoRename(int iItem); + void DoOverwrite(int iItem); + int GetSelectedItem(); + void LoadList(); + void SetLastLoaded(); + void ClearListItems(); + bool GetAutoLoginProfileChoice(int &iProfile); +}; |