Qt 6.x
The Qt SDK
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
28 m_activity(QtAndroid::activity())
29{
30}
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.first());
49 Q_EMIT currentChanged(m_selectedFile.first());
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.first());
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 .]+)");
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 const QString nameFilter = nameFilters.isEmpty() ? QString() : nameFilters.first();
150
151 if (!nameFilter.isEmpty()) {
153 for (const QString &filter : nameFilterExtensions(nameFilter))
154 mimeTypes.append(db.mimeTypeForFile(filter, QMimeDatabase::MatchExtension).name());
155 }
156
157 const QString initialType = mimeTypes.size() == 1 ? mimeTypes.at(0) : "*/*"_L1;
158 m_intent.callObjectMethod("setType", "(Ljava/lang/String;)Landroid/content/Intent;",
159 QJniObject::fromString(initialType).object());
160
161 if (!mimeTypes.isEmpty()) {
162 const QJniObject extraMimeType = QJniObject::getStaticObjectField(
163 JniIntentClass, "EXTRA_MIME_TYPES", "Ljava/lang/String;");
164
165 const QJniObject mimeTypesArray = QJniObject::callStaticObjectMethod(
166 "org/qtproject/qt/android/QtNative",
167 "getStringArray",
168 "(Ljava/lang/String;)[Ljava/lang/String;",
169 QJniObject::fromString(mimeTypes.join(",")).object());
170
171 m_intent.callObjectMethod(
172 "putExtra", "(Ljava/lang/String;[Ljava/lang/String;)Landroid/content/Intent;",
173 extraMimeType.object(), mimeTypesArray.object());
174 }
175}
176
177QJniObject QAndroidPlatformFileDialogHelper::getFileDialogIntent(const QString &intentType)
178{
179 const QJniObject ACTION_OPEN_DOCUMENT = QJniObject::getStaticObjectField(
180 JniIntentClass, intentType.toLatin1(), "Ljava/lang/String;");
181 return QJniObject(JniIntentClass, "(Ljava/lang/String;)V",
182 ACTION_OPEN_DOCUMENT.object());
183}
184
185bool QAndroidPlatformFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent)
186{
187 Q_UNUSED(windowFlags);
188 Q_UNUSED(windowModality);
190
191 bool isDirDialog = false;
192
193 m_selectedFile.clear();
194
195 if (options()->acceptMode() == QFileDialogOptions::AcceptSave) {
196 m_intent = getFileDialogIntent("ACTION_CREATE_DOCUMENT");
198 if (selectedFiles.size() > 0)
199 setInitialFileName(selectedFiles.first().fileName());
200 } else if (options()->acceptMode() == QFileDialogOptions::AcceptOpen) {
201 switch (options()->fileMode()) {
204 m_intent = getFileDialogIntent("ACTION_OPEN_DOCUMENT_TREE");
205 isDirDialog = true;
206 break;
208 m_intent = getFileDialogIntent("ACTION_OPEN_DOCUMENT");
209 setAllowMultipleSelections(true);
210 break;
213 m_intent = getFileDialogIntent("ACTION_OPEN_DOCUMENT");
214 break;
215 }
216 }
217
218 if (!isDirDialog) {
219 setOpenableCategory();
220 setMimeTypes();
221 }
222
223 setInitialDirectoryUri(m_directory.toString());
224
226 m_activity.callMethod<void>("startActivityForResult", "(Landroid/content/Intent;I)V",
227 m_intent.object(), REQUEST_CODE);
228 return true;
229}
230
232{
233 if (m_eventLoop.isRunning())
234 m_eventLoop.exit();
236}
237
239{
240 m_directory = directory;
241}
242
244{
245 m_eventLoop.exec(QEventLoop::DialogExec);
246}
247}
248
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
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
T & first()
Definition qlist.h:628
void append(parameter_type t)
Definition qlist.h:441
void clear()
Definition qlist.h:417
\inmodule QtCore
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:311
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
\inmodule QtCore \reentrant
QRegularExpressionMatchIterator globalMatch(const QString &subject, qsizetype offset=0, MatchType matchType=NormalMatch, MatchOptions matchOptions=NoMatchOption) const
Attempts to perform a global match of the regular expression against the given subject string,...
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QByteArray toLatin1() const &
Definition qstring.h:559
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
\inmodule QtCore
Definition qurl.h:94
QString fileName(ComponentFormattingOptions options=FullyDecoded) const
Definition qurl.cpp:2494
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
Definition qurl.cpp:1888
QString toString(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2828
\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)
Q_CORE_EXPORT void unregisterActivityResultListener(ActivityResultListener *listener)
Q_CORE_EXPORT void registerActivityResultListener(ActivityResultListener *listener)
WindowModality
return ret
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint name
GLint first
#define Q_EMIT
#define Q_UNUSED(x)
QString title
[35]
QMimeDatabase db
[0]
QGraphicsItem * item
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent