[Android-development] QDesktopServices::openUrl in Qt 5.15

Sandro Andrade sandroandrade at kde.org
Thu May 7 21:46:43 CEST 2020


To whom it may concern, this is how I sorted out this issue. Anyway,
QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)
should be fixed for Android 10, since it currently uses the deprecated
Environment.getExternalStoragePublicDirectory() method, which returns
a
non-writable location.

Cheers,
--
Sandro

Android 10 introduced Scoped Storage, which forbids applications to
directly write in QStandardPaths::DownloadLocation path. Instead,
applications should write to app-specific (as
QStandardPaths::AppDataLocation). If app-specific files are expected
to be handled by other applications (like the downloaded PDF we want
to exhibit in Emile) we must use FileProvider to create a content URI
and grant the required URI permissions. In Android,
QDesktopServices::openUrl() checks for those permissions before using
Intent.ACTION_VIEW to display the Url.

Summary of required steps:

1) AndroidManifest.xml shouldn't contain
android:requestLegacyExternalStorage="true" since this disables Scoped
Storage.
2) Since we are using a FileProvider, there is no need for the
StrictMode.disableDeathOnFileUriExposure hack anymore.
3) FileProvider is provided by AndroidX. To enable AndroidX in our Qt
application we need the following:
    3.1) Add GRADLE_OPTS="-Dorg.gradle.project.android.useAndroidX=true
-Dorg.gradle.project.android.enableJetifier=true"
         in android/gradlew right beneath the 'DEFAULT_JVM_OPTS=""' line
    3.2) Add implementation 'androidx.appcompat:appcompat:1.1.0' at
the 'dependencies' section of android/build.gradle
4) Configure the file provider:
    4.1) Add <provider> block in AndroidManifest.xml
    4.2) Create android/res/xml/filepaths.xml with the provided
app-specific directories
5) After downloading the file into a *really* writable location, we
should create a content URI and setup permissions:
    5.1) Uri contentUri = FileProvider.getUriForFile(activity,
"<your-provider-authority>", new File(fileName));
    5.2) activity.grantUriPermission(activity.getPackageName(),
contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
    5.3) activity.getContentResolver().takePersistableUriPermission
(contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
6) Then, we can finally invoke QDesktopServices::openUrl with the
created content URI

On Wed, May 6, 2020 at 10:43 PM Sandro Andrade <sandroandrade at kde.org> wrote:
>
> Hi there,
>
> With latest Android changes for user storage handling, I've noticed
> that QDesktopServices::openUrl() now checks for Uri's with valid
> permissions in Android.
>
> My case is as follows:
> 1) I download a remote PDF file into the
> QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)
> directory
> 2) Call QDesktopServices::openUrl() to view PDF file using some
> Android system viewer
>
> This worked fine in previous Qt for Android releases. With Qt 5.15 it
> fails with a "openURL(): No permissions to open Uri" message,
> generated by QtNative::openURL(), since no Uri permissions are found.
>
> I tried to add Uri permissions like the ones presented in
> https://codereview.qt-project.org/gitweb?p=qt%2Fqtbase.git;a=commit;h=dcb38e4bc74337d0d670bc30b8193cbd73f11599
>
> I basically invoke a JNI method which does the following:
>
> Uri uri = Uri.parse(fileName);
> activity.getContentResolver().takePersistableUriPermission(uri,
> Intent.FLAG_GRANT_READ_URI_PERMISSION |
> Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
>
> But invoking takePersistableUriPermission fails with a:
> java.lang.SecurityException: No persistable permission grants found
> for UID 10632 and Uri
> file:///emulated/0/Download/historico-escolar-parcial.pdf
>
> I also tried to store the downloaded PDF file in
> QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) with
> no success.
>
> Any hints on how to properly grant Uri permissions for downloaded files?
>
> Thanks in advance,
> --
> Sandro


More information about the Android-development mailing list