QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
AppSettings.cc
Go to the documentation of this file.
1#include "AppSettings.h"
2#include "QGCFileHelper.h"
3#include "QGCPalette.h"
4#include "AppMessages.h"
5#include "QGCApplication.h"
6#include "QGCMAVLink.h"
7#include "LinkManager.h"
8
9#ifdef Q_OS_ANDROID
10#include "AndroidInterface.h"
11#endif
12
13#include <QtCore/QStandardPaths>
14#include <QtCore/QDir>
15#include <QtCore/QSettings>
16
17// Release languages are 90%+ complete
18QList<QLocale::Language> AppSettings::_rgReleaseLanguages = {
19 QLocale::English,
20 QLocale::Azerbaijani,
21 QLocale::Chinese,
22 QLocale::Japanese,
23 QLocale::Korean,
24 QLocale::Portuguese,
25 QLocale::Russian,
26};
27
28// Partial languages are 40%+ complete
29QList<QLocale::Language> AppSettings::_rgPartialLanguages = {
30 QLocale::Ukrainian,
31};
32
33AppSettings::LanguageInfo_t AppSettings::_rgLanguageInfo[] = {
34 { QLocale::AnyLanguage, "System" }, // Must be first
35 { QLocale::Azerbaijani, "Azerbaijani (Azerbaijani)" },
36 { QLocale::Bulgarian, "български (Bulgarian)" },
37 { QLocale::Chinese, "中文 (Chinese)" },
38 { QLocale::Dutch, "Nederlands (Dutch)" },
39 { QLocale::English, "English" },
40 { QLocale::Finnish, "Suomi (Finnish)" },
41 { QLocale::French, "Français (French)" },
42 { QLocale::German, "Deutsche (German)" },
43 { QLocale::Greek, "Ελληνικά (Greek)" },
44 { QLocale::Hebrew, "עברית (Hebrew)" },
45 { QLocale::Italian, "Italiano (Italian)" },
46 { QLocale::Japanese, "日本語 (Japanese)" },
47 { QLocale::Korean, "한국어 (Korean)" },
48 { QLocale::NorwegianBokmal, "Norsk (Norwegian)" },
49 { QLocale::Polish, "Polskie (Polish)" },
50 { QLocale::Portuguese, "Português (Portuguese)" },
51 { QLocale::Russian, "Pусский (Russian)" },
52 { QLocale::Spanish, "Español (Spanish)" },
53 { QLocale::Swedish, "Svenska (Swedish)" },
54 { QLocale::Turkish, "Türk (Turkish)" }
55};
56
58{
59 QGCPalette::setGlobalTheme(indoorPalette()->rawValue().toBool() ? QGCPalette::Dark : QGCPalette::Light);
60
61 // Instantiate savePath so we can check for override and setup default path if needed
62
63 SettingsFact* savePathFact = qobject_cast<SettingsFact*>(savePath());
64 QString appName = QCoreApplication::applicationName();
65#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
66 // Mobile builds always use the runtime generated location for savePath.
67 bool userHasModifiedSavePath = false;
68#else
69 bool userHasModifiedSavePath = !savePathFact->rawValue().toString().isEmpty() || !_nameToMetaDataMap[savePathName]->rawDefaultValue().toString().isEmpty();
70#endif
71
72 if (!userHasModifiedSavePath) {
73#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
74 #ifdef Q_OS_IOS
75 // This will expose the directories directly to the File iOs app
76 QDir rootDir = QDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation));
77 savePathFact->setRawValue(rootDir.absolutePath());
78 #else
79 QString rootDirPath;
80 #ifdef Q_OS_ANDROID
81 if (!androidDontSaveToSDCard()->rawValue().toBool()) {
82 rootDirPath = AndroidInterface::getSDCardPath();
83 qDebug() << "AndroidInterface::getSDCardPath();" << rootDirPath;
84 if (rootDirPath.isEmpty() || !QDir(rootDirPath).exists()) {
85 rootDirPath.clear();
86 qDebug() << "Save to SD card specified for application data. But no SD card present or permissions not granted. Using internal storage.";
87 } else if (!QFileInfo(rootDirPath).isWritable()) {
88 rootDirPath.clear();
89 QGC::showAppMessage(AppSettings::tr("Save to SD card specified for application data. But SD card is write protected. Using internal storage."));
90 }
91 }
92 #endif
93 if (rootDirPath.isEmpty()) {
94 rootDirPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
95 }
96 savePathFact->setRawValue(QDir(rootDirPath).filePath(appName));
97 #endif
98 savePathFact->setUserVisible(false);
99#else
100 QDir rootDir;
101 if (QGC::runningUnitTests() || qgcApp()->simpleBootTest()) {
102 rootDir = QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
103 } else {
104 rootDir = QDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation));
105 }
106 savePathFact->setRawValue(rootDir.filePath(appName));
107#endif
108 }
109
110 connect(savePathFact, &Fact::rawValueChanged, this, &AppSettings::savePathsChanged);
111 connect(savePathFact, &Fact::rawValueChanged, this, &AppSettings::_checkSavePathDirectories);
112
113 _checkSavePathDirectories();
114
115 // When a specific preferred firmware/vehicle is chosen, keep the offline editing settings in sync
116 connect(preferredFirmwareClass(), &Fact::rawValueChanged, this, [this](QVariant value) {
117 if (value.toUInt() != 0) {
118 offlineEditingFirmwareClass()->setRawValue(value);
119 }
120 });
121 connect(preferredVehicleClass(), &Fact::rawValueChanged, this, [this](QVariant value) {
122 if (value.toUInt() != 0) {
123 offlineEditingVehicleClass()->setRawValue(value);
124 }
125 });
126}
127
128DECLARE_SETTINGSFACT(AppSettings, preferredFirmwareClass)
129DECLARE_SETTINGSFACT(AppSettings, preferredVehicleClass)
130DECLARE_SETTINGSFACT(AppSettings, offlineEditingFirmwareClass)
131DECLARE_SETTINGSFACT(AppSettings, offlineEditingVehicleClass)
132DECLARE_SETTINGSFACT(AppSettings, offlineEditingCruiseSpeed)
133DECLARE_SETTINGSFACT(AppSettings, offlineEditingHoverSpeed)
134DECLARE_SETTINGSFACT(AppSettings, offlineEditingAscentSpeed)
135DECLARE_SETTINGSFACT(AppSettings, offlineEditingDescentSpeed)
136DECLARE_SETTINGSFACT(AppSettings, batteryPercentRemainingAnnounce)
137DECLARE_SETTINGSFACT(AppSettings, defaultMissionItemAltitude)
140DECLARE_SETTINGSFACT(AppSettings, virtualJoystick)
141DECLARE_SETTINGSFACT(AppSettings, virtualJoystickAutoCenterThrottle)
142DECLARE_SETTINGSFACT(AppSettings, virtualJoystickLeftHandedMode)
143DECLARE_SETTINGSFACT(AppSettings, uiScalePercent)
145DECLARE_SETTINGSFACT(AppSettings, androidDontSaveToSDCard)
147DECLARE_SETTINGSFACT(AppSettings, enforceChecklist)
148DECLARE_SETTINGSFACT(AppSettings, enableMultiVehiclePanel)
149DECLARE_SETTINGSFACT(AppSettings, tiandituToken)
151DECLARE_SETTINGSFACT(AppSettings, mapboxAccount)
157DECLARE_SETTINGSFACT(AppSettings, gstDebugLevel)
159DECLARE_SETTINGSFACT(AppSettings, clearSettingsNextBoot)
160DECLARE_SETTINGSFACT(AppSettings, disableAllPersistence)
161DECLARE_SETTINGSFACT(AppSettings, firstRunPromptIdsShown)
162DECLARE_SETTINGSFACT(AppSettings, favoriteParameters)
163DECLARE_SETTINGSFACT(AppSettings, remoteLoggingEnabled)
164DECLARE_SETTINGSFACT(AppSettings, remoteLoggingHost)
165DECLARE_SETTINGSFACT(AppSettings, remoteLoggingPort)
166DECLARE_SETTINGSFACT(AppSettings, remoteLoggingProtocol)
167DECLARE_SETTINGSFACT(AppSettings, remoteLoggingVehicleId)
168DECLARE_SETTINGSFACT(AppSettings, remoteLoggingTlsEnabled)
169DECLARE_SETTINGSFACT(AppSettings, remoteLoggingTlsVerifyPeer)
170DECLARE_SETTINGSFACT(AppSettings, remoteLoggingCompressionEnabled)
171DECLARE_SETTINGSFACT(AppSettings, remoteLoggingCompressionLevel)
172
174{
175 if (!_indoorPaletteFact) {
176 _indoorPaletteFact = _createSettingsFact(indoorPaletteName);
177 connect(_indoorPaletteFact, &Fact::rawValueChanged, this, &AppSettings::_indoorPaletteChanged);
178 }
179 return _indoorPaletteFact;
180}
181
183{
184 if (!_qLocaleLanguageFact) {
185 _qLocaleLanguageFact = _createSettingsFact(qLocaleLanguageName);
186 connect(_qLocaleLanguageFact, &Fact::rawValueChanged, this, &AppSettings::_qLocaleLanguageChanged);
187
188 FactMetaData* metaData = _qLocaleLanguageFact->metaData();
189 QStringList rgEnumStrings;
190 QVariantList rgEnumValues;
191
192 // System is always an available selection
193 rgEnumStrings.append(_rgLanguageInfo[0].languageName);
194 rgEnumValues.append(_rgLanguageInfo[0].languageId);
195
196 for (const auto& languageInfo: _rgLanguageInfo) {
197 if (_rgReleaseLanguages.contains(languageInfo.languageId)) {
198 rgEnumStrings.append(languageInfo.languageName);
199 rgEnumValues.append(languageInfo.languageId);
200 }
201 }
202 for (const auto& languageInfo: _rgLanguageInfo) {
203 if (_rgPartialLanguages.contains(languageInfo.languageId)) {
204 rgEnumStrings.append(QString(languageInfo.languageName) + AppSettings::tr(" (Partial)"));
205 rgEnumValues.append(languageInfo.languageId);
206 }
207 }
208#ifdef QGC_DAILY_BUILD
209 // Only daily builds include full set of languages for testing purposes
210 for (const auto& languageInfo: _rgLanguageInfo) {
211 if (!_rgReleaseLanguages.contains(languageInfo.languageId) && !_rgPartialLanguages.contains(languageInfo.languageId)) {
212 rgEnumStrings.append(QString(languageInfo.languageName) + AppSettings::tr(" (Test Only)"));
213 rgEnumValues.append(languageInfo.languageId);
214 }
215 }
216#endif
217#ifdef QT_DEBUG
218 // Debug builds include pseudo-localization for UI layout testing
219 rgEnumStrings.append(AppSettings::tr("Pseudo Localization (Test Only)"));
220 rgEnumValues.append(QLocale::Esperanto);
221#endif
222 metaData->setEnumInfo(rgEnumStrings, rgEnumValues);
223
224 if (_qLocaleLanguageFact->enumIndex() == -1) {
225 _qLocaleLanguageFact->setRawValue(QLocale::AnyLanguage);
226 }
227 }
228 return _qLocaleLanguageFact;
229}
230
231void AppSettings::_qLocaleLanguageChanged()
232{
233 qgcApp()->setLanguage();
234}
235
236void AppSettings::_checkSavePathDirectories(void)
237{
238 const QString savePath = this->savePath()->rawValue().toString();
240 // Create all subdirectories
250 }
251}
252
253QString AppSettings::_childSavePath(const char* directory)
254{
255 const QString rootPath = savePath()->rawValue().toString();
256 if (rootPath.isEmpty()) {
257 return QString();
258 }
259
260 const QDir rootDir(rootPath);
261 if (!rootDir.exists()) {
262 return QString();
263 }
264
265 return rootDir.filePath(directory);
266}
267
268void AppSettings::_indoorPaletteChanged(void)
269{
270 QGCPalette::setGlobalTheme(indoorPalette()->rawValue().toBool() ? QGCPalette::Dark : QGCPalette::Light);
271}
272
274{
275 return _childSavePath(missionDirectory);
276}
277
279{
280 return _childSavePath(parameterDirectory);
281}
282
284{
285 return _childSavePath(telemetryDirectory);
286}
287
289{
290 return _childSavePath(logDirectory);
291}
292
294{
295 return _childSavePath(videoDirectory);
296}
297
299{
300 return _childSavePath(photoDirectory);
301}
302
304{
305 return _childSavePath(crashDirectory);
306}
307
309{
310 return _childSavePath(mavlinkActionsDirectory);
311}
312
314{
315 return _childSavePath(settingsDirectory);
316}
317
318QList<int> AppSettings::firstRunPromptsIdsVariantToList(const QVariant& firstRunPromptIds)
319{
320 QList<int> rgIds;
321
322 QStringList strIdList = firstRunPromptIds.toString().split(",", Qt::SkipEmptyParts);
323
324 for (const QString& strId: strIdList) {
325 rgIds.append(strId.toInt());
326 }
327 return rgIds;
328}
329
330QVariant AppSettings::firstRunPromptsIdsListToVariant(const QList<int>& rgIds)
331{
332 QStringList strList;
333 for (int id: rgIds) {
334 strList.append(QString::number(id));
335 }
336 return QVariant(strList.join(","));
337}
338
340{
341 QList<int> rgIds = firstRunPromptsIdsVariantToList(firstRunPromptIdsShown()->rawValue());
342 if (!rgIds.contains(id)) {
343 rgIds.append(id);
344 firstRunPromptIdsShown()->setRawValue(firstRunPromptsIdsListToVariant(rgIds));
345 }
346}
347
353QLocale::Language AppSettings::_qLocaleLanguageEarlyAccess(void)
354{
355 QSettings settings;
356
357 // Note that the AppSettings group has no group name
358 QLocale::Language localeLanguage = static_cast<QLocale::Language>(settings.value(qLocaleLanguageName).toInt());
359 for (auto& languageInfo: _rgLanguageInfo) {
360 if (languageInfo.languageId == localeLanguage) {
361 return localeLanguage;
362 }
363 }
364
365#ifdef QT_DEBUG
366 if (localeLanguage == QLocale::Esperanto) {
367 return localeLanguage;
368 }
369#endif
370
371 localeLanguage = QLocale::AnyLanguage;
372 settings.setValue(qLocaleLanguageName, localeLanguage);
373
374 return localeLanguage;
375}
#define qgcApp()
#define DECLARE_SETTINGSFACT_NO_FUNC(CLASS, NAME)
#define DECLARE_SETTINGSFACT(CLASS, NAME)
#define DECLARE_SETTINGGROUP(NAME, GROUP)
Application Settings.
Definition AppSettings.h:10
static QVariant firstRunPromptsIdsListToVariant(const QList< int > &rgIds)
QString videoSavePath()
QString logSavePath()
QString telemetrySavePath()
QString crashSavePath()
static constexpr const char * settingsDirectory
QString parameterSavePath()
static constexpr const char * photoDirectory
static constexpr const char * telemetryDirectory
Q_INVOKABLE void firstRunPromptIdsMarkIdAsShown(int id)
static constexpr const char * logDirectory
void savePathsChanged()
QString settingsSavePath()
static constexpr const char * videoDirectory
static QList< int > firstRunPromptsIdsVariantToList(const QVariant &firstRunPromptIds)
static constexpr const char * missionDirectory
static constexpr const char * mavlinkActionsDirectory
QString photoSavePath()
QString missionSavePath()
static constexpr const char * crashDirectory
QString mavlinkActionsSavePath()
static constexpr const char * parameterDirectory
Holds the meta data associated with a Fact.
void setEnumInfo(const QStringList &strings, const QVariantList &values)
void rawValueChanged(const QVariant &value)
QGCPalette is used in QML ui to expose color properties for the QGC palette.
Definition QGCPalette.h:83
static void setGlobalTheme(Theme newTheme)
A SettingsFact is Fact which holds a QSettings value.
bool ensureDirectoryExists(const QString &path)
QString joinPath(const QString &dir, const QString &name)
bool runningUnitTests()
void showAppMessage(const QString &message, const QString &title)
Modal application message. Queued if the UI isn't ready yet.
Definition AppMessages.cc:9