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-01 22:10:57 +0200 |
| commit | 81a748efb742092f5a0a1c33b8340478e52cc79f (patch) | |
| tree | d1819dc8ed95fdc34cbd4bc1b82aaccff576c694 /src | |
| parent | 1e50420354fc717b665cb8398658e97b89a7f953 (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.
Pick-to: 6.5 6.4 6.2 5.15
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>
Diffstat (limited to 'src')
| -rw-r--r-- | src/corelib/io/qstandardpaths.cpp | 17 | ||||
| -rw-r--r-- | src/corelib/io/qstandardpaths_android.cpp | 76 |
2 files changed, 78 insertions, 15 deletions
diff --git a/src/corelib/io/qstandardpaths.cpp b/src/corelib/io/qstandardpaths.cpp index ec8e6899e4c..a20c50e096c 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 4d5664d3ff8..e058f379c28 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" @@ -37,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 */ @@ -129,25 +171,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(); @@ -175,8 +227,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) { |
