Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qandroidplatformservices.cpp
Go to the documentation of this file.
1// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
2// Copyright (C) 2021 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
7
8#if QT_CONFIG(desktopservices)
9#include <QDebug>
10#include <QDesktopServices>
11#include <QFile>
12#include <QMimeDatabase>
13#include <QtCore/QJniObject>
14#include <QtCore/qcoreapplication.h>
15#include <QtCore/qscopedvaluerollback.h>
16#endif
17
19
20using namespace QtJniTypes;
21using namespace Qt::StringLiterals;
22
23#if QT_CONFIG(desktopservices)
24static constexpr auto s_defaultScheme = "file"_L1;
25static constexpr auto s_defaultProvider = "qtprovider"_L1;
26#endif
27
29{
30#if QT_CONFIG(desktopservices)
31 m_actionView = QJniObject::getStaticObjectField("android/content/Intent", "ACTION_VIEW",
32 "Ljava/lang/String;")
33 .toString();
34
35 QtAndroidPrivate::registerNewIntentListener(this);
36
37 // Qt applications without Activity contexts cannot retrieve intents from the Activity.
38 if (QNativeInterface::QAndroidApplication::isActivityContext()) {
39 QMetaObject::invokeMethod(
40 this,
41 [this] {
42 QJniObject context = QJniObject(QtAndroidPrivate::context());
43 QJniObject intent =
44 context.callObjectMethod("getIntent", "()Landroid/content/Intent;");
45 handleNewIntent(nullptr, intent.object());
46 },
47 Qt::QueuedConnection);
48 }
49#endif // QT_CONFIG(desktopservices)
50}
51
53{
54 return QByteArray("Android");
55}
56
57Q_DECLARE_JNI_CLASS(FileProvider, "androidx/core/content/FileProvider");
58Q_DECLARE_JNI_CLASS(PackageManager, "android/content/pm/PackageManager");
59Q_DECLARE_JNI_CLASS(PackageInfo, "android/content/pm/PackageInfo");
60Q_DECLARE_JNI_CLASS(ProviderInfo, "android/content/pm/ProviderInfo");
61
62#if QT_CONFIG(desktopservices)
63bool QAndroidPlatformServices::openUrl(const QUrl &theUrl)
64{
65 QUrl url(theUrl);
66
67 // avoid recursing back into self
68 if (url == m_handlingUrl)
69 return false;
70
71 // a real URL including the scheme is needed, else the Intent can not be started
72 if (url.scheme().isEmpty())
73 url.setScheme(s_defaultScheme);
74
75 const int sdkVersion = QNativeInterface::QAndroidApplication::sdkVersion();
76 if (url.scheme() != s_defaultScheme || sdkVersion < 24 )
77 return openURL(url);
78 return openUrlWithFileProvider(url);
79}
80
81QString QAndroidPlatformServices::getMimeOfUrl(const QUrl &url) const
82{
83 QString mime;
84 if (url.scheme() == s_defaultScheme)
85 mime = QMimeDatabase().mimeTypeForUrl(url).name();
86 return mime;
87}
88
89bool QAndroidPlatformServices::openURL(const QUrl &url) const
90{
91 return openURL(url.toString());
92}
93
94bool QAndroidPlatformServices::openURL(const QString &url) const
95{
96 return QJniObject::callStaticMethod<jboolean>(
97 QtAndroid::applicationClass(), "openURL",
98 QNativeInterface::QAndroidApplication::context(),
99 url,
100 getMimeOfUrl(url));
101}
102
103bool QAndroidPlatformServices::openUrlWithFileProvider(const QUrl &url)
104{
105 const QJniObject context = QNativeInterface::QAndroidApplication::context();
106 auto authorities = getFileProviderAuthorities(context);
107 if (authorities.isEmpty())
108 return false;
109 return openUrlWithAuthority(url, getAdequateFileproviderAuthority(authorities));
110}
111
112
113QString QAndroidPlatformServices::getAdequateFileproviderAuthority(const QStringList &authorities) const
114{
115 if (authorities.size() == 1)
116 return authorities[0];
117
118 QString nonQtAuthority;
119 for (const auto &authority : authorities) {
120 if (!authority.endsWith(s_defaultProvider, Qt::CaseSensitive)) {
121 nonQtAuthority = authority;
122 break;
123 }
124 }
125 return nonQtAuthority;
126}
127
128bool QAndroidPlatformServices::openUrlWithAuthority(const QUrl &url, const QString &authority)
129{
130 const auto urlPath = QJniObject::fromString(url.path());
131 const auto urlFile = QJniObject(Traits<File>::className(),
132 urlPath.object<jstring>());
133 const auto fileProviderUri = QJniObject::callStaticMethod<Uri>(
134 Traits<FileProvider>::className(), "getUriForFile",
135 QNativeInterface::QAndroidApplication::context(), authority,
136 urlFile.object<File>());
137 if (fileProviderUri.isValid())
138 return openURL(fileProviderUri.toString());
139 return false;
140}
141
142QStringList QAndroidPlatformServices::getFileProviderAuthorities(const QJniObject &context) const
143{
144 QStringList authorityList;
145
146 const auto packageManager = context.callMethod<PackageManager>("getPackageManager");
147 const auto packageName = context.callMethod<QString>("getPackageName");
148 const auto packageInfo = packageManager.callMethod<PackageInfo>("getPackageInfo",
149 packageName,
150 8 /* PackageManager.GET_PROVIDERS */);
151 const auto providersArray = packageInfo.getField<ProviderInfo[]>("providers");
152
153 if (providersArray.isValid()) {
154 const auto className = Traits<FileProvider>::className();
155 for (const auto &fileProvider : providersArray) {
156 auto providerName = fileProvider.getField<QString>("name");
157 if (providerName.replace(".", "/").contains(className.data())) {
158 const auto authority = fileProvider.getField<QString>("authority");
159 if (!authority.isEmpty())
160 authorityList << authority;
161 }
162 }
163 }
164 if (authorityList.isEmpty())
165 qWarning() << "No file provider found in the AndroidManifest.xml.";
166
167 return authorityList;
168}
169
170bool QAndroidPlatformServices::openDocument(const QUrl &url)
171{
172 return openUrl(url);
173}
174
175bool QAndroidPlatformServices::handleNewIntent(JNIEnv *env, jobject intent)
176{
177 Q_UNUSED(env);
178
179 const QJniObject jniIntent(intent);
180
181 const QString action = jniIntent.callObjectMethod<jstring>("getAction").toString();
182 if (action != m_actionView)
183 return false;
184
185 const QString url = jniIntent.callObjectMethod<jstring>("getDataString").toString();
186 QScopedValueRollback<QUrl> rollback(m_handlingUrl, url);
187 return QDesktopServices::openUrl(url);
188}
189#endif // QT_CONFIG(desktopservices)
190
191QT_END_NAMESPACE
QByteArray desktopEnvironment() const override
QPlatformServices::desktopEnvironment returns the active desktop environment.
Combined button and popup list for selecting options.
Q_DECLARE_JNI_CLASS(MotionEvent, "android/view/MotionEvent")