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

Commenting for this post has ended.

E
ekke
1 point
25 months ago

thx for all your work on this. Will test as soon as 5.15.13 will be out and later also on 6.5.

E
ekke
1 point
25 months ago

Thus, Qt's default manifest will come with an initial file provider element.just testing 5.15.13... <

from your blog I expexted that now if creating Android templates the Manifest contains a provider element. There's no one in 5.15.13 - or do I have misunderstood ?

C
cameron
0 points
25 months ago

Android 10, brings about Scoped Storage with various access restrictions, see app access restrictions.

Shouldn't this read "Android 11"?

Scoped storage limits app access to external storage. In Android 11 or higher, apps targeting API 30 or higher must use scoped storage. Previously in Android 10, apps could opt out of scoped storage [1].

--

However, Android 11 adds a new permission MANAGEEXTERNALSTORAGE which allows full storage access that can go around the restrictions ( while requiring a manual review by Play Store).

Some additional detail could be added here. Only certain applications can use this permission in the Play Store. See [2]

[1] https://source.android.com/docs/core/storage/scoped [2] https://support.google.com/googleplay/android-developer/answer/10467955?hl=en#zippy=%2Cpermitted-uses-of-the-all-files-access-permission

Alexander Dyagilev
0 points
23 months ago

"This will be coming in Qt 6.6." - what does it mean? QDesktopServices content scheme support in Qt 6.6+ only? "QDesktopServices::openUrl(fileName)" - is this a bug? Shouldn't it be QDesktopServices::openUrl(QUrl::fromLocalFile(fileName)) instead?

D
damiane
0 points
2 months ago

PNW Warehousing is a game-changer for rework freight! Their team is knowledgeable, responsive, and super easy to work with. I had a tight deadline, and they delivered beyond my expectations. Learn more about their services here: https://pnwwarehousing.com/rework-freight.html Definitely my go-to from now on!