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