Android: fix and document QStandardPaths behavior on different versions
Partially revert e1440dd7bc
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>
This commit is contained in:
parent
1e50420354
commit
81a748efb7
@ -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.
|
||||
|
@ -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"
|
||||
@ -36,6 +36,48 @@ static inline QString getAbsolutePath(const QJniObject &file)
|
||||
return path.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
|
Loading…
Reference in New Issue
Block a user