Qt for Android Storage Updates

Keeping up with the constant changes in Android's APIs, Qt is adding some improvements when managing files and content URIs under the restrictions introduced with Android's Scoped Storage.

Improvements for content URIs handling

Now, Qt classes support a few more operations that have been missing when dealing with content scheme URIs. Qt's Android content file engine can now handle creating, removing, renaming, and moving files and directories. However, this is available only when possible and when the appropriate permissions are available for example through the Android Storage Framework that's accessible through QFileDialog. We implement a hierarchy similar to Android's DocumentFile and use DocumentsContract for some file operations under the hood.

Furthermore, since content URIs have their own format where only part of the path is percent-encoded, it was hard to deal with file names. Now, Qt's classes like QFile, QDir and QFileInfo got fixed to properly deal with content URIs. These include properly returning absolute paths, file and base names, dir names, etc.

As part of these changes, a bug where using QDirIterator was returning unusable subdirectories paths or fully stuck on an infinite loop when iterating certain directories, is now fixed as well.

With those changes, it's worth mentioning, we don't yet fully integrate with the MediaStore API and SAF via QFileDialog can still be used to grant access when needed.

Android 10, brings about Scoped Storage with various access restrictions, see app access restrictions. However, Android 11 adds a new permission MANAGE_EXTERNAL_STORAGE which allows full storage access that can go around the restrictions, however, note that this permission is not possible for all apps on the Play Store. To read more about that, check Google's policy regarding this permission.

QStandardPaths

With the introduction of Scoped Storage, it's recommended to use app-specific directories over external public directories. The latter directories will still be returned by QStandardPaths::writableLocation() when usable, otherwise, the app private locations are returned based on the runtime Android version. Along, the way, a bug where potentially duplicate paths were returned by QStandardPaths::standardLocations() was fixed. Appropriate notes were added to our documentation.

Note that, all  the changes mentioned so far are back ported to the currently supported Qt releases.

QDesktopServices and FileProvider

Qt's QDesktopServices now support Android's FileProvider under the hood. This allows Qt apps to easily open or share files that can be stored under the app's private storage, with other apps. Thus, Qt's default manifest will come with an initial file provider element. The quick snippet below creates a file under the app's private/sandbox storage and when calling openUrl(), Qt uses FileProvider facilities to obtain a shareable content URI.

const auto standardPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
const auto fileName = standardPath + "/text.txt";
QFile file(fileName);
file.open(QIODevice::WriteOnly);
file.write("Sample text");
file.close();

QDesktopServices::openUrl(fileName);

Note that, this effectively adds AndroidX dependency by default to Gradle configuration file (i.e. build.gradle). This will be coming in Qt 6.6.


Blog Topics:

Comments