File indexing completed on 2024-04-14 14:08:45
0001 /* GCompris - ApplicationInfo.cpp 0002 * 0003 * SPDX-FileCopyrightText: 2014-2015 Bruno Coudoin <bruno.coudoin@gcompris.net> 0004 * 0005 * Authors: 0006 * Bruno Coudoin <bruno.coudoin@gcompris.net> 0007 * 0008 * This file was originally created from Digia example code under BSD license 0009 * and heavily modified since then. 0010 * 0011 * SPDX-License-Identifier: GPL-3.0-or-later 0012 */ 0013 0014 #include "ApplicationInfo.h" 0015 0016 #include <QtQml> 0017 #include <QGuiApplication> 0018 #include <QScreen> 0019 #include <QLocale> 0020 #include <QQuickWindow> 0021 #include <QSensor> 0022 0023 #include <QDebug> 0024 0025 #include <QFontDatabase> 0026 #include <QDir> 0027 0028 QQuickWindow *ApplicationInfo::m_window = nullptr; 0029 ApplicationInfo *ApplicationInfo::m_instance = nullptr; 0030 0031 ApplicationInfo::ApplicationInfo(QObject *parent) : 0032 QObject(parent) 0033 { 0034 0035 #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_BLACKBERRY) || defined(UBUNTUTOUCH) 0036 m_isMobile = true; 0037 #else 0038 m_isMobile = false; 0039 #endif 0040 0041 #if defined(Q_OS_ANDROID) 0042 // Put android before checking linux/unix as it is also a linux 0043 m_platform = Android; 0044 #elif defined(UBUNTUTOUCH) 0045 m_platform = UbuntuTouchOS; 0046 #elif defined(Q_OS_MAC) 0047 m_platform = MacOSX; 0048 #elif (defined(Q_OS_LINUX) || defined(Q_OS_UNIX)) 0049 m_platform = Linux; 0050 #elif defined(Q_OS_WIN) 0051 m_platform = Windows; 0052 #elif defined(Q_OS_IOS) 0053 m_platform = Ios; 0054 #elif defined(Q_OS_BLACKBERRY) 0055 m_platform = Blackberry; 0056 #else // default is Linux 0057 m_platform = Linux; 0058 #endif 0059 0060 m_isBox2DInstalled = false; 0061 0062 QRect rect = qApp->primaryScreen()->geometry(); 0063 m_ratio = qMin(qMax(rect.width(), rect.height()) / 800., qMin(rect.width(), rect.height()) / 520.); 0064 // calculate a factor for font-scaling, cf. 0065 // https://doc.qt.io/qt-5/scalability.html#calculating-scaling-ratio 0066 qreal refDpi = 216.; 0067 qreal refHeight = 1776.; 0068 qreal refWidth = 1080.; 0069 qreal height = qMax(rect.width(), rect.height()); 0070 qreal width = qMin(rect.width(), rect.height()); 0071 qreal dpi = qApp->primaryScreen()->logicalDotsPerInch(); 0072 0073 #if defined(UBUNTUTOUCH) 0074 m_fontRatio = floor(m_ratio * 10) / 10; 0075 #else 0076 m_fontRatio = qMax(qreal(1.0), qMin(height * refDpi / (dpi * refHeight), width * refDpi / (dpi * refWidth))); 0077 #endif 0078 m_isPortraitMode = m_isMobile ? rect.height() > rect.width() : false; 0079 m_applicationWidth = m_isMobile ? rect.width() : 1120; 0080 0081 m_useOpenGL = true; 0082 0083 if (m_isMobile) 0084 connect(qApp->primaryScreen(), &QScreen::physicalSizeChanged, this, &ApplicationInfo::notifyPortraitMode); 0085 0086 // @FIXME this does not work on iOS: https://bugreports.qt.io/browse/QTBUG-50624 0087 #if !defined(Q_OS_IOS) 0088 // Get all symbol fonts to remove them 0089 QFontDatabase database; 0090 m_excludedFonts = database.families(QFontDatabase::Symbol); 0091 #endif 0092 // Get fonts from rcc 0093 const QStringList fontFilters = { "*.otf", "*.ttf" }; 0094 m_fontsFromRcc = QDir(":/gcompris/src/core/resource/fonts").entryList(fontFilters); 0095 } 0096 0097 ApplicationInfo::~ApplicationInfo() 0098 { 0099 m_instance = nullptr; 0100 } 0101 0102 bool ApplicationInfo::sensorIsSupported(const QString &sensorType) 0103 { 0104 return QSensor::sensorTypes().contains(sensorType.toUtf8()); 0105 } 0106 0107 Qt::ScreenOrientation ApplicationInfo::getNativeOrientation() 0108 { 0109 return QGuiApplication::primaryScreen()->nativeOrientation(); 0110 } 0111 0112 void ApplicationInfo::setApplicationWidth(const int newWidth) 0113 { 0114 if (newWidth != m_applicationWidth) { 0115 m_applicationWidth = newWidth; 0116 Q_EMIT applicationWidthChanged(); 0117 } 0118 } 0119 0120 QStringList ApplicationInfo::getResourceDataPaths() 0121 { 0122 return { ApplicationSettings::getInstance()->userDataPath(), "qrc:/gcompris/data" }; 0123 } 0124 0125 QString ApplicationInfo::getFilePath(const QString &file) 0126 { 0127 #if defined(Q_OS_ANDROID) 0128 return QString("assets:/share/GCompris/rcc/%1").arg(file); 0129 #elif defined(Q_OS_MACX) 0130 return QString("%1/../Resources/rcc/%2").arg(QCoreApplication::applicationDirPath(), file); 0131 #elif defined(Q_OS_IOS) 0132 return QString("%1/rcc/%2").arg(QCoreApplication::applicationDirPath(), file); 0133 #else 0134 return QString("%1/%2/rcc/%3").arg(QCoreApplication::applicationDirPath(), GCOMPRIS_DATA_FOLDER, file); 0135 #endif 0136 } 0137 0138 QString ApplicationInfo::getAudioFilePath(const QString &file) 0139 { 0140 QString localeName = getVoicesLocale(ApplicationSettings::getInstance()->locale()); 0141 return getAudioFilePathForLocale(file, localeName); 0142 } 0143 0144 QString ApplicationInfo::getAudioFilePathForLocale(const QString &file, 0145 const QString &localeName) 0146 { 0147 QString filename = file; 0148 filename.replace("$LOCALE", localeName); 0149 filename.replace("$CA", CompressedAudio()); 0150 // Absolute paths are returned as is 0151 if (file.startsWith('/') || file.startsWith(QLatin1String("qrc:")) || file.startsWith(':')) 0152 return filename; 0153 0154 // For relative paths, look into resource folders if found 0155 const QStringList dataPaths = getResourceDataPaths(); 0156 for (const QString &dataPath: dataPaths) { 0157 if (QFile::exists(dataPath + '/' + filename)) { 0158 if (dataPath.startsWith('/')) 0159 return "file://" + dataPath + '/' + filename; 0160 else 0161 return dataPath + '/' + filename; 0162 } 0163 } 0164 // If not found, return the default qrc:/gcompris/data path 0165 return dataPaths.last() + '/' + filename; 0166 } 0167 0168 QString ApplicationInfo::getLocaleFilePath(const QString &file) 0169 { 0170 QString localeShortName = localeShort(); 0171 0172 QString filename = file; 0173 filename.replace("$LOCALE", localeShortName); 0174 return filename; 0175 } 0176 0177 QStringList ApplicationInfo::getSystemExcludedFonts() 0178 { 0179 return m_excludedFonts; 0180 } 0181 0182 QStringList ApplicationInfo::getFontsFromRcc() 0183 { 0184 return m_fontsFromRcc; 0185 } 0186 0187 QStringList ApplicationInfo::getBackgroundMusicFromRcc() 0188 { 0189 const QStringList backgroundMusicFilters = { QString("*.%1").arg(COMPRESSED_AUDIO) }; 0190 m_backgroundMusicFromRcc = QDir(":/gcompris/data/backgroundMusic").entryList(backgroundMusicFilters); 0191 return m_backgroundMusicFromRcc; 0192 } 0193 0194 void ApplicationInfo::notifyPortraitMode() 0195 { 0196 int width = qApp->primaryScreen()->geometry().width(); 0197 int height = qApp->primaryScreen()->geometry().height(); 0198 setIsPortraitMode(height > width); 0199 } 0200 0201 void ApplicationInfo::setIsPortraitMode(const bool newMode) 0202 { 0203 if (m_isPortraitMode != newMode) { 0204 m_isPortraitMode = newMode; 0205 Q_EMIT portraitModeChanged(); 0206 } 0207 } 0208 0209 void ApplicationInfo::setWindow(QQuickWindow *window) 0210 { 0211 m_window = window; 0212 } 0213 0214 void ApplicationInfo::screenshot(const QString &file) 0215 { 0216 QImage img = m_window->grabWindow(); 0217 img.save(file); 0218 } 0219 0220 void ApplicationInfo::notifyFullscreenChanged() 0221 { 0222 if (ApplicationSettings::getInstance()->isFullscreen()) 0223 m_window->showFullScreen(); 0224 else 0225 m_window->showNormal(); 0226 } 0227 0228 // Would be better to create a component importing Box2D 2.0 using QQmlContext and test if it exists but it does not work. 0229 void ApplicationInfo::setBox2DInstalled(const QQmlEngine &engine) 0230 { 0231 /* 0232 QQmlContext *context = new QQmlContext(engine.rootContext()); 0233 context->setContextObject(&myDataSet); 0234 0235 QQmlComponent component(&engine); 0236 component.setData("import QtQuick 2.12\nimport Box2D 2.0\nItem { }", QUrl()); 0237 component.create(context); 0238 box2dInstalled = (component != nullptr); 0239 */ 0240 bool box2dInstalled = false; 0241 const QStringList importPathList = engine.importPathList(); 0242 for (const QString &folder: importPathList) { 0243 if (QDir(folder).entryList().contains(QLatin1String("Box2D.2.0"))) { 0244 if (QDir(folder + "/Box2D.2.0").entryList().contains("qmldir")) { 0245 qDebug() << "Found box2d in " << folder; 0246 box2dInstalled = true; 0247 break; 0248 } 0249 } 0250 } 0251 m_isBox2DInstalled = box2dInstalled; 0252 Q_EMIT isBox2DInstalledChanged(); 0253 } 0254 0255 // return the shortest possible locale name for the given locale, describing 0256 // a unique voices dataset 0257 QString ApplicationInfo::getVoicesLocale(const QString &locale) 0258 { 0259 QString _locale = locale; 0260 if (_locale == GC_DEFAULT_LOCALE) { 0261 _locale = QLocale::system().name(); 0262 if (_locale == "C") 0263 _locale = "en_US"; 0264 } 0265 // locales we have country-specific voices for: 0266 /* clang-format off */ 0267 if (_locale.startsWith(QLatin1String("en_GB")) || 0268 _locale.startsWith(QLatin1String("en_US")) || 0269 _locale.startsWith(QLatin1String("pt_BR")) || 0270 _locale.startsWith(QLatin1String("zh_CN")) || 0271 _locale.startsWith(QLatin1String("zh_TW"))) { 0272 return QLocale(_locale).name(); 0273 } 0274 /* clang-format on */ 0275 0276 // short locale for all the rest: 0277 return localeShort(_locale); 0278 } 0279 0280 QVariantList ApplicationInfo::localeSort(QVariantList list, 0281 const QString &locale) const 0282 { 0283 std::sort(list.begin(), list.end(), 0284 [&locale, this](const QVariant &a, const QVariant &b) { 0285 return (localeCompare(a.toString(), b.toString(), locale) < 0); 0286 }); 0287 return list; 0288 } 0289 0290 QObject *ApplicationInfo::applicationInfoProvider(QQmlEngine *engine, 0291 QJSEngine *scriptEngine) 0292 { 0293 Q_UNUSED(engine) 0294 Q_UNUSED(scriptEngine) 0295 /* 0296 * Connect the fullscreen change signal to applicationInfo in order to change 0297 * the QQuickWindow value 0298 */ 0299 ApplicationInfo *appInfo = getInstance(); 0300 connect(ApplicationSettings::getInstance(), &ApplicationSettings::fullscreenChanged, appInfo, 0301 &ApplicationInfo::notifyFullscreenChanged); 0302 0303 return appInfo; 0304 } 0305 0306 #include "moc_ApplicationInfo.cpp"