diff options
author | Assam Boudjelthia <assam.boudjelthia@qt.io> | 2023-02-25 17:37:43 +0200 |
---|---|---|
committer | Assam Boudjelthia <assam.boudjelthia@qt.io> | 2023-03-07 21:06:46 +0000 |
commit | 888de8bcf83926ba3adec7f2c500fae0c0cd129f (patch) | |
tree | 9258803fbc4adfbb2b5c8e801eb204791194b412 | |
parent | 5560521a8c03e9ec3e900d3e903b2aed0e33992c (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.cpp | 17 | ||||
-rw-r--r-- | src/corelib/io/qstandardpaths_android.cpp | 79 |
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) { |