summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAssam Boudjelthia <assam.boudjelthia@qt.io>2022-08-24 21:06:45 +0300
committerAssam Boudjelthia <assam.boudjelthia@qt.io>2023-02-24 17:45:10 +0200
commit738c48244bb7fe2a29e5f8f537a6f6c2ebb33008 (patch)
tree40e170713bba44d9246de5f6f0274e32b25ea9b6
parentb8d51001f7d1c40cced642b4a1d990c90244706f (diff)
Android: use FileProvider with QDesktopServices::openUrl()
Allow openUrl() to use FileProvider for opening files that are located under app scoped paths and that use a file scheme for Android sdk 24 or above. [ChangeLog][Core][Android] Add FileProvider support for QDesktopServices::openUrl(). [ChangeLog][Core][Android] Add AndroidX dependency to Gradle builds by default since it's required by FileProvider. Fixes: QTBUG-85238 Change-Id: Ia7403f74f2a8fd4886f74dba72e42b318ef5d079 Reviewed-by: Konstantin Ritt <ritt.ks@gmail.com> Reviewed-by: Nicholas Bennett <nicholas.bennett@qt.io> Reviewed-by: Ville Voutilainen <ville.voutilainen@qt.io>
-rw-r--r--src/3rdparty/gradle/gradle.properties3
-rw-r--r--src/android/templates/AndroidManifest.xml10
-rw-r--r--src/android/templates/CMakeLists.txt1
-rw-r--r--src/android/templates/build.gradle1
-rw-r--r--src/android/templates/res/xml/qtprovider_paths.xml4
-rw-r--r--src/gui/doc/src/external-resources.qdoc10
-rw-r--r--src/gui/util/qdesktopservices.cpp7
-rw-r--r--src/plugins/platforms/android/qandroidplatformservices.cpp40
8 files changed, 70 insertions, 6 deletions
diff --git a/src/3rdparty/gradle/gradle.properties b/src/3rdparty/gradle/gradle.properties
index 263d70238a..8308822585 100644
--- a/src/3rdparty/gradle/gradle.properties
+++ b/src/3rdparty/gradle/gradle.properties
@@ -12,3 +12,6 @@ org.gradle.parallel=true
# build with the same inputs. However, over time, the cache size will
# grow. Uncomment the following line to enable it.
#org.gradle.caching=true
+
+# Allow AndroidX usage
+android.useAndroidX=true
diff --git a/src/android/templates/AndroidManifest.xml b/src/android/templates/AndroidManifest.xml
index d7438317b8..8f91863719 100644
--- a/src/android/templates/AndroidManifest.xml
+++ b/src/android/templates/AndroidManifest.xml
@@ -43,5 +43,15 @@
android:name="android.app.extract_android_style"
android:value="minimal" />
</activity>
+
+ <provider
+ android:name="androidx.core.content.FileProvider"
+ android:authorities="${applicationId}.qtprovider"
+ android:exported="false"
+ android:grantUriPermissions="true">
+ <meta-data
+ android:name="android.support.FILE_PROVIDER_PATHS"
+ android:resource="@xml/qtprovider_paths"/>
+ </provider>
</application>
</manifest>
diff --git a/src/android/templates/CMakeLists.txt b/src/android/templates/CMakeLists.txt
index 0d710f72f4..94f3243c22 100644
--- a/src/android/templates/CMakeLists.txt
+++ b/src/android/templates/CMakeLists.txt
@@ -14,6 +14,7 @@ add_custom_target(Qt${QtBase_VERSION_MAJOR}AndroidTemplates
SOURCES
${template_files}
"${CMAKE_CURRENT_SOURCE_DIR}/res/values/libs.xml"
+ "${CMAKE_CURRENT_SOURCE_DIR}/res/xml/qtprovider_paths.xml"
)
qt_path_join(destination ${QT_INSTALL_DIR} ${INSTALL_DATADIR} "src/android/templates")
diff --git a/src/android/templates/build.gradle b/src/android/templates/build.gradle
index f6c2f3d56d..33867903c2 100644
--- a/src/android/templates/build.gradle
+++ b/src/android/templates/build.gradle
@@ -18,6 +18,7 @@ apply plugin: 'com.android.application'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
+ implementation 'androidx.core:core:1.8.0'
}
android {
diff --git a/src/android/templates/res/xml/qtprovider_paths.xml b/src/android/templates/res/xml/qtprovider_paths.xml
new file mode 100644
index 0000000000..ae5b4b6074
--- /dev/null
+++ b/src/android/templates/res/xml/qtprovider_paths.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<paths xmlns:android="http://schemas.android.com/apk/res/android">
+ <files-path name="files_path" path="/"/>
+</paths>
diff --git a/src/gui/doc/src/external-resources.qdoc b/src/gui/doc/src/external-resources.qdoc
index cea63f59cc..67ba9f3013 100644
--- a/src/gui/doc/src/external-resources.qdoc
+++ b/src/gui/doc/src/external-resources.qdoc
@@ -46,3 +46,13 @@
\externalpage https://www.lunarg.com/vulkan-sdk/
\title LunarG Vulkan SDK
*/
+
+/*!
+ \externalpage https://developer.android.com/reference/androidx/core/content/FileProvider
+ \title Android: FileProvider
+*/
+
+/*!
+ \externalpage https://developer.android.com/training/secure-file-sharing/setup-sharing.html
+ \title Android: Setting up file sharing
+*/
diff --git a/src/gui/util/qdesktopservices.cpp b/src/gui/util/qdesktopservices.cpp
index ae452885fc..4a12f6db6f 100644
--- a/src/gui/util/qdesktopservices.cpp
+++ b/src/gui/util/qdesktopservices.cpp
@@ -162,6 +162,13 @@ void QOpenUrlHandlerRegistry::handlerDestroyed(QObject *handler)
\snippet code/src_gui_util_qdesktopservices.cpp 3
+ \note For Android Nougat (SDK 24) and above, URLs with a \c file scheme
+ are opened using \l {Android: FileProvider}{FileProvider} which tries to obtain
+ a shareable \c content scheme URI first. For that reason, Qt for Android defines
+ a file provider with the authority \c ${applicationId}.qtprovider, with \c applicationId
+ being the app's package name to avoid name conflicts. For more information, also see
+ \l {Android: Setting up file sharing}{Setting up file sharing}.
+
\sa setUrlHandler()
*/
bool QDesktopServices::openUrl(const QUrl &url)
diff --git a/src/plugins/platforms/android/qandroidplatformservices.cpp b/src/plugins/platforms/android/qandroidplatformservices.cpp
index 2599fe6db2..5964236420 100644
--- a/src/plugins/platforms/android/qandroidplatformservices.cpp
+++ b/src/plugins/platforms/android/qandroidplatformservices.cpp
@@ -35,6 +35,11 @@ QAndroidPlatformServices::QAndroidPlatformServices()
Qt::QueuedConnection);
}
+Q_DECLARE_JNI_TYPE(UriType, "Landroid/net/Uri;")
+Q_DECLARE_JNI_TYPE(FileType, "Ljava/io/File;")
+Q_DECLARE_JNI_CLASS(File, "java/io/File")
+Q_DECLARE_JNI_CLASS(FileProvider, "androidx/core/content/FileProvider");
+
bool QAndroidPlatformServices::openUrl(const QUrl &theUrl)
{
QString mime;
@@ -55,13 +60,36 @@ bool QAndroidPlatformServices::openUrl(const QUrl &theUrl)
if (url.scheme() == fileScheme)
mime = QMimeDatabase().mimeTypeForUrl(url).name();
+ const QJniObject mimeString = QJniObject::fromString(mime);
+
using namespace QNativeInterface;
- QJniObject urlString = QJniObject::fromString(url.toString());
- QJniObject mimeString = QJniObject::fromString(mime);
- return QJniObject::callStaticMethod<jboolean>(
- QtAndroid::applicationClass(), "openURL",
- "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)Z",
- QAndroidApplication::context(), urlString.object(), mimeString.object());
+
+ auto openUrl = [mimeString](const QJniObject &url) {
+ return QJniObject::callStaticMethod<jboolean>(QtAndroid::applicationClass(), "openURL",
+ QAndroidApplication::context(), url.object<jstring>(), mimeString.object<jstring>());
+ };
+
+ if (url.scheme() != fileScheme || QNativeInterface::QAndroidApplication::sdkVersion() < 24)
+ return openUrl(QJniObject::fromString(url.toString()));
+
+ // Use FileProvider for file scheme with sdk >= 24
+ const QJniObject context = QAndroidApplication::context();
+ const auto appId = context.callMethod<jstring>("getPackageName").toString();
+ const auto providerName = QJniObject::fromString(appId + ".qtprovider"_L1);
+
+ const auto urlPath = QJniObject::fromString(url.path());
+ const auto urlFile = QJniObject(QtJniTypes::className<QtJniTypes::File>(),
+ urlPath.object<jstring>());
+
+ const auto fileProviderUri = QJniObject::callStaticMethod<QtJniTypes::UriType>(
+ QtJniTypes::className<QtJniTypes::FileProvider>(), "getUriForFile",
+ QAndroidApplication::context(), providerName.object<jstring>(),
+ urlFile.object<QtJniTypes::FileType>());
+
+ if (fileProviderUri.isValid())
+ return openUrl(fileProviderUri.callMethod<jstring>("toString"));
+
+ return false;
}
bool QAndroidPlatformServices::openDocument(const QUrl &url)