1 /* GCompris - ApplicationInfo.h
2  *
3  * SPDX-FileCopyrightText: 2014-2015 Bruno Coudoin <bruno.coudoin@gcompris.net>
4  *
5  * Authors:
6  *   Bruno Coudoin <bruno.coudoin@gcompris.net>
7  *
8  * This file was originally created from Digia example code under BSD license
9  * and heavily modified since then.
10  *
11  *   SPDX-License-Identifier: GPL-3.0-or-later
12  */
13 #ifndef APPLICATIONINFO_H
14 #define APPLICATIONINFO_H
15 
16 #include <config.h>
17 #include "ApplicationSettings.h"
18 
19 #include <QObject>
20 #include <QLocale>
21 #include <QQmlEngine>
22 #include <QtGlobal>
23 
24 class QQuickWindow;
25 
26 /**
27  * @class ApplicationInfo
28  * @short A general purpose singleton that exposes miscellaneous native
29  *        functions to the QML layer.
30  * @ingroup infrastructure
31  */
32 class ApplicationInfo : public QObject
33 {
34     Q_OBJECT
35 
36     /**
37      * Width of the application viewport.
38      */
39     Q_PROPERTY(int applicationWidth READ applicationWidth WRITE setApplicationWidth NOTIFY applicationWidthChanged)
40 
41     /**
42      * Platform the application is currently running on.
43      */
44     Q_PROPERTY(Platform platform READ platform CONSTANT)
45 
46     /**
47      * Whether the application is currently running on a mobile platform.
48      *
49      * Mobile platforms are Android, Ios (not supported yet),
50      * Blackberry (not supported)
51      */
52     Q_PROPERTY(bool isMobile READ isMobile CONSTANT)
53 
54     /**
55      * Whether the current platform supports fragment shaders.
56      *
57      * This flag is used in some core modules to selectively deactivate
58      * particle effects which cause crashes on some Android devices.
59      *
60      * cf. https://bugreports.qt.io/browse/QTBUG-44194
61      *
62      * For now always set to false, to prevent crashes.
63      */
64     Q_PROPERTY(bool hasShader READ hasShader CONSTANT)
65 
66     /**
67      * Whether currently in portrait mode, on mobile platforms.
68      *
69      * Based on viewport geometry.
70      */
71     Q_PROPERTY(bool isPortraitMode READ isPortraitMode WRITE setIsPortraitMode NOTIFY portraitModeChanged)
72 
73     /**
74      * Ratio factor used for scaling of sizes on high-dpi devices.
75      *
76      * Must be used by activities as a scaling factor to all pixel values.
77      */
78     Q_PROPERTY(qreal ratio READ ratio NOTIFY ratioChanged)
79 
80     /**
81      * Ratio factor used for font scaling.
82      *
83      * On some low-dpi Android devices with high res (e.g. Galaxy Tab 4) the
84      * fonts in Text-like elements appear too small with respect to the other
85      * graphics -- also if we are using font.pointSize.
86      *
87      * For these cases we calculate a fontRatio in ApplicationInfo that takes
88      * dpi information into account, as proposed on
89      * https://doc.qt.io/qt-5/scalability.html#calculating-scaling-ratio
90      *
91      * GCText applies this factor automatically on its new fontSize property.
92      */
93     Q_PROPERTY(qreal fontRatio READ fontRatio NOTIFY fontRatioChanged)
94 
95     /**
96      * Short (2-letter) locale string of the currently active language.
97      */
98     Q_PROPERTY(QString localeShort READ localeShort CONSTANT)
99 
100     /**
101      * GCompris version string (compile time).
102      */
103     Q_PROPERTY(QString GCVersion READ GCVersion CONSTANT)
104 
105     /**
106      * GCompris version code (compile time).
107      */
108     Q_PROPERTY(int GCVersionCode READ GCVersionCode CONSTANT)
109 
110     /**
111      * Qt version string (runtime).
112      */
113     Q_PROPERTY(QString QTVersion READ QTVersion CONSTANT)
114 
115     /**
116      * Audio codec used for voices resources.
117      *
118      * This is determined at compile time (ogg for free platforms, aac on
119      * MacOSX and IOS).
120      */
121     Q_PROPERTY(QString CompressedAudio READ CompressedAudio CONSTANT)
122 
123     /**
124      * Download allowed
125      *
126      * This is determined at compile time. If set to NO GCompris will
127      * never download anything.
128      */
129     Q_PROPERTY(bool isDownloadAllowed READ isDownloadAllowed CONSTANT)
130 
131     /**
132      * Whether the application is currently using OpenGL or not.
133      *
134      * Use to deactivate some effects if OpenGL not used.
135      */
136     Q_PROPERTY(bool useOpenGL READ useOpenGL WRITE setUseOpenGL NOTIFY useOpenGLChanged)
137 
138     /**
139      * Whether Box2D is installed or not.
140      *
141      * Use to disable activities that use Box2D when it's not installed.
142      */
143     Q_PROPERTY(bool isBox2DInstalled READ isBox2DInstalled NOTIFY isBox2DInstalledChanged)
144 
145 public:
146     /**
147 	 * Known host platforms.
148 	 */
149     enum Platform {
150         Linux, /**< Linux (except Android) */
151         Windows, /**< Windows */
152         MacOSX, /**< MacOSX */
153         Android, /**< Android */
154         Ios, /**< IOS (not supported) */
155         Blackberry, /**< Blackberry (not supported) */
156         SailfishOS, /**< SailfishOS */
157         UbuntuTouchOS /**< UbuntuTouch OS */
158     };
159 
160     Q_ENUM(Platform)
161 
162     /**
163      * Returns an absolute and platform independent path to the passed @p file
164      *
165      * @param file A relative filename.
166      * @returns Absolute path to the file.
167      */
168     static QString getFilePath(const QString &file);
169 
170     /**
171      * Returns the short locale name for the passed @p locale.
172      *
173      * Handles also 'system' (GC_DEFAULT_LOCALE) correctly which resolves to
174      * QLocale::system().name().
175      *
176      * @param locale A locale string of the form \<language\>_\<country\>
177      * @returns A short locale string of the form \<language\>
178      */
localeShort(const QString & locale)179     static QString localeShort(const QString &locale)
180     {
181         QString _locale = locale;
182         if (_locale == GC_DEFAULT_LOCALE) {
183             _locale = QLocale::system().name();
184         }
185         if (_locale == "C") {
186             _locale = "en_US";
187         }
188         // Can't use left(2) because of Asturian where there are 3 chars
189         return _locale.left(_locale.indexOf('_'));
190     }
191 
192     /**
193      * Returns a locale string that can be used in voices filenames.
194      *
195      * @param locale A locale string of the form \<language\>_\<country\>
196      * @returns A locale string as used in voices filenames.
197      */
198     Q_INVOKABLE QString getVoicesLocale(const QString &locale);
199 
200     /**
201      * Request GCompris to take the Audio Focus at the system level.
202      *
203      * On systems that support it, it will mute a running audio player.
204      */
205     Q_INVOKABLE bool requestAudioFocus() const;
206 
207     /**
208      * Abandon the Audio Focus.
209      *
210      * On systems that support it, it will let an audio player start again.
211      */
212     Q_INVOKABLE void abandonAudioFocus() const;
213 
214     /**
215      * Compare two strings respecting locale specific sort order.
216      *
217      * @param a         First string to compare
218      * @param b         Second string to compare
219      * @param locale    Locale to respect for comparison in any of the forms
220      *                  used in GCompris xx[_XX][.codeset]. Defaults to currently
221      *                  set language from global configuration.
222      * @returns         -1, 0 or 1 if a is less than, equal to or greater than b
223      */
224     Q_INVOKABLE int localeCompare(const QString &a, const QString &b, const QString &locale = "") const;
225 
226     /**
227      * Sort a list of strings respecting locale specific sort order.
228      *
229      * This function is supposed to be called from QML/JS. As there are still
230      * problems marshalling QStringList from C++ to QML/JS we use QVariantList
231      * both for argument and return value.
232      *
233      * @param list      List of strings to be sorted.
234      * @param locale    Locale to respect for sorting in any of the forms
235      *                  used in GCompris xx[_XX][.codeset].
236      * @returns         List sorted by the sort order of the passed locale.
237      */
238     Q_INVOKABLE QVariantList localeSort(QVariantList list, const QString &locale = "") const;
239 
240     /// @cond INTERNAL_DOCS
241 
getInstance()242     static ApplicationInfo *getInstance()
243     {
244         if (!m_instance) {
245             m_instance = new ApplicationInfo();
246         }
247         return m_instance;
248     }
249     static QObject *applicationInfoProvider(QQmlEngine *engine,
250                                             QJSEngine *scriptEngine);
251     static void setWindow(QQuickWindow *window);
252     explicit ApplicationInfo(QObject *parent = 0);
253     ~ApplicationInfo();
applicationWidth()254     int applicationWidth() const { return m_applicationWidth; }
255     void setApplicationWidth(const int newWidth);
platform()256     Platform platform() const { return m_platform; }
isPortraitMode()257     bool isPortraitMode() const { return m_isPortraitMode; }
258     void setIsPortraitMode(const bool newMode);
isMobile()259     bool isMobile() const { return m_isMobile; }
hasShader()260     bool hasShader() const
261     {
262 #if defined(Q_OS_ANDROID)
263         return false;
264 #else
265         return true;
266 #endif
267     }
ratio()268     qreal ratio() const { return m_ratio; }
fontRatio()269     qreal fontRatio() const { return m_fontRatio; }
localeShort()270     QString localeShort() const
271     {
272         return localeShort(ApplicationSettings::getInstance()->locale());
273     }
GCVersion()274     static QString GCVersion() { return VERSION; }
GCVersionCode()275     static int GCVersionCode() { return VERSION_CODE; }
QTVersion()276     static QString QTVersion() { return qVersion(); }
CompressedAudio()277     static QString CompressedAudio() { return COMPRESSED_AUDIO; }
isDownloadAllowed()278     static bool isDownloadAllowed() { return QString(DOWNLOAD_ALLOWED) == "ON"; }
useOpenGL()279     bool useOpenGL() const { return m_useOpenGL; }
setUseOpenGL(bool useOpenGL)280     void setUseOpenGL(bool useOpenGL) { m_useOpenGL = useOpenGL; }
281 
isBox2DInstalled()282     bool isBox2DInstalled() const { return m_isBox2DInstalled; }
283     void setBox2DInstalled(const QQmlEngine &engine);
284 
285     /**
286      * Returns the native screen orientation.
287      *
288      * Wraps QScreen::nativeOrientation: The native orientation of the screen
289      * is the orientation where the logo sticker of the device appears the
290      * right way up, or Qt::PrimaryOrientation if the platform does not support
291      * this functionality.
292      *
293      * The native orientation is a property of the hardware, and does not change
294      */
295     Q_INVOKABLE Qt::ScreenOrientation getNativeOrientation();
296 
297     /**
298      * Change the desired orientation of the application.
299      *
300      * Android specific function, cf. https://developer.android.com/reference/android/app/Activity.html#setRequestedOrientation(int)
301      *
302      * @param orientation Desired orientation of the application. For possible
303      *                    values cf. https://developer.android.com/reference/android/content/pm/ActivityInfo.html#screenOrientation .
304      *                    Some useful values:
305      *                    - -1: SCREEN_ORIENTATION_UNSPECIFIED
306      *                    -  0: SCREEN_ORIENTATION_LANDSCAPE: forces landscape
307      *                    -  1: SCREEN_ORIENTATION_PORTRAIT: forces portrait
308      *                    -  5: SCREEN_ORIENTATION_NOSENSOR: forces native
309      *                          orientation mode on each device (portrait on
310      *                          smartphones, landscape on tablet)
311      *                    - 14: SCREEN_ORIENTATION_LOCKED: lock current orientation
312      */
313     Q_INVOKABLE void setRequestedOrientation(int orientation);
314 
315     /**
316      * Query the desired orientation of the application.
317      *
318      * @sa setRequestedOrientation
319      */
320     Q_INVOKABLE int getRequestedOrientation();
321 
322     /**
323      * Checks whether a sensor type from the QtSensor module is supported on
324      * the current platform.
325      *
326      * @param sensorType  Classname of a sensor from the QtSensor module
327      *                    to be checked (e.g. "QTiltSensor").
328      */
329     Q_INVOKABLE bool sensorIsSupported(const QString &sensorType);
330 
331     /**
332      * Toggles activation of screensaver on android
333      *
334      * @param value Whether screensaver should be disabled (true) or
335      *              enabled (false).
336      */
337     Q_INVOKABLE void setKeepScreenOn(bool value);
338 
339     /**
340      * Ask for permissions for reading/writing in storage for android.
341      * Do nothing for other platforms.
342      *
343      * https://developer.android.com/training/permissions/requesting
344      * https://doc.qt.io/qt-5/qtandroid.html#requestPermissions
345      * Needed for android above 6.0
346      */
347     bool checkPermissions() const;
348 
349     /// @endcond
350 
351 public slots:
352     /**
353      * Returns the resource root-paths used for GCompris resources.
354      * First look in a local folder if exists, else will look into the rcc files.
355      */
356     QStringList getResourceDataPaths();
357 
358     /**
359      * Returns an absolute path to a language specific sound/voices file. If
360      * the file is already absolute only the token replacement is applied.
361      *
362      * @param file A templated relative path to a language specific file. Any
363      *             occurrence of the '$LOCALE' placeholder will be replaced by
364      *             the currently active locale string.
365      *             Any occurrence of '$CA' placeholder will be replaced by
366      *             the current compressed audio format ('ogg' or 'aac).
367      *             Example: 'voices-$CA/$LOCALE/misc/click_on_letter.$CA'
368      * @returns An absolute path to the corresponding resource file.
369      */
370     Q_INVOKABLE QString getAudioFilePath(const QString &file);
371 
372     /**
373      * Returns an absolute path to a language specific sound/voices file. If
374      * the file is already absolute only the token replacement is applied.
375      *
376      * @param file A templated relative path to a language specific file. Any
377      *             occurrence of the '$LOCALE' placeholder will be replaced by
378      *             the currently active locale string.
379      *             Any occurrence of '$CA' placeholder will be replaced by
380      *             the current compressed audio format ('ogg' or 'aac).
381      *             Example: 'voices-$CA/$LOCALE/misc/click_on_letter.$CA'
382      * @param localeName the locale for which to get this audio file
383      * @returns An absolute path to the corresponding resource file.
384      */
385     Q_INVOKABLE QString getAudioFilePathForLocale(const QString &file,
386                                                   const QString &localeName);
387 
388     /**
389      * Returns an absolute path to a language specific resource file.
390      *
391      * Generalization of getAudioFilePath().
392      * @sa getAudioFilePath
393      */
394     Q_INVOKABLE QString getLocaleFilePath(const QString &file);
395 
396     /**
397      * @returns A list of systems-fonts that should be excluded from font
398      *          selection.
399      */
400     Q_INVOKABLE QStringList getSystemExcludedFonts();
401 
402     /**
403      * @returns A list of fonts contained in the fonts resources.
404      */
405     Q_INVOKABLE QStringList getFontsFromRcc();
406     /**
407      * @returns A list of background music contained in the background music resources.
408      */
409     Q_INVOKABLE QStringList getBackgroundMusicFromRcc();
410     /**
411      * Stores a screenshot in the passed @p file.
412      *
413      * @param file Absolute destination filename.
414      */
415     Q_INVOKABLE void screenshot(const QString &file);
416 
417     void notifyPortraitMode();
418     Q_INVOKABLE void notifyFullscreenChanged();
419 
420 protected:
getSizeWithRatio(const qreal height)421     qreal getSizeWithRatio(const qreal height) { return ratio() * height; }
422 
423 signals:
424     void applicationWidthChanged();
425     void portraitModeChanged();
426     void ratioChanged();
427     void fontRatioChanged();
428     void applicationSettingsChanged();
429     void fullscreenChanged();
430     void useOpenGLChanged();
431     void isBox2DInstalledChanged();
432 
433 private:
434     static ApplicationInfo *m_instance;
435     int m_applicationWidth;
436     Platform m_platform;
437     bool m_isPortraitMode;
438     bool m_isMobile;
439     bool m_useOpenGL;
440     bool m_isBox2DInstalled;
441     qreal m_ratio;
442     qreal m_fontRatio;
443 
444     // Symbols fonts that user can't see
445     QStringList m_excludedFonts;
446     QStringList m_fontsFromRcc;
447     QStringList m_backgroundMusicFromRcc;
448 
449     static QQuickWindow *m_window;
450 };
451 
452 #endif // APPLICATIONINFO_H
453