diff --git a/src/activities/balancebox/ActivityInfo.qml b/src/activities/balancebox/ActivityInfo.qml index d06741842..dc90c9519 100644 --- a/src/activities/balancebox/ActivityInfo.qml +++ b/src/activities/balancebox/ActivityInfo.qml @@ -1,53 +1,54 @@ /* GCompris - ActivityInfo.qml * * Copyright (C) 2015 Holger Kaelberer * * Authors: * Holger Kaelberer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ import GCompris 1.0 ActivityInfo { name: "balancebox/Balancebox.qml" difficulty: 2 icon: "balancebox/balancebox.svg" author: "Holger Kaelberer <holger.k@elberer.de>" demo: true title: qsTr("Balance Box") description: qsTr("Navigate the ball to the door by tilting the box.") // intro: "Click on the word matching the picture." goal: qsTr("Practice fine motor skills and basic counting.") prerequisite: "None" manual: qsTr("Navigate the ball to the door. Be careful to not make it fall into the holes. Numbered contact buttons in the box need to be touched in the correct order to unlock the door. You can move the ball by tilting your mobile device. On desktop platforms use the arrow keys to simulate tilting. In the configuration dialog you can choose between the default 'builtin' level set and one that you can define yourself ('user'). A user-defined level set can be created by choosing the 'user' level set and start the level editor by clicking on the corresponding button. In the level editor you can create your own levels. Choose one of the editing tools on the left side to modify the map cells of the currently active level in the editor: Cross: Clear a map cell completely Horizontal Wall: Set/remove a horizontal wall on the lower edge of a cell Vertical Wall: Set/remove a vertical wall on the right edge of a cell Hole: Set/remove a hole on a cell Ball: Set the starting position of the ball. Door: Set the door position. Contact: Set/remove a contact button. With the spin-box you can adjust the value of the contact button. It is not possible to set a value more than one time on a map. All tools (except the clear-tool) toggle their respective target on the clicked cell: an item can be placed by clicking on an empty cell and by clicking again on the same cell with the same tool, you can remove it again. You can test a modified level by clicking on the 'Test' button on the right side of the editor view. You can return from testing mode by clicking on the home-button on the bar or by pressing Escape on your keyboard or the back button on your mobile device. In the editor you can change the level currently edited by using the arrow buttons on the bar. Back to the editor you can continue editing the current level and test it again if needed. When your level is finished you can save it to the user level file by clicking on the 'Save' button on the right side. To return to the configuration configuration dialog click on the home-button on the bar or press Escape on your keyboard or the back button on your mobile device.") credit: "" section: "mobile fun" + enabled: !ApplicationInfo.isMobile() || ApplicationInfo.sensorIsSupported("QTiltSensor") } diff --git a/src/core/ApplicationInfo.cpp b/src/core/ApplicationInfo.cpp index 48cbdd7be..c0e1c0429 100644 --- a/src/core/ApplicationInfo.cpp +++ b/src/core/ApplicationInfo.cpp @@ -1,247 +1,253 @@ /* GCompris - ApplicationSettingsDefault.cpp * * Copyright (C) 2014-2015 Bruno Coudoin * * Authors: * Bruno Coudoin * * This file was originally created from Digia example code under BSD license * and heavily modified since then. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "ApplicationInfo.h" #include #include #include #include #include #include #include #include +#include #include #include #include #include QQuickWindow *ApplicationInfo::m_window = NULL; ApplicationInfo *ApplicationInfo::m_instance = NULL; ApplicationInfo::ApplicationInfo(QObject *parent): QObject(parent) { m_isMobile = false; #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_BLACKBERRY) || defined(SAILFISHOS) m_isMobile = true; #endif #if defined(Q_OS_ANDROID) // Put android before checking linux/unix as it is also a linux m_platform = Android; #elif (defined(Q_OS_LINUX) || defined(Q_OS_UNIX)) m_platform = Linux; #elif defined(Q_OS_WIN) m_platform = Windows; #elif defined(Q_OS_MAC) m_platform = MacOSX; #elif defined(Q_OS_IOS) m_platform = Ios; #elif defined(Q_OS_BLACKBERRY) m_platform = Blackberry; #elif defined(SAILFISHOS) m_platform = SailfishOS; #endif QRect rect = qApp->primaryScreen()->geometry(); m_ratio = qMin(qMax(rect.width(), rect.height())/800. , qMin(rect.width(), rect.height())/520.); // calculate a factor for font-scaling, cf. // http://doc.qt.io/qt-5/scalability.html#calculating-scaling-ratio qreal refDpi = 216.; qreal refHeight = 1776.; qreal refWidth = 1080.; qreal height = qMax(rect.width(), rect.height()); qreal width = qMin(rect.width(), rect.height()); qreal dpi = qApp->primaryScreen()->logicalDotsPerInch(); m_fontRatio = qMax(qreal(1.0), qMin(height*refDpi/(dpi*refHeight), width*refDpi/(dpi*refWidth))); m_isPortraitMode = m_isMobile ? rect.height() > rect.width() : false; m_applicationWidth = m_isMobile ? rect.width() : 1120; if (m_isMobile) connect(qApp->primaryScreen(), SIGNAL(physicalSizeChanged(QSizeF)), this, SLOT(notifyPortraitMode())); // Get all symbol fonts to remove them QFontDatabase database; m_excludedFonts = database.families(QFontDatabase::Symbol); // Get fonts from rcc const QStringList fontFilters = {"*.otf", "*.ttf"}; m_fontsFromRcc = QDir(":/gcompris/src/core/resource/fonts").entryList(fontFilters); } ApplicationInfo::~ApplicationInfo() { m_instance = NULL; } +bool ApplicationInfo::sensorIsSupported(const QString& sensorType) +{ + return QSensor::sensorTypes().contains(sensorType.toUtf8()); +} + Qt::ScreenOrientation ApplicationInfo::getNativeOrientation() { return QGuiApplication::primaryScreen()->nativeOrientation(); } void ApplicationInfo::setApplicationWidth(const int newWidth) { if (newWidth != m_applicationWidth) { m_applicationWidth = newWidth; emit applicationWidthChanged(); } } QString ApplicationInfo::getResourceDataPath() { return QString("qrc:/gcompris/data"); } QString ApplicationInfo::getFilePath(const QString &file) { #if defined(Q_OS_ANDROID) return QString("assets:/%1").arg(file); #elif defined(Q_OS_MAC) return QString("%1/rcc/%2").arg(QCoreApplication::applicationDirPath(), file); #else return QString("%1/%2/rcc/%3").arg(QCoreApplication::applicationDirPath(), GCOMPRIS_DATA_FOLDER, file); #endif } QString ApplicationInfo::getAudioFilePath(const QString &file) { QString localeName = getVoicesLocale(ApplicationSettings::getInstance()->locale()); return getAudioFilePathForLocale(file, localeName); } QString ApplicationInfo::getAudioFilePathForLocale(const QString &file, const QString &localeName) { QString filename = file; filename.replace("$LOCALE", localeName); filename.replace("$CA", COMPRESSED_AUDIO); if(file.startsWith('/') || file.startsWith(QLatin1String("qrc:")) || file.startsWith(':')) return filename; else return getResourceDataPath() + '/' + filename; } QString ApplicationInfo::getLocaleFilePath(const QString &file) { QString localeShortName = localeShort(); QString filename = file; filename.replace("$LOCALE", localeShortName); return filename; } QString ApplicationInfo::getSharedWritablePath() const { return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/GCompris"); } QStringList ApplicationInfo::getSystemExcludedFonts() { return m_excludedFonts; } QStringList ApplicationInfo::getFontsFromRcc() { return m_fontsFromRcc; } void ApplicationInfo::notifyPortraitMode() { int width = qApp->primaryScreen()->geometry().width(); int height = qApp->primaryScreen()->geometry().height(); setIsPortraitMode(height > width); } void ApplicationInfo::setIsPortraitMode(const bool newMode) { if (m_isPortraitMode != newMode) { m_isPortraitMode = newMode; emit portraitModeChanged(); } } void ApplicationInfo::setWindow(QQuickWindow *window) { m_window = window; } void ApplicationInfo::screenshot(QString const &path) { QImage img = m_window->grabWindow(); img.save(path); } void ApplicationInfo::notifyFullscreenChanged() { if(ApplicationSettings::getInstance()->isFullscreen()) m_window->showFullScreen(); else m_window->showNormal(); } // return the shortest possible locale name for the given locale, describing // a unique voices dataset QString ApplicationInfo::getVoicesLocale(const QString &locale) { QString _locale = locale; if(_locale == GC_DEFAULT_LOCALE) { _locale = QLocale::system().name(); if(_locale == "C") _locale = "en_US"; } // locales we have country-specific voices for: if (_locale.startsWith(QLatin1String("pt_BR")) || _locale.startsWith(QLatin1String("zh_CN")) || _locale.startsWith(QLatin1String("zh_TW"))) return QLocale(_locale).name(); // short locale for all the rest: return localeShort(_locale); } QObject *ApplicationInfo::systeminfoProvider(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine) Q_UNUSED(scriptEngine) /* * Connect the fullscreen change signal to applicationInfo in order to change * the QQuickWindow value */ ApplicationInfo* appInfo = getInstance(); connect(ApplicationSettings::getInstance(), SIGNAL(fullscreenChanged()), appInfo, SLOT(notifyFullscreenChanged())); return appInfo; } void ApplicationInfo::init() { qmlRegisterSingletonType("GCompris", 1, 0, "ApplicationInfo", systeminfoProvider); } diff --git a/src/core/ApplicationInfo.h b/src/core/ApplicationInfo.h index 364a21cc6..72fa057bb 100644 --- a/src/core/ApplicationInfo.h +++ b/src/core/ApplicationInfo.h @@ -1,383 +1,392 @@ /* GCompris - ApplicationSettingsDefault.cpp * * Copyright (C) 2014-2015 Bruno Coudoin * * Authors: * Bruno Coudoin * * This file was originally created from Digia example code under BSD license * and heavily modified since then. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef APPLICATIONINFO_H #define APPLICATIONINFO_H #include #include "ApplicationSettings.h" #include #include #include #include #include class QQuickWindow; /** * @class ApplicationInfo * @short A general purpose singleton that exposes miscellaneous native * functions to the QML layer. * @ingroup infrastructure */ class ApplicationInfo : public QObject { Q_OBJECT Q_ENUMS(Platform) /** * Width of the application viewport. */ Q_PROPERTY(int applicationWidth READ applicationWidth WRITE setApplicationWidth NOTIFY applicationWidthChanged) /** * Platform the application is currently running on. */ Q_PROPERTY(Platform platform READ platform CONSTANT) /** * Whether the application is currently running on a mobile platform. * * Mobile platforms are Android, Ios (not supported yet), * Blackberry (not supported) */ Q_PROPERTY(bool isMobile READ isMobile CONSTANT) /** * Whether the current platform supports fragment shaders. * * This flag is used in some core modules to selectively deactivate * particle effects which cause crashes on some Android devices. * * cf. https://bugreports.qt.io/browse/QTBUG-44194 * * For now always set to false, to prevent crashes. */ Q_PROPERTY(bool hasShader READ hasShader CONSTANT) /** * Whether currently in portrait mode, on mobile platforms. * * Based on viewport geometry. */ Q_PROPERTY(bool isPortraitMode READ isPortraitMode WRITE setIsPortraitMode NOTIFY portraitModeChanged) /** * Ratio factor used for scaling of sizes on high-dpi devices. * * Must be used by activities as a scaling factor to all pixel values. */ Q_PROPERTY(qreal ratio READ ratio NOTIFY ratioChanged) /** * Ratio factor used for font scaling. * * On some low-dpi Android devices with high res (e.g. Galaxy Tab 4) the * fonts in Text-like elements appear too small with respect to the other * graphics -- also if we are using font.pointSize. * * For these cases we calculate a fontRatio in ApplicationInfo that takes * dpi information into account, as proposed on * http://doc.qt.io/qt-5/scalability.html#calculating-scaling-ratio * * GCText applies this factor automatically on its new fontSize property. */ Q_PROPERTY(qreal fontRatio READ fontRatio NOTIFY fontRatioChanged) /** * Short (2-letter) locale string of the currently active language. */ Q_PROPERTY(QString localeShort READ localeShort) /** * GCompris version string (compile time). */ Q_PROPERTY(QString GCVersion READ GCVersion CONSTANT) /** * Qt version string (runtime). */ Q_PROPERTY(QString QTVersion READ QTVersion CONSTANT) /** * Audio codec used for voices resources. * * This is determined at compile time (ogg for free platforms, aac on * MacOSX and IOS). */ Q_PROPERTY(QString CompressedAudio READ CompressedAudio CONSTANT) /** * Download allowed * * This is determined at compile time. If set to NO GCompris will * never download anything. */ Q_PROPERTY(bool isDownloadAllowed READ isDownloadAllowed CONSTANT) public: /** * Known host platforms. */ enum Platform { Linux, /**< Linux (except Android) */ Windows, /**< Windows */ MacOSX, /**< MacOSX */ Android, /**< Android */ Ios, /**< IOS (not supported) */ Blackberry, /**< Blackberry (not supported) */ SailfishOS /**< SailfishOS */ }; /** * Registers singleton in the QML engine. * * It is not recommended to create a singleton of Qml Singleton registered * object but we could not found a better way to let us access ApplicationInfo * on the C++ side. All our test shows that it works. */ static void init(); /** * Returns an absolute and platform independent path to the passed @p file * * @param file A relative filename. * @returns Absolute path to the file. */ static QString getFilePath(const QString &file); /** * Returns the short locale name for the passed @p locale. * * Handles also 'system' (GC_DEFAULT_LOCALE) correctly which resolves to * QLocale::system().name(). * * @param locale A locale string of the form \_\ * @returns A short locale string of the form \ */ static QString localeShort(const QString &locale) { QString _locale = locale; if(_locale == GC_DEFAULT_LOCALE) { _locale = QLocale::system().name(); } // Can't use left(2) because of Asturian where there are 3 chars return _locale.left(_locale.indexOf('_')); } /** * Returns a locale string that can be used in voices filenames. * * @param locale A locale string of the form \_\ * @returns A locale string as used in voices filenames. */ Q_INVOKABLE QString getVoicesLocale(const QString &locale); /** * Request GCompris to take the Audio Focus at the system level. * * On systems that support it, it will mute a running audio player. */ Q_INVOKABLE bool requestAudioFocus() const; /** * Abandon the Audio Focus. * * On systems that support it, it will let an audio player start again. */ Q_INVOKABLE void abandonAudioFocus() const; /** * Return the platform specific path for storing data shared between apps * * On Android: /storage/emulated/0/GCompris (>= Android 4.2), * /storage/sdcard0/GCompris (< Android 4.2) * On Linux: $HOME/local/share/GCompris */ Q_INVOKABLE QString getSharedWritablePath() const; /// @cond INTERNAL_DOCS static ApplicationInfo *getInstance() { if(!m_instance) { m_instance = new ApplicationInfo(); } return m_instance; } static QObject *systeminfoProvider(QQmlEngine *engine, QJSEngine *scriptEngine); static void setWindow(QQuickWindow *window); explicit ApplicationInfo(QObject *parent = 0); ~ApplicationInfo(); int applicationWidth() const { return m_applicationWidth; } void setApplicationWidth(const int newWidth); Platform platform() const { return m_platform; } bool isPortraitMode() const { return m_isPortraitMode; } void setIsPortraitMode(const bool newMode); bool isMobile() const { return m_isMobile; } bool hasShader() const { return false; } qreal ratio() const { return m_ratio; } qreal fontRatio() const { return m_fontRatio; } QString localeShort() const { return localeShort( ApplicationSettings::getInstance()->locale() ); } static QString GCVersion() { return VERSION; } static QString QTVersion() { return qVersion(); } static QString CompressedAudio() { return COMPRESSED_AUDIO; } static bool isDownloadAllowed() { return QString(DOWNLOAD_ALLOWED) == "ON"; } /** * Returns the native screen orientation. * * Wraps QScreen::nativeOrientation: The native orientation of the screen * is the orientation where the logo sticker of the device appears the * right way up, or Qt::PrimaryOrientation if the platform does not support * this functionality. * * The native orientation is a property of the hardware, and does not change */ Q_INVOKABLE Qt::ScreenOrientation getNativeOrientation(); /** * Change the desired orientation of the application. * * Android specific function, cf. http://developer.android.com/reference/android/app/Activity.html#setRequestedOrientation(int) * * @param orientation Desired orientation of the application. For possible * values cf. http://developer.android.com/reference/android/content/pm/ActivityInfo.html#screenOrientation . * Some useful values: * - -1: SCREEN_ORIENTATION_UNSPECIFIED * - 0: SCREEN_ORIENTATION_LANDSCAPE: forces landscape * - 1: SCREEN_ORIENTATION_PORTRAIT: forces portrait * - 5: SCREEN_ORIENTATION_NOSENSOR: forces native * orientation mode on each device (portrait on * smartphones, landscape on tablet) * - 14: SCREEN_ORIENTATION_LOCKED: lock current orientation */ Q_INVOKABLE void setRequestedOrientation(int orientation); /** * Query the desired orientation of the application. * * @sa setRequestedOrientation */ Q_INVOKABLE int getRequestedOrientation(); + /** + * Checks whether a sensor type from the QtSensor module is supported on + * the current platform. + * + * @param sensorType Classname of a sensor from the QtSensor module + * to be checked (e.g. "QTiltSensor"). + */ + Q_INVOKABLE bool sensorIsSupported(const QString& sensorType); + /// @endcond protected slots: /** * Returns the resource root-path used for GCompris resources. */ QString getResourceDataPath(); /** * Returns an absolute path to a language specific sound/voices file. If * the file is already absolute only the token replacement is applied. * * @param file A templated relative path to a language specific file. Any * occurrence of the '$LOCALE' placeholder will be replaced by * the currently active locale string. * Any occurrence of '$CA' placeholder will be replaced by * the current compressed audio format ('ogg' or 'aac). * Example: 'voices-$CA/$LOCALE/misc/click_on_letter.$CA' * @returns An absolute path to the corresponding resource file. */ Q_INVOKABLE QString getAudioFilePath(const QString &file); /** * Returns an absolute path to a language specific sound/voices file. If * the file is already absolute only the token replacement is applied. * * @param file A templated relative path to a language specific file. Any * occurrence of the '$LOCALE' placeholder will be replaced by * the currently active locale string. * Any occurrence of '$CA' placeholder will be replaced by * the current compressed audio format ('ogg' or 'aac). * Example: 'voices-$CA/$LOCALE/misc/click_on_letter.$CA' * @param locale the locale for which to get this audio file * @returns An absolute path to the corresponding resource file. */ Q_INVOKABLE QString getAudioFilePathForLocale(const QString &file, const QString &localeName); /** * Returns an absolute path to a language specific resource file. * * Generalization of getAudioFilePath(). * @sa getAudioFilePath */ Q_INVOKABLE QString getLocaleFilePath(const QString &file); /** * @returns A list of systems-fonts that should be excluded from font * selection. */ Q_INVOKABLE QStringList getSystemExcludedFonts(); /** * @returns A list of fonts contained in the fonts resources. */ Q_INVOKABLE QStringList getFontsFromRcc(); /** * Stores a screenshot in the passed @p file. * * @param file Absolute destination filename. */ Q_INVOKABLE void screenshot(const QString &file); void notifyPortraitMode(); Q_INVOKABLE void notifyFullscreenChanged(); protected: qreal getSizeWithRatio(const qreal height) { return ratio() * height; } signals: void applicationWidthChanged(); void portraitModeChanged(); void ratioChanged(); void fontRatioChanged(); void applicationSettingsChanged(); void fullscreenChanged(); private: static ApplicationInfo *m_instance; int m_applicationWidth; Platform m_platform; QQmlPropertyMap *m_constants; bool m_isPortraitMode; bool m_isMobile; qreal m_ratio; qreal m_fontRatio; // Symbols fonts that user can't see QStringList m_excludedFonts; QStringList m_fontsFromRcc; static QQuickWindow *m_window; }; #endif // APPLICATIONINFO_H