Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qandroidplatformfiledialoghelper.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB)
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#include <androidjnimain.h>
8#include <QtCore/QJniObject>
9
10#include <QMimeDatabase>
11#include <QMimeType>
12#include <QRegularExpression>
13#include <QUrl>
14
16
17using namespace Qt::StringLiterals;
18
20
21#define RESULT_OK -1
22#define REQUEST_CODE 1305 // Arbitrary
23
24const char JniIntentClass[] = "android/content/Intent";
25
31
32bool QAndroidPlatformFileDialogHelper::handleActivityResult(jint requestCode, jint resultCode, jobject data)
33{
34 if (requestCode != REQUEST_CODE)
35 return false;
36
37 if (resultCode != RESULT_OK) {
38 Q_EMIT reject();
39 return true;
40 }
41
42 const QJniObject intent = QJniObject::fromLocalRef(data);
43
44 const QJniObject uri = intent.callObjectMethod("getData", "()Landroid/net/Uri;");
45 if (uri.isValid()) {
46 takePersistableUriPermission(uri);
47 m_selectedFile.append(QUrl(uri.toString()));
48 Q_EMIT fileSelected(m_selectedFile.constFirst());
49 Q_EMIT currentChanged(m_selectedFile.constFirst());
50 Q_EMIT accept();
51
52 return true;
53 }
54
55 const QJniObject uriClipData =
56 intent.callObjectMethod("getClipData", "()Landroid/content/ClipData;");
57 if (uriClipData.isValid()) {
58 const int size = uriClipData.callMethod<jint>("getItemCount");
59 for (int i = 0; i < size; ++i) {
60 QJniObject item = uriClipData.callObjectMethod(
61 "getItemAt", "(I)Landroid/content/ClipData$Item;", i);
62
63 QJniObject itemUri = item.callObjectMethod("getUri", "()Landroid/net/Uri;");
64 takePersistableUriPermission(itemUri);
65 m_selectedFile.append(itemUri.toString());
66 }
67 Q_EMIT filesSelected(m_selectedFile);
68 Q_EMIT currentChanged(m_selectedFile.constFirst());
69 Q_EMIT accept();
70 }
71
72 return true;
73}
74
75void QAndroidPlatformFileDialogHelper::takePersistableUriPermission(const QJniObject &uri)
76{
77 int modeFlags = QJniObject::getStaticField<jint>(
78 JniIntentClass, "FLAG_GRANT_READ_URI_PERMISSION");
79
80 if (options()->acceptMode() == QFileDialogOptions::AcceptSave) {
81 modeFlags |= QJniObject::getStaticField<jint>(
82 JniIntentClass, "FLAG_GRANT_WRITE_URI_PERMISSION");
83 }
84
85 QJniObject contentResolver = m_activity.callObjectMethod(
86 "getContentResolver", "()Landroid/content/ContentResolver;");
87 contentResolver.callMethod<void>("takePersistableUriPermission", "(Landroid/net/Uri;I)V",
88 uri.object(), modeFlags);
89}
90
91void QAndroidPlatformFileDialogHelper::setInitialFileName(const QString &title)
92{
93 const QJniObject extraTitle = QJniObject::getStaticObjectField(
94 JniIntentClass, "EXTRA_TITLE", "Ljava/lang/String;");
95 m_intent.callObjectMethod("putExtra",
96 "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;",
97 extraTitle.object(), QJniObject::fromString(title).object());
98}
99
100void QAndroidPlatformFileDialogHelper::setInitialDirectoryUri(const QString &directory)
101{
102 if (directory.isEmpty())
103 return;
104
105 if (QNativeInterface::QAndroidApplication::sdkVersion() < 26)
106 return;
107
108 const auto extraInitialUri = QJniObject::getStaticObjectField(
109 "android/provider/DocumentsContract", "EXTRA_INITIAL_URI", "Ljava/lang/String;");
110 m_intent.callObjectMethod("putExtra",
111 "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;",
112 extraInitialUri.object(),
113 QJniObject::fromString(directory).object());
114}
115
116void QAndroidPlatformFileDialogHelper::setOpenableCategory()
117{
118 const QJniObject CATEGORY_OPENABLE = QJniObject::getStaticObjectField(
119 JniIntentClass, "CATEGORY_OPENABLE", "Ljava/lang/String;");
120 m_intent.callObjectMethod("addCategory", "(Ljava/lang/String;)Landroid/content/Intent;",
121 CATEGORY_OPENABLE.object());
122}
123
124void QAndroidPlatformFileDialogHelper::setAllowMultipleSelections(bool allowMultiple)
125{
126 const QJniObject allowMultipleSelections = QJniObject::getStaticObjectField(
127 JniIntentClass, "EXTRA_ALLOW_MULTIPLE", "Ljava/lang/String;");
128 m_intent.callObjectMethod("putExtra", "(Ljava/lang/String;Z)Landroid/content/Intent;",
129 allowMultipleSelections.object(), allowMultiple);
130}
131
133{
135#if QT_CONFIG(regularexpression)
136 QRegularExpression re("(\\*\\.[a-z .]+)");
137 QRegularExpressionMatchIterator i = re.globalMatch(nameFilters);
138 while (i.hasNext())
139 ret << i.next().captured(1).trimmed();
140#endif // QT_CONFIG(regularexpression)
141 ret.removeAll("*");
142 return ret;
143}
144
145void QAndroidPlatformFileDialogHelper::setMimeTypes()
146{
147 QStringList mimeTypes = options()->mimeTypeFilters();
148 const QStringList nameFilters = options()->nameFilters();
149
150 if (!nameFilters.isEmpty()) {
152 for (auto filter : nameFilters) {
153 if (!filter.isEmpty()) {
155 mimeTypes.append(db.mimeTypeForFile(filter, QMimeDatabase::MatchExtension).name());
156 }
157 }
158 }
159
160 const QString initialType = mimeTypes.size() == 1 ? mimeTypes.at(0) : "*/*"_L1;
161 m_intent.callObjectMethod("setType", "(Ljava/lang/String;)Landroid/content/Intent;",
162 QJniObject::fromString(initialType).object());
163
164 if (!mimeTypes.isEmpty()) {
165 const QJniObject extraMimeType = QJniObject::getStaticObjectField(
166 JniIntentClass, "EXTRA_MIME_TYPES", "Ljava/lang/String;");
167
168 const QJniObject mimeTypesArray = QJniObject::callStaticObjectMethod(
169 "org/qtproject/qt/android/QtNative",
170 "getStringArray",
171 "(Ljava/lang/String;)[Ljava/lang/String;",
172 QJniObject::fromString(mimeTypes.join(",")).object());
173
174 m_intent.callObjectMethod(
175 "putExtra", "(Ljava/lang/String;[Ljava/lang/String;)Landroid/content/Intent;",
176 extraMimeType.object(), mimeTypesArray.object());
177 }
178}
179
180QJniObject QAndroidPlatformFileDialogHelper::getFileDialogIntent(const QString &intentType)
181{
182 const QJniObject ACTION_OPEN_DOCUMENT = QJniObject::getStaticObjectField(
183 JniIntentClass, intentType.toLatin1(), "Ljava/lang/String;");
184 return QJniObject(JniIntentClass, "(Ljava/lang/String;)V",
185 ACTION_OPEN_DOCUMENT.object());
186}
187
188bool QAndroidPlatformFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent)
189{
190 Q_UNUSED(windowFlags);
191 Q_UNUSED(windowModality);
193
194 bool isDirDialog = false;
195
196 m_selectedFile.clear();
197
198 if (options()->acceptMode() == QFileDialogOptions::AcceptSave) {
199 m_intent = getFileDialogIntent("ACTION_CREATE_DOCUMENT");
200 const QList<QUrl> selectedFiles = options()->initiallySelectedFiles();
201 if (selectedFiles.size() > 0)
202 setInitialFileName(selectedFiles.first().fileName());
203 } else if (options()->acceptMode() == QFileDialogOptions::AcceptOpen) {
204 switch (options()->fileMode()) {
207 m_intent = getFileDialogIntent("ACTION_OPEN_DOCUMENT_TREE");
208 isDirDialog = true;
209 break;
211 m_intent = getFileDialogIntent("ACTION_OPEN_DOCUMENT");
212 setAllowMultipleSelections(true);
213 break;
216 m_intent = getFileDialogIntent("ACTION_OPEN_DOCUMENT");
217 break;
218 }
219 }
220
221 if (!isDirDialog) {
222 setOpenableCategory();
223 setMimeTypes();
224 }
225
226 setInitialDirectoryUri(m_directory.toString());
227
229 m_activity.callMethod<void>("startActivityForResult", "(Landroid/content/Intent;I)V",
230 m_intent.object(), REQUEST_CODE);
231 return true;
232}
233
235{
236 if (m_eventLoop.isRunning())
237 m_eventLoop.exit();
239}
240
245
250}
251
int exec(ProcessEventsFlags flags=AllEvents)
Enters the main event loop and waits until exit() is called.
void exit(int returnCode=0)
Tells the event loop to exit with a return code.
bool isRunning() const
Returns true if the event loop is running; otherwise returns false.
QStringList mimeTypeFilters() const
QList< QUrl > initiallySelectedFiles() const
QStringList nameFilters() const
\inmodule QtCore
qsizetype size() const noexcept
Definition qlist.h:397
T & first()
Definition qlist.h:645
const T & constFirst() const noexcept
Definition qlist.h:647
void append(parameter_type t)
Definition qlist.h:458
void clear()
Definition qlist.h:434
\inmodule QtCore
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:346
The QPlatformFileDialogHelper class allows for platform-specific customization of file dialogs.
void fileSelected(const QUrl &file)
void currentChanged(const QUrl &path)
const QSharedPointer< QFileDialogOptions > & options() const
void filesSelected(const QList< QUrl > &files)
\inmodule QtCore \reentrant
QRegularExpressionMatch next()
Returns the next match result and advances the iterator by one position.
QString captured(int nth=0) const
Returns the substring captured by the nth capturing group.
\inmodule QtCore \reentrant
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString trimmed() const &
Definition qstring.h:447
\inmodule QtCore
Definition qurl.h:94
QString fileName(ComponentFormattingOptions options=FullyDecoded) const
Definition qurl.cpp:2497
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
Definition qurl.cpp:1896
QString toString(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2831
\inmodule QtGui
Definition qwindow.h:63
bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) override
bool handleActivityResult(jint requestCode, jint resultCode, jobject data) override
list append(new Employee("Blackpool", "Stephen"))
Combined button and popup list for selecting options.
QStringList nameFilterExtensions(const QString nameFilters)
\preliminary \inmodule QtCorePrivate
Q_CORE_EXPORT void unregisterActivityResultListener(ActivityResultListener *listener)
Q_CORE_EXPORT void registerActivityResultListener(ActivityResultListener *listener)
WindowModality
return ret
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLuint name
#define Q_EMIT
#define Q_UNUSED(x)
QString title
[35]
QMimeDatabase db
[0]
QGraphicsItem * item