1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qstandardpaths.h"
41 
42 #ifndef QT_NO_STANDARDPATHS
43 
44 #include <QtCore/private/qjni_p.h>
45 #include <QtCore/private/qjnihelpers_p.h>
46 #include <QtCore/qmap.h>
47 #include <QDir>
48 
49 QT_BEGIN_NAMESPACE
50 
51 typedef QMap<QString, QString> AndroidDirCache;
Q_GLOBAL_STATIC(AndroidDirCache,androidDirCache)52 Q_GLOBAL_STATIC(AndroidDirCache, androidDirCache)
53 
54 static QString testDir()
55 {
56     return QStandardPaths::isTestModeEnabled() ? QLatin1String("/qttest")
57                                                : QLatin1String("");
58 }
59 
applicationContext()60 static QJNIObjectPrivate applicationContext()
61 {
62     static QJNIObjectPrivate appCtx;
63     if (appCtx.isValid())
64         return appCtx;
65 
66     QJNIObjectPrivate context(QtAndroidPrivate::activity());
67     if (!context.isValid()) {
68         context = QtAndroidPrivate::service();
69         if (!context.isValid())
70             return appCtx;
71     }
72 
73     appCtx = context.callObjectMethod("getApplicationContext",
74                                       "()Landroid/content/Context;");
75     return appCtx;
76 }
77 
getAbsolutePath(const QJNIObjectPrivate & file)78 static inline QString getAbsolutePath(const QJNIObjectPrivate &file)
79 {
80     QJNIObjectPrivate path = file.callObjectMethod("getAbsolutePath",
81                                                    "()Ljava/lang/String;");
82     if (!path.isValid())
83         return QString();
84 
85     return path.toString();
86 }
87 
88 /*
89  * Locations where applications can place persistent files it owns.
90  * E.g., /storage/org.app/Music
91  */
getExternalFilesDir(const char * directoryField=0)92 static QString getExternalFilesDir(const char *directoryField = 0)
93 {
94     QString &path = (*androidDirCache)[QLatin1String("APPNAME_%1").arg(QLatin1String(directoryField))];
95     if (!path.isEmpty())
96         return path;
97 
98     QJNIObjectPrivate appCtx = applicationContext();
99     if (!appCtx.isValid())
100         return QString();
101 
102     QJNIObjectPrivate dirField = QJNIObjectPrivate::fromString(QLatin1String(""));
103     if (directoryField && strlen(directoryField) > 0) {
104         dirField = QJNIObjectPrivate::getStaticObjectField("android/os/Environment",
105                                                            directoryField,
106                                                            "Ljava/lang/String;");
107         if (!dirField.isValid())
108             return QString();
109     }
110 
111     QJNIObjectPrivate file = appCtx.callObjectMethod("getExternalFilesDir",
112                                                      "(Ljava/lang/String;)Ljava/io/File;",
113                                                      dirField.object());
114 
115     if (!file.isValid())
116         return QString();
117 
118     return (path = getAbsolutePath(file));
119 }
120 
121 /*
122  * Directory where applications can store cache files it owns (public).
123  * E.g., /storage/org.app/
124  */
getExternalCacheDir()125 static QString getExternalCacheDir()
126 {
127     QString &path = (*androidDirCache)[QStringLiteral("APPNAME_CACHE")];
128     if (!path.isEmpty())
129         return path;
130 
131     QJNIObjectPrivate appCtx = applicationContext();
132     if (!appCtx.isValid())
133         return QString();
134 
135     QJNIObjectPrivate file = appCtx.callObjectMethod("getExternalCacheDir",
136                                                      "()Ljava/io/File;");
137 
138     if (!file.isValid())
139         return QString();
140 
141     return (path = getAbsolutePath(file));
142 }
143 
144 /*
145  * Directory where applications can store cache files it owns (private).
146  */
getCacheDir()147 static QString getCacheDir()
148 {
149     QString &path = (*androidDirCache)[QStringLiteral("APPROOT_CACHE")];
150     if (!path.isEmpty())
151         return path;
152 
153     QJNIObjectPrivate appCtx = applicationContext();
154     if (!appCtx.isValid())
155         return QString();
156 
157     QJNIObjectPrivate file = appCtx.callObjectMethod("getCacheDir",
158                                                      "()Ljava/io/File;");
159     if (!file.isValid())
160         return QString();
161 
162     return (path = getAbsolutePath(file));
163 }
164 
165 /*
166  * Directory where applications can store files it owns (private).
167  * (Same location as $HOME)
168  */
getFilesDir()169 static QString getFilesDir()
170 {
171     QString &path = (*androidDirCache)[QStringLiteral("APPROOT_FILES")];
172     if (!path.isEmpty())
173         return path;
174 
175     QJNIObjectPrivate appCtx = applicationContext();
176     if (!appCtx.isValid())
177         return QString();
178 
179     QJNIObjectPrivate file = appCtx.callObjectMethod("getFilesDir",
180                                                      "()Ljava/io/File;");
181     if (!file.isValid())
182         return QString();
183 
184     return (path = getAbsolutePath(file));
185 }
186 
writableLocation(StandardLocation type)187 QString QStandardPaths::writableLocation(StandardLocation type)
188 {
189     switch (type) {
190     case QStandardPaths::MusicLocation:
191         return getExternalFilesDir("DIRECTORY_MUSIC");
192     case QStandardPaths::MoviesLocation:
193         return getExternalFilesDir("DIRECTORY_MOVIES");
194     case QStandardPaths::PicturesLocation:
195         return getExternalFilesDir("DIRECTORY_PICTURES");
196     case QStandardPaths::DocumentsLocation:
197         return getExternalFilesDir("DIRECTORY_DOCUMENTS");
198     case QStandardPaths::DownloadLocation:
199         return getExternalFilesDir("DIRECTORY_DOWNLOADS");
200     case QStandardPaths::GenericConfigLocation:
201     case QStandardPaths::ConfigLocation:
202     case QStandardPaths::AppConfigLocation:
203         return getFilesDir() + testDir() + QLatin1String("/settings");
204     case QStandardPaths::GenericDataLocation:
205         return getExternalFilesDir() + testDir();
206     case QStandardPaths::AppDataLocation:
207     case QStandardPaths::AppLocalDataLocation:
208         return getFilesDir() + testDir();
209     case QStandardPaths::GenericCacheLocation:
210     case QStandardPaths::RuntimeLocation:
211     case QStandardPaths::TempLocation:
212     case QStandardPaths::CacheLocation:
213         return getCacheDir() + testDir();
214     case QStandardPaths::DesktopLocation:
215     case QStandardPaths::HomeLocation:
216         return getFilesDir();
217     case QStandardPaths::ApplicationsLocation:
218     case QStandardPaths::FontsLocation:
219     default:
220         break;
221     }
222 
223     return QString();
224 }
225 
standardLocations(StandardLocation type)226 QStringList QStandardPaths::standardLocations(StandardLocation type)
227 {
228     if (type == MusicLocation) {
229         return QStringList() << writableLocation(type)
230                              << getExternalFilesDir("DIRECTORY_MUSIC")
231                              << getExternalFilesDir("DIRECTORY_PODCASTS")
232                              << getExternalFilesDir("DIRECTORY_NOTIFICATIONS")
233                              << getExternalFilesDir("DIRECTORY_ALARMS");
234     }
235 
236     if (type == MoviesLocation) {
237         return QStringList() << writableLocation(type)
238                              << getExternalFilesDir("DIRECTORY_MOVIES");
239     }
240 
241     if (type == PicturesLocation) {
242         return QStringList()  << writableLocation(type)
243                               << getExternalFilesDir("DIRECTORY_PICTURES");
244     }
245 
246     if (type == DocumentsLocation) {
247         return QStringList() << writableLocation(type)
248                              << getExternalFilesDir("DIRECTORY_DOCUMENTS");
249     }
250 
251     if (type == DownloadLocation) {
252         return QStringList() << writableLocation(type)
253                              << getExternalFilesDir("DIRECTORY_DOWNLOADS");
254     }
255 
256     if (type == AppDataLocation || type == AppLocalDataLocation) {
257         return QStringList() << writableLocation(type)
258                              << getExternalFilesDir();
259     }
260 
261     if (type == CacheLocation) {
262         return QStringList() << writableLocation(type)
263                              << getExternalCacheDir();
264     }
265 
266     if (type == FontsLocation) {
267         QString &fontLocation = (*androidDirCache)[QStringLiteral("FONT_LOCATION")];
268         if (!fontLocation.isEmpty())
269             return QStringList(fontLocation);
270 
271         const QByteArray ba = qgetenv("QT_ANDROID_FONT_LOCATION");
272         if (!ba.isEmpty())
273             return QStringList((fontLocation = QDir::cleanPath(QString::fromLocal8Bit(ba))));
274 
275         // Don't cache the fallback, as we might just have been called before
276         // QT_ANDROID_FONT_LOCATION has been set.
277         return QStringList(QLatin1String("/system/fonts"));
278     }
279 
280     return QStringList(writableLocation(type));
281 }
282 
283 QT_END_NAMESPACE
284 
285 #endif // QT_NO_STANDARDPATHS
286