summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAssam Boudjelthia <assam.boudjelthia@qt.io>2023-02-25 17:37:43 +0200
committerAssam Boudjelthia <assam.boudjelthia@qt.io>2023-03-07 21:06:46 +0000
commit888de8bcf83926ba3adec7f2c500fae0c0cd129f (patch)
tree9258803fbc4adfbb2b5c8e801eb204791194b412
parent5560521a8c03e9ec3e900d3e903b2aed0e33992c (diff)
Android: fix and document QStandardPaths behavior on different versions
Partially revert e1440dd7bc1a5da9a536f88b9733d04ec8fa6e61 for Android versions below 11 which could take advantage of the manifest flag android:requestLegacyExternalStorage. And for other newer versions avoid returning them while adding a note to the docs about this behavior. Fixes: QTBUG-108013 Fixes: QTBUG-104892 Task-number: QTBUG-81860 Change-Id: I10851c20e2831bddaa329164c941e2ae71f0a497 Reviewed-by: Ville Voutilainen <ville.voutilainen@qt.io> Reviewed-by: Rami Potinkara <rami.potinkara@qt.io> (cherry picked from commit 81a748efb742092f5a0a1c33b8340478e52cc79f) Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
-rw-r--r--src/corelib/io/qstandardpaths.cpp17
-rw-r--r--src/corelib/io/qstandardpaths_android.cpp79
2 files changed, 81 insertions, 15 deletions
diff --git a/src/corelib/io/qstandardpaths.cpp b/src/corelib/io/qstandardpaths.cpp
index ec8e6899e4..a20c50e096 100644
--- a/src/corelib/io/qstandardpaths.cpp
+++ b/src/corelib/io/qstandardpaths.cpp
@@ -250,7 +250,7 @@ using namespace Qt::StringLiterals;
\li "<APPROOT>/files"
\li "<APPROOT>/Documents/Desktop"
\row \li DocumentsLocation
- \li "<USER>/Documents", "<USER>/<APPNAME>/Documents"
+ \li "<USER>/Documents" [*], "<USER>/<APPNAME>/Documents"
\li "<APPROOT>/Documents"
\row \li FontsLocation
\li "/system/fonts" (not writable)
@@ -259,13 +259,13 @@ using namespace Qt::StringLiterals;
\li not supported (directory not readable)
\li not supported
\row \li MusicLocation
- \li "<USER>/Music", "<USER>/<APPNAME>/Music"
+ \li "<USER>/Music" [*], "<USER>/<APPNAME>/Music"
\li "<APPROOT>/Documents/Music"
\row \li MoviesLocation
- \li "<USER>/Movies", "<USER>/<APPNAME>/Movies"
+ \li "<USER>/Movies" [*], "<USER>/<APPNAME>/Movies"
\li "<APPROOT>/Documents/Movies"
\row \li PicturesLocation
- \li "<USER>/Pictures", "<USER>/<APPNAME>/Pictures"
+ \li "<USER>/Pictures" [*], "<USER>/<APPNAME>/Pictures"
\li "<APPROOT>/Documents/Pictures", "assets-library://"
\row \li TempLocation
\li "<APPROOT>/cache"
@@ -280,7 +280,7 @@ using namespace Qt::StringLiterals;
\li "<APPROOT>/cache", "<USER>/<APPNAME>/cache"
\li "<APPROOT>/Library/Caches"
\row \li GenericDataLocation
- \li "<USER>"
+ \li "<USER>" [*] or "<USER>/<APPNAME>/files"
\li "<APPROOT>/Library/Application Support"
\row \li RuntimeLocation
\li "<APPROOT>/cache"
@@ -292,7 +292,7 @@ using namespace Qt::StringLiterals;
\li "<APPROOT>/files/settings" (there is no shared settings)
\li "<APPROOT>/Library/Preferences"
\row \li DownloadLocation
- \li "<USER>/Downloads", "<USER>/<APPNAME>/Downloads"
+ \li "<USER>/Downloads" [*], "<USER>/<APPNAME>/Downloads"
\li "<APPROOT>/Documents/Downloads"
\row \li GenericCacheLocation
\li "<APPROOT>/cache" (there is no shared cache)
@@ -328,6 +328,11 @@ using namespace Qt::StringLiterals;
\note On Android, reading/writing to GenericDataLocation needs the READ_EXTERNAL_STORAGE/WRITE_EXTERNAL_STORAGE permission granted.
+ \note [*] On Android 11 and above, public directories are no longer directly accessible
+ in scoped storage mode. Thus, paths of the form \c "<USER>/DirName" are not returned.
+ Instead, you can use \l QFileDialog which uses the Storage Access Framework (SAF)
+ to access such directories.
+
\note On iOS, if you do pass \c {QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).last()}
as argument to \l{QFileDialog::setDirectory()},
a native image picker dialog will be used for accessing the user's photo album.
diff --git a/src/corelib/io/qstandardpaths_android.cpp b/src/corelib/io/qstandardpaths_android.cpp
index efc3661e23..a911087c9c 100644
--- a/src/corelib/io/qstandardpaths_android.cpp
+++ b/src/corelib/io/qstandardpaths_android.cpp
@@ -1,4 +1,4 @@
-// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qstandardpaths.h"
@@ -12,6 +12,9 @@
QT_BEGIN_NAMESPACE
+Q_DECLARE_JNI_CLASS(Environment, "android/os/Environment");
+Q_DECLARE_JNI_TYPE(File, "Ljava/io/File;");
+
using namespace QNativeInterface;
using namespace Qt::StringLiterals;
@@ -34,6 +37,48 @@ static inline QString getAbsolutePath(const QJniObject &file)
}
/*
+ * The root of the external storage
+ *
+ */
+static QString getExternalStorageDirectory()
+{
+ QString &path = (*androidDirCache)[QStringLiteral("EXT_ROOT")];
+ if (!path.isEmpty())
+ return path;
+
+ QJniObject file = QJniObject::callStaticMethod<QtJniTypes::File>("android/os/Environment",
+ "getExternalStorageDirectory");
+ if (!file.isValid())
+ return QString();
+
+ return (path = getAbsolutePath(file));
+}
+
+/*
+ * Locations where applications can place user files shared by all apps (public).
+ * E.g., /storage/Music
+ */
+static QString getExternalStoragePublicDirectory(const char *directoryField)
+{
+ QString &path = (*androidDirCache)[QLatin1String(directoryField)];
+ if (!path.isEmpty())
+ return path;
+
+ QJniObject dirField = QJniObject::getStaticField<jstring>("android/os/Environment",
+ directoryField);
+ if (!dirField.isValid())
+ return QString();
+
+ QJniObject file = QJniObject::callStaticMethod<QtJniTypes::File>("android/os/Environment",
+ "getExternalStoragePublicDirectory",
+ dirField.object<jstring>());
+ if (!file.isValid())
+ return QString();
+
+ return (path = getAbsolutePath(file));
+}
+
+/*
* Locations where applications can place persistent files it owns.
* E.g., /storage/org.app/Music
*/
@@ -132,25 +177,35 @@ static QString getFilesDir()
return (path = getAbsolutePath(file));
}
+static QString getSdkBasedExternalDir(const char *directoryField = nullptr)
+{
+ return (QNativeInterface::QAndroidApplication::sdkVersion() >= 30)
+ ? getExternalFilesDir(directoryField)
+ : getExternalStoragePublicDirectory(directoryField);
+}
+
QString QStandardPaths::writableLocation(StandardLocation type)
{
switch (type) {
case QStandardPaths::MusicLocation:
- return getExternalFilesDir("DIRECTORY_MUSIC");
+ return getSdkBasedExternalDir("DIRECTORY_MUSIC");
case QStandardPaths::MoviesLocation:
- return getExternalFilesDir("DIRECTORY_MOVIES");
+ return getSdkBasedExternalDir("DIRECTORY_MOVIES");
case QStandardPaths::PicturesLocation:
- return getExternalFilesDir("DIRECTORY_PICTURES");
+ return getSdkBasedExternalDir("DIRECTORY_PICTURES");
case QStandardPaths::DocumentsLocation:
- return getExternalFilesDir("DIRECTORY_DOCUMENTS");
+ return getSdkBasedExternalDir("DIRECTORY_DOCUMENTS");
case QStandardPaths::DownloadLocation:
- return getExternalFilesDir("DIRECTORY_DOWNLOADS");
+ return getSdkBasedExternalDir("DIRECTORY_DOWNLOADS");
case QStandardPaths::GenericConfigLocation:
case QStandardPaths::ConfigLocation:
case QStandardPaths::AppConfigLocation:
return getFilesDir() + testDir() + "/settings"_L1;
case QStandardPaths::GenericDataLocation:
- return getExternalFilesDir() + testDir();
+ {
+ return QAndroidApplication::sdkVersion() >= 30 ?
+ getExternalFilesDir() + testDir() : getExternalStorageDirectory() + testDir();
+ }
case QStandardPaths::AppDataLocation:
case QStandardPaths::AppLocalDataLocation:
return getFilesDir() + testDir();
@@ -178,8 +233,14 @@ QStringList QStandardPaths::standardLocations(StandardLocation type)
QStringList locations;
if (type == MusicLocation) {
- locations << getExternalFilesDir("DIRECTORY_MUSIC")
- << getExternalFilesDir("DIRECTORY_PODCASTS")
+ locations << getExternalFilesDir("DIRECTORY_MUSIC");
+ // Place the public dirs before the app own dirs
+ if (QNativeInterface::QAndroidApplication::sdkVersion() < 30) {
+ locations << getExternalStoragePublicDirectory("DIRECTORY_PODCASTS")
+ << getExternalStoragePublicDirectory("DIRECTORY_NOTIFICATIONS")
+ << getExternalStoragePublicDirectory("DIRECTORY_ALARMS");
+ }
+ locations << getExternalFilesDir("DIRECTORY_PODCASTS")
<< getExternalFilesDir("DIRECTORY_NOTIFICATIONS")
<< getExternalFilesDir("DIRECTORY_ALARMS");
} else if (type == MoviesLocation) {