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