diff options
Diffstat (limited to 'src/corelib/io')
-rw-r--r-- | src/corelib/io/qabstractfileengine_p.h | 2 | ||||
-rw-r--r-- | src/corelib/io/qbuffer.cpp | 4 | ||||
-rw-r--r-- | src/corelib/io/qdir.cpp | 19 | ||||
-rw-r--r-- | src/corelib/io/qfile.cpp | 11 | ||||
-rw-r--r-- | src/corelib/io/qfiledevice.cpp | 5 | ||||
-rw-r--r-- | src/corelib/io/qfileinfo.cpp | 22 | ||||
-rw-r--r-- | src/corelib/io/qfsfileengine.cpp | 2 | ||||
-rw-r--r-- | src/corelib/io/qfsfileengine_unix.cpp | 2 | ||||
-rw-r--r-- | src/corelib/io/qresource.cpp | 18 | ||||
-rw-r--r-- | src/corelib/io/qstandardpaths.cpp | 17 | ||||
-rw-r--r-- | src/corelib/io/qstandardpaths_android.cpp | 161 | ||||
-rw-r--r-- | src/corelib/io/qurl.cpp | 14 |
12 files changed, 186 insertions, 91 deletions
diff --git a/src/corelib/io/qabstractfileengine_p.h b/src/corelib/io/qabstractfileengine_p.h index bed4aa56d9..1322df1f68 100644 --- a/src/corelib/io/qabstractfileengine_p.h +++ b/src/corelib/io/qabstractfileengine_p.h @@ -238,7 +238,7 @@ public: virtual QString currentFileName() const = 0; virtual QFileInfo currentFileInfo() const; - QString currentFilePath() const; + virtual QString currentFilePath() const; protected: enum EntryInfoType { diff --git a/src/corelib/io/qbuffer.cpp b/src/corelib/io/qbuffer.cpp index 48150bdea3..a0b8fd8000 100644 --- a/src/corelib/io/qbuffer.cpp +++ b/src/corelib/io/qbuffer.cpp @@ -319,6 +319,10 @@ void QBuffer::setData(const QByteArray &data) /*! \reimp + + Unlike QFile, opening a QBuffer QIODevice::WriteOnly does not truncate it. + However, pos() is set to 0. Use QIODevice::Append or QIODevice::Truncate to + change either behavior. */ bool QBuffer::open(OpenMode flags) { diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp index 01340828c2..e2a410c64b 100644 --- a/src/corelib/io/qdir.cpp +++ b/src/corelib/io/qdir.cpp @@ -536,6 +536,10 @@ inline void QDirPrivate::initFileEngine() \snippet qdir-listfiles/main.cpp 0 + \section1 Platform Specific Issues + + \include android-content-uri-limitations.qdocinc + \sa QFileInfo, QFile, QFileDialog, QCoreApplication::applicationDirPath(), {Find Files Example} */ @@ -655,8 +659,12 @@ QString QDir::path() const QString QDir::absolutePath() const { const QDirPrivate* d = d_ptr.constData(); - d->resolveAbsoluteEntry(); - return d->absoluteDirEntry.filePath(); + if (!d->fileEngine) { + d->resolveAbsoluteEntry(); + return d->absoluteDirEntry.filePath(); + } + + return d->fileEngine->fileName(QAbstractFileEngine::AbsoluteName); } /*! @@ -699,7 +707,9 @@ QString QDir::canonicalPath() const QString QDir::dirName() const { const QDirPrivate* d = d_ptr.constData(); - return d->dirEntry.fileName(); + if (!d_ptr->fileEngine) + return d->dirEntry.fileName(); + return d->fileEngine->fileName(QAbstractFileEngine::BaseName); } @@ -1016,6 +1026,9 @@ bool QDir::cd(const QString &dirName) otherwise returns \c false. Note that the logical cdUp() operation is not performed if the new directory does not exist. + \note On Android, this is not supported for content URIs. For more information, + see \l {Android: DocumentFile.getParentFile()}{DocumentFile.getParentFile()}. + \sa cd(), isReadable(), exists(), path() */ bool QDir::cdUp() diff --git a/src/corelib/io/qfile.cpp b/src/corelib/io/qfile.cpp index e031e9c091..0bc206cbe8 100644 --- a/src/corelib/io/qfile.cpp +++ b/src/corelib/io/qfile.cpp @@ -226,6 +226,8 @@ QAbstractFileEngine *QFilePrivate::engine() const function mostly useless for NTFS volumes. It may still be of use for USB sticks that use VFAT file systems. POSIX ACLs are not manipulated, either. + \include android-content-uri-limitations.qdocinc + \sa QTextStream, QDataStream, QFileInfo, QDir, {The Qt Resource System} */ @@ -759,6 +761,9 @@ QFile::link(const QString &fileName, const QString &linkName) \include qfile-copy.qdocinc + \note On Android, this operation is not yet supported for \c content + scheme URIs. + \sa setFileName() */ @@ -867,6 +872,9 @@ QFile::copy(const QString &newName) \include qfile-copy.qdocinc + \note On Android, this operation is not yet supported for \c content + scheme URIs. + \sa rename() */ @@ -887,6 +895,9 @@ QFile::copy(const QString &fileName, const QString &newName) \note In \l{QIODevice::}{WriteOnly} or \l{QIODevice::}{ReadWrite} mode, if the relevant file does not already exist, this function will try to create a new file before opening it. + On Android, it's expected to have access permission to the parent + of the file name, otherwise, it won't be possible to create this + non-existing file. \sa QIODevice::OpenMode, setFileName() */ diff --git a/src/corelib/io/qfiledevice.cpp b/src/corelib/io/qfiledevice.cpp index ff8de7c0bb..4b73060025 100644 --- a/src/corelib/io/qfiledevice.cpp +++ b/src/corelib/io/qfiledevice.cpp @@ -145,6 +145,11 @@ void QFileDevicePrivate::setError(QFileDevice::FileError err, int errNum) decrementing \c qt_ntfs_permission_lookup by 1. \snippet ntfsp.cpp 1 + + \note Since this is a non-atomic global variable, it is only safe + to increment or decrement \c qt_ntfs_permission_lookup before any + threads other than the main thread have started or after every thread + other than the main thread has ended. */ //************* QFileDevice diff --git a/src/corelib/io/qfileinfo.cpp b/src/corelib/io/qfileinfo.cpp index 326dc01577..fab5ae91e7 100644 --- a/src/corelib/io/qfileinfo.cpp +++ b/src/corelib/io/qfileinfo.cpp @@ -297,6 +297,11 @@ QDateTime &QFileInfoPrivate::getFileTime(QAbstractFileEngine::FileTime request) \snippet ntfsp.cpp 1 + \note Since this is a non-atomic global variable, it is only safe + to increment or decrement \c qt_ntfs_permission_lookup before any + threads other than the main thread have started or after every thread + other than the main thread has ended. + \section1 Performance Issues Some of QFileInfo's functions query the file system, but for @@ -317,6 +322,10 @@ QDateTime &QFileInfoPrivate::getFileTime(QAbstractFileEngine::FileTime request) If you want to make sure that all information is read from the file system, use stat(). + \section1 Platform Specific Issues + + \include android-content-uri-limitations.qdocinc + \sa QDir, QFile */ @@ -772,7 +781,9 @@ QString QFileInfo::fileName() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return QLatin1String(""); - return d->fileEntry.fileName(); + if (!d->fileEngine) + return d->fileEntry.fileName(); + return d->fileEngine->fileName(QAbstractFileEngine::BaseName); } /*! @@ -816,7 +827,9 @@ QString QFileInfo::baseName() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return QLatin1String(""); - return d->fileEntry.baseName(); + if (!d->fileEngine) + return d->fileEntry.baseName(); + return QFileSystemEntry(d->fileEngine->fileName(QAbstractFileEngine::BaseName)).baseName(); } /*! @@ -835,7 +848,10 @@ QString QFileInfo::completeBaseName() const Q_D(const QFileInfo); if (d->isDefaultConstructed) return QLatin1String(""); - return d->fileEntry.completeBaseName(); + if (!d->fileEngine) + return d->fileEntry.completeBaseName(); + const QString fileEngineBaseName = d->fileEngine->fileName(QAbstractFileEngine::BaseName); + return QFileSystemEntry(fileEngineBaseName).completeBaseName(); } /*! diff --git a/src/corelib/io/qfsfileengine.cpp b/src/corelib/io/qfsfileengine.cpp index 394f21bd6d..4960bd6270 100644 --- a/src/corelib/io/qfsfileengine.cpp +++ b/src/corelib/io/qfsfileengine.cpp @@ -361,7 +361,7 @@ bool QFSFileEnginePrivate::openFd(QIODevice::OpenMode openMode, int fd) // Seek to the end when in Append mode. if (openMode & QFile::Append) { - int ret; + QT_OFF_T ret; do { ret = QT_LSEEK(fd, 0, SEEK_END); } while (ret == -1 && errno == EINTR); diff --git a/src/corelib/io/qfsfileengine_unix.cpp b/src/corelib/io/qfsfileengine_unix.cpp index 33d1047925..afee63b968 100644 --- a/src/corelib/io/qfsfileengine_unix.cpp +++ b/src/corelib/io/qfsfileengine_unix.cpp @@ -141,7 +141,7 @@ bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode) // Seek to the end when in Append mode. if (flags & QFile::Append) { - int ret; + QT_OFF_T ret; do { ret = QT_LSEEK(fd, 0, SEEK_END); } while (ret == -1 && errno == EINTR); diff --git a/src/corelib/io/qresource.cpp b/src/corelib/io/qresource.cpp index a1b818da8b..684d362251 100644 --- a/src/corelib/io/qresource.cpp +++ b/src/corelib/io/qresource.cpp @@ -201,7 +201,6 @@ struct QResourceGlobalData { QRecursiveMutex resourceMutex; ResourceList resourceList; - QStringList resourceSearchPaths; }; Q_GLOBAL_STATIC(QResourceGlobalData, resourceGlobalData) @@ -211,9 +210,6 @@ static inline QRecursiveMutex &resourceMutex() static inline ResourceList *resourceList() { return &resourceGlobalData->resourceList; } -static inline QStringList *resourceSearchPaths() -{ return &resourceGlobalData->resourceSearchPaths; } - /*! \class QResource \inmodule QtCore @@ -394,16 +390,10 @@ void QResourcePrivate::ensureInitialized() const if (path.startsWith(QLatin1Char('/'))) { that->load(path.toString()); } else { - const auto locker = qt_scoped_lock(resourceMutex()); - QStringList searchPaths = *resourceSearchPaths(); - searchPaths << QLatin1String(""); - for (int i = 0; i < searchPaths.size(); ++i) { - const QString searchPath(searchPaths.at(i) + QLatin1Char('/') + path); - if (that->load(searchPath)) { - that->absoluteFilePath = QLatin1Char(':') + searchPath; - break; - } - } + // Should we search QDir::searchPath() before falling back to root ? + const QString searchPath(u'/' + path); + if (that->load(searchPath)) + that->absoluteFilePath = u':' + searchPath; } } diff --git a/src/corelib/io/qstandardpaths.cpp b/src/corelib/io/qstandardpaths.cpp index 1984316a4e..33b0545ffe 100644 --- a/src/corelib/io/qstandardpaths.cpp +++ b/src/corelib/io/qstandardpaths.cpp @@ -267,7 +267,7 @@ QT_BEGIN_NAMESPACE \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) @@ -276,13 +276,13 @@ QT_BEGIN_NAMESPACE \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" @@ -297,7 +297,7 @@ QT_BEGIN_NAMESPACE \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" @@ -309,7 +309,7 @@ QT_BEGIN_NAMESPACE \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) @@ -339,6 +339,11 @@ QT_BEGIN_NAMESPACE \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 d5ece85818..2ff47d74d0 100644 --- a/src/corelib/io/qstandardpaths_android.cpp +++ b/src/corelib/io/qstandardpaths_android.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -70,6 +70,51 @@ 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::callStaticObjectMethod("android/os/Environment", + "getExternalStorageDirectory", + "()Ljava/io/File;"); + 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::getStaticObjectField("android/os/Environment", + directoryField, + "Ljava/lang/String;"); + if (!dirField.isValid()) + return QString(); + + QJniObject file = QJniObject::callStaticObjectMethod("android/os/Environment", + "getExternalStoragePublicDirectory", + "(Ljava/lang/String;)Ljava/io/File;", + dirField.object()); + if (!file.isValid()) + return QString(); + + return (path = getAbsolutePath(file)); +} + +/* * Locations where applications can place persistent files it owns. * E.g., /storage/org.app/Music */ @@ -168,25 +213,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() + QLatin1String("/settings"); case QStandardPaths::GenericDataLocation: - return getExternalFilesDir() + testDir(); + { + return QAndroidApplication::sdkVersion() >= 30 ? + getExternalFilesDir() + testDir() : getExternalStorageDirectory() + testDir(); + } case QStandardPaths::AppDataLocation: case QStandardPaths::AppLocalDataLocation: return getFilesDir() + testDir(); @@ -209,59 +264,53 @@ QString QStandardPaths::writableLocation(StandardLocation type) QStringList QStandardPaths::standardLocations(StandardLocation type) { - if (type == MusicLocation) { - return QStringList() << writableLocation(type) - << getExternalFilesDir("DIRECTORY_MUSIC") - << getExternalFilesDir("DIRECTORY_PODCASTS") - << getExternalFilesDir("DIRECTORY_NOTIFICATIONS") - << getExternalFilesDir("DIRECTORY_ALARMS"); - } - - if (type == MoviesLocation) { - return QStringList() << writableLocation(type) - << getExternalFilesDir("DIRECTORY_MOVIES"); - } - - if (type == PicturesLocation) { - return QStringList() << writableLocation(type) - << getExternalFilesDir("DIRECTORY_PICTURES"); - } + QStringList locations; - if (type == DocumentsLocation) { - return QStringList() << writableLocation(type) - << getExternalFilesDir("DIRECTORY_DOCUMENTS"); - } - - if (type == DownloadLocation) { - return QStringList() << writableLocation(type) - << getExternalFilesDir("DIRECTORY_DOWNLOADS"); - } - - if (type == AppDataLocation || type == AppLocalDataLocation) { - return QStringList() << writableLocation(type) - << getExternalFilesDir(); - } - - if (type == CacheLocation) { - return QStringList() << writableLocation(type) - << getExternalCacheDir(); - } - - if (type == FontsLocation) { + if (type == MusicLocation) { + 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) { + locations << getExternalFilesDir("DIRECTORY_MOVIES"); + } else if (type == PicturesLocation) { + locations << getExternalFilesDir("DIRECTORY_PICTURES"); + } else if (type == DocumentsLocation) { + locations << getExternalFilesDir("DIRECTORY_DOCUMENTS"); + } else if (type == DownloadLocation) { + locations << getExternalFilesDir("DIRECTORY_DOWNLOADS"); + } else if (type == AppDataLocation || type == AppLocalDataLocation) { + locations << getExternalFilesDir(); + } else if (type == CacheLocation) { + locations << getExternalCacheDir(); + } else if (type == FontsLocation) { QString &fontLocation = (*androidDirCache)[QStringLiteral("FONT_LOCATION")]; - if (!fontLocation.isEmpty()) - return QStringList(fontLocation); - - const QByteArray ba = qgetenv("QT_ANDROID_FONT_LOCATION"); - if (!ba.isEmpty()) - return QStringList((fontLocation = QDir::cleanPath(QString::fromLocal8Bit(ba)))); - - // Don't cache the fallback, as we might just have been called before - // QT_ANDROID_FONT_LOCATION has been set. - return QStringList(QLatin1String("/system/fonts")); + if (!fontLocation.isEmpty()) { + locations << fontLocation; + } else { + const QByteArray ba = qgetenv("QT_ANDROID_FONT_LOCATION"); + if (!ba.isEmpty()) { + locations << (fontLocation = QDir::cleanPath(QString::fromLocal8Bit(ba))); + } else { + // Don't cache the fallback, as we might just have been called before + // QT_ANDROID_FONT_LOCATION has been set. + locations << QLatin1String("/system/fonts"); + } + } } - return QStringList(writableLocation(type)); + const QString writable = writableLocation(type); + if (!writable.isEmpty()) + locations.prepend(writable); + + locations.removeDuplicates(); + return locations; } QT_END_NAMESPACE diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp index 27388ab2a4..ccf836c2a6 100644 --- a/src/corelib/io/qurl.cpp +++ b/src/corelib/io/qurl.cpp @@ -834,14 +834,16 @@ recodeFromUser(const QString &input, const ushort *actions, int from, int to) static inline void appendToUser(QString &appendTo, QStringView value, QUrl::FormattingOptions options, const ushort *actions) { - // Test ComponentFormattingOptions, ignore FormattingOptions. - if ((options & 0xFFFF0000) == QUrl::PrettyDecoded) { + // The stored value is already QUrl::PrettyDecoded, so there's nothing to + // do if that's what the user asked for (test only + // ComponentFormattingOptions, ignore FormattingOptions). + if ((options & 0xFFFF0000) == QUrl::PrettyDecoded || + !qt_urlRecode(appendTo, value, options, actions)) appendTo += value; - return; - } - if (!qt_urlRecode(appendTo, value, options, actions)) - appendTo += value; + // copy nullness, if necessary, because QString::operator+=(QStringView) doesn't + if (appendTo.isNull() && !value.isNull()) + appendTo.detach(); } inline void QUrlPrivate::appendAuthority(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const |