Warning, /graphics/krita/3rdparty/ext_qt/0017-Android-fully-integrate-native-file-dialog.patch is written in an unsupported language. File is not indexed.
0001 From 594abe9dbae59c2319e81eca38cb3dd237edb550 Mon Sep 17 00:00:00 2001 0002 From: Assam Boudjelthia <assam.boudjelthia@qt.io> 0003 Date: Tue, 28 Jan 2020 16:04:06 +0200 0004 Subject: [PATCH 17/46] Android: fully integrate native file dialog 0005 0006 Allow Qt to use native file dialog to open (file, multiple files, 0007 directory) and save a file. 0008 0009 Due to changes in file permission in Android 10, proper permissions 0010 tokens are granted after selecting a file or directory. 0011 0012 [ChangeLog][Android] Use native file dialog by default for open and save 0013 operations. 0014 0015 Task-number: QTBUG-82120 0016 Fixes: QTBUG-75484 0017 Change-Id: I92c9d08e0f214a57c4b3880fbd948adbabe39694 0018 Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io> 0019 --- 0020 .../qandroidplatformfiledialoghelper.cpp | 176 +++++++++++++----- 0021 .../qandroidplatformfiledialoghelper.h | 36 ++-- 0022 2 files changed, 148 insertions(+), 64 deletions(-) 0023 0024 diff --git a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp 0025 index fb979ab6cc..7b5f2f16f8 100644 0026 --- a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp 0027 +++ b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp 0028 @@ -40,7 +40,6 @@ 0029 #include "qandroidplatformfiledialoghelper.h" 0030 0031 #include <androidjnimain.h> 0032 -#include <private/qjni_p.h> 0033 #include <jni.h> 0034 0035 QT_BEGIN_NAMESPACE 0036 @@ -50,9 +49,11 @@ namespace QtAndroidFileDialogHelper { 0037 #define RESULT_OK -1 0038 #define REQUEST_CODE 1305 // Arbitrary 0039 0040 +const char JniIntentClass[] = "android/content/Intent"; 0041 + 0042 QAndroidPlatformFileDialogHelper::QAndroidPlatformFileDialogHelper() 0043 - : QPlatformFileDialogHelper() 0044 - , m_selectedFile() 0045 + : QPlatformFileDialogHelper(), 0046 + m_activity(QtAndroid::activity()) 0047 { 0048 } 0049 0050 @@ -61,92 +62,165 @@ bool QAndroidPlatformFileDialogHelper::handleActivityResult(jint requestCode, ji 0051 if (requestCode != REQUEST_CODE) 0052 return false; 0053 0054 - if (resultCode == RESULT_OK) { 0055 - const QJNIObjectPrivate intent = QJNIObjectPrivate::fromLocalRef(data); 0056 - const QJNIObjectPrivate uri = intent.callObjectMethod("getData", "()Landroid/net/Uri;"); 0057 - const QString uriStr = uri.callObjectMethod("toString", "()Ljava/lang/String;").toString(); 0058 - m_selectedFile = QUrl(uriStr); 0059 - Q_EMIT fileSelected(m_selectedFile); 0060 - Q_EMIT accept(); 0061 - } else { 0062 + if (resultCode != RESULT_OK) { 0063 Q_EMIT reject(); 0064 + return true; 0065 } 0066 0067 - return true; 0068 -} 0069 - 0070 -bool QAndroidPlatformFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) 0071 -{ 0072 - Q_UNUSED(windowFlags) 0073 - Q_UNUSED(windowModality) 0074 - Q_UNUSED(parent) 0075 - 0076 - if (options()->fileMode() != QFileDialogOptions::FileMode::ExistingFile) 0077 - return false; 0078 + const QJNIObjectPrivate intent = QJNIObjectPrivate::fromLocalRef(data); 0079 0080 - QtAndroidPrivate::registerActivityResultListener(this); 0081 + const QJNIObjectPrivate uri = intent.callObjectMethod("getData", "()Landroid/net/Uri;"); 0082 + if (uri.isValid()) { 0083 + takePersistableUriPermission(uri); 0084 + m_selectedFile.append(QUrl(uri.toString())); 0085 + Q_EMIT fileSelected(m_selectedFile.first()); 0086 + Q_EMIT accept(); 0087 0088 - const QJNIObjectPrivate ACTION_OPEN_DOCUMENT = QJNIObjectPrivate::getStaticObjectField("android/content/Intent", "ACTION_OPEN_DOCUMENT", "Ljava/lang/String;"); 0089 - QJNIObjectPrivate intent("android/content/Intent", "(Ljava/lang/String;)V", ACTION_OPEN_DOCUMENT.object()); 0090 - const QJNIObjectPrivate CATEGORY_OPENABLE = QJNIObjectPrivate::getStaticObjectField("android/content/Intent", "CATEGORY_OPENABLE", "Ljava/lang/String;"); 0091 - intent.callObjectMethod("addCategory", "(Ljava/lang/String;)Landroid/content/Intent;", CATEGORY_OPENABLE.object()); 0092 - intent.callObjectMethod("setType", "(Ljava/lang/String;)Landroid/content/Intent;", QJNIObjectPrivate::fromString(QStringLiteral("*/*")).object()); 0093 + return true; 0094 + } 0095 0096 - const QJNIObjectPrivate activity(QtAndroid::activity()); 0097 - activity.callMethod<void>("startActivityForResult", "(Landroid/content/Intent;I)V", intent.object(), REQUEST_CODE); 0098 + const QJNIObjectPrivate uriClipData = 0099 + intent.callObjectMethod("getClipData", "()Landroid/content/ClipData;"); 0100 + if (uriClipData.isValid()) { 0101 + const int size = uriClipData.callMethod<jint>("getItemCount"); 0102 + for (int i = 0; i < size; ++i) { 0103 + QJNIObjectPrivate item = uriClipData.callObjectMethod( 0104 + "getItemAt", "(I)Landroid/content/ClipData$Item;", i); 0105 + 0106 + QJNIObjectPrivate itemUri = item.callObjectMethod("getUri", "()Landroid/net/Uri;"); 0107 + takePersistableUriPermission(itemUri); 0108 + m_selectedFile.append(itemUri.toString()); 0109 + Q_EMIT filesSelected(m_selectedFile); 0110 + Q_EMIT accept(); 0111 + } 0112 + } 0113 0114 return true; 0115 } 0116 0117 -void QAndroidPlatformFileDialogHelper::exec() 0118 +void QAndroidPlatformFileDialogHelper::takePersistableUriPermission(const QJNIObjectPrivate &uri) 0119 { 0120 - m_eventLoop.exec(QEventLoop::DialogExec); 0121 + int modeFlags = QJNIObjectPrivate::getStaticField<jint>( 0122 + JniIntentClass, "FLAG_GRANT_READ_URI_PERMISSION"); 0123 + 0124 + if (options()->acceptMode() == QFileDialogOptions::AcceptSave) { 0125 + modeFlags |= QJNIObjectPrivate::getStaticField<jint>( 0126 + JniIntentClass, "FLAG_GRANT_WRITE_URI_PERMISSION"); 0127 + } 0128 + 0129 + QJNIObjectPrivate contentResolver = m_activity.callObjectMethod( 0130 + "getContentResolver", "()Landroid/content/ContentResolver;"); 0131 + contentResolver.callMethod<void>("takePersistableUriPermission", "(Landroid/net/Uri;I)V", 0132 + uri.object(), modeFlags); 0133 } 0134 0135 -void QAndroidPlatformFileDialogHelper::hide() 0136 +void QAndroidPlatformFileDialogHelper::setLocalFilesOnly(bool localOnly) 0137 { 0138 - if (m_eventLoop.isRunning()) 0139 - m_eventLoop.exit(); 0140 - QtAndroidPrivate::unregisterActivityResultListener(this); 0141 + const QJNIObjectPrivate extraLocalOnly = QJNIObjectPrivate::getStaticObjectField( 0142 + JniIntentClass, "EXTRA_LOCAL_ONLY", "Ljava/lang/String;"); 0143 + m_intent.callObjectMethod("putExtra", "(Ljava/lang/String;Z)Landroid/content/Intent;", 0144 + extraLocalOnly.object(), localOnly); 0145 } 0146 0147 -QString QAndroidPlatformFileDialogHelper::selectedNameFilter() const 0148 +void QAndroidPlatformFileDialogHelper::setIntentTitle(const QString &title) 0149 { 0150 - return QString(); 0151 + const QJNIObjectPrivate extraTitle = QJNIObjectPrivate::getStaticObjectField( 0152 + JniIntentClass, "EXTRA_TITLE", "Ljava/lang/String;"); 0153 + m_intent.callObjectMethod("putExtra", 0154 + "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;", 0155 + extraTitle.object(), QJNIObjectPrivate::fromString(title).object()); 0156 } 0157 0158 -void QAndroidPlatformFileDialogHelper::selectNameFilter(const QString &filter) 0159 +void QAndroidPlatformFileDialogHelper::setOpenableCategory() 0160 { 0161 - Q_UNUSED(filter) 0162 + const QJNIObjectPrivate CATEGORY_OPENABLE = QJNIObjectPrivate::getStaticObjectField( 0163 + JniIntentClass, "CATEGORY_OPENABLE", "Ljava/lang/String;"); 0164 + m_intent.callObjectMethod("addCategory", "(Ljava/lang/String;)Landroid/content/Intent;", 0165 + CATEGORY_OPENABLE.object()); 0166 } 0167 0168 -void QAndroidPlatformFileDialogHelper::setFilter() 0169 +void QAndroidPlatformFileDialogHelper::setAllowMultipleSelections(bool allowMultiple) 0170 { 0171 + const QJNIObjectPrivate allowMultipleSelections = QJNIObjectPrivate::getStaticObjectField( 0172 + JniIntentClass, "EXTRA_ALLOW_MULTIPLE", "Ljava/lang/String;"); 0173 + m_intent.callObjectMethod("putExtra", "(Ljava/lang/String;Z)Landroid/content/Intent;", 0174 + allowMultipleSelections.object(), allowMultiple); 0175 } 0176 0177 -QList<QUrl> QAndroidPlatformFileDialogHelper::selectedFiles() const 0178 +void QAndroidPlatformFileDialogHelper::setMimeTypes() 0179 { 0180 - return {m_selectedFile}; 0181 + m_intent.callObjectMethod("setType", "(Ljava/lang/String;)Landroid/content/Intent;", 0182 + QJNIObjectPrivate::fromString("*/*").object()); 0183 + 0184 + const QJNIObjectPrivate extraMimeType = QJNIObjectPrivate::getStaticObjectField( 0185 + JniIntentClass, "EXTRA_MIME_TYPES", "Ljava/lang/String;"); 0186 + for (const QString &type : options()->mimeTypeFilters()) { 0187 + m_intent.callObjectMethod( 0188 + "putExtra", "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;", 0189 + extraMimeType.object(), QJNIObjectPrivate::fromString(type).object()); 0190 + } 0191 } 0192 0193 -void QAndroidPlatformFileDialogHelper::selectFile(const QUrl &file) 0194 +QJNIObjectPrivate QAndroidPlatformFileDialogHelper::getFileDialogIntent(const QString &intentType) 0195 { 0196 - Q_UNUSED(file) 0197 + const QJNIObjectPrivate ACTION_OPEN_DOCUMENT = QJNIObjectPrivate::getStaticObjectField( 0198 + JniIntentClass, intentType.toLatin1(), "Ljava/lang/String;"); 0199 + return QJNIObjectPrivate(JniIntentClass, "(Ljava/lang/String;)V", 0200 + ACTION_OPEN_DOCUMENT.object()); 0201 } 0202 0203 -QUrl QAndroidPlatformFileDialogHelper::directory() const 0204 +bool QAndroidPlatformFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) 0205 { 0206 - return QUrl(); 0207 + Q_UNUSED(windowFlags) 0208 + Q_UNUSED(windowModality) 0209 + Q_UNUSED(parent) 0210 + 0211 + bool isDirDialog = false; 0212 + 0213 + if (options()->acceptMode() == QFileDialogOptions::AcceptSave) { 0214 + m_intent = getFileDialogIntent("ACTION_CREATE_DOCUMENT"); 0215 + } else if (options()->acceptMode() == QFileDialogOptions::AcceptOpen) { 0216 + switch (options()->fileMode()) { 0217 + case QFileDialogOptions::FileMode::DirectoryOnly: 0218 + case QFileDialogOptions::FileMode::Directory: 0219 + m_intent = getFileDialogIntent("ACTION_OPEN_DOCUMENT_TREE"); 0220 + isDirDialog = true; 0221 + break; 0222 + case QFileDialogOptions::FileMode::ExistingFiles: 0223 + m_intent = getFileDialogIntent("ACTION_OPEN_DOCUMENT"); 0224 + setAllowMultipleSelections(true); 0225 + break; 0226 + case QFileDialogOptions::FileMode::AnyFile: 0227 + case QFileDialogOptions::FileMode::ExistingFile: 0228 + m_intent = getFileDialogIntent("ACTION_OPEN_DOCUMENT"); 0229 + break; 0230 + } 0231 + } 0232 + 0233 + if (!isDirDialog) { 0234 + setOpenableCategory(); 0235 + setMimeTypes(); 0236 + } 0237 + 0238 + setIntentTitle(options()->windowTitle()); 0239 + setLocalFilesOnly(true); 0240 + 0241 + QtAndroidPrivate::registerActivityResultListener(this); 0242 + m_activity.callMethod<void>("startActivityForResult", "(Landroid/content/Intent;I)V", 0243 + m_intent.object(), REQUEST_CODE); 0244 + return true; 0245 } 0246 0247 -void QAndroidPlatformFileDialogHelper::setDirectory(const QUrl &directory) 0248 +void QAndroidPlatformFileDialogHelper::hide() 0249 { 0250 - Q_UNUSED(directory) 0251 + if (m_eventLoop.isRunning()) 0252 + m_eventLoop.exit(); 0253 + QtAndroidPrivate::unregisterActivityResultListener(this); 0254 } 0255 0256 -bool QAndroidPlatformFileDialogHelper::defaultNameFilterDisables() const 0257 +void QAndroidPlatformFileDialogHelper::exec() 0258 { 0259 - return false; 0260 + m_eventLoop.exec(QEventLoop::DialogExec); 0261 } 0262 } 0263 0264 diff --git a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h 0265 index 5cd26af7c9..fa9c3f47b3 100644 0266 --- a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h 0267 +++ b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h 0268 @@ -44,6 +44,8 @@ 0269 #include <QEventLoop> 0270 #include <qpa/qplatformdialoghelper.h> 0271 #include <QtCore/private/qjnihelpers_p.h> 0272 +#include <private/qjni_p.h> 0273 +#include <QEventLoop> 0274 0275 QT_BEGIN_NAMESPACE 0276 0277 @@ -55,26 +57,34 @@ class QAndroidPlatformFileDialogHelper: public QPlatformFileDialogHelper, public 0278 0279 public: 0280 QAndroidPlatformFileDialogHelper(); 0281 - void exec() override; 0282 0283 - bool show(Qt::WindowFlags windowFlags, 0284 - Qt::WindowModality windowModality, 0285 - QWindow *parent) override; 0286 + void exec() override; 0287 + bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) override; 0288 void hide() override; 0289 0290 - QString selectedNameFilter() const override; 0291 - void selectNameFilter(const QString &filter) override; 0292 - void setFilter() override; 0293 - QList<QUrl> selectedFiles() const override; 0294 - void selectFile(const QUrl &file) override; 0295 - QUrl directory() const override; 0296 - void setDirectory(const QUrl &directory) override; 0297 - bool defaultNameFilterDisables() const override; 0298 + QString selectedNameFilter() const override { return QString(); }; 0299 + void selectNameFilter(const QString &filter) override { Q_UNUSED(filter) }; 0300 + void setFilter() override {}; 0301 + QList<QUrl> selectedFiles() const override { return m_selectedFile; }; 0302 + void selectFile(const QUrl &file) override { Q_UNUSED(file) }; 0303 + QUrl directory() const override { return QUrl(); }; 0304 + void setDirectory(const QUrl &directory) override { Q_UNUSED(directory) }; 0305 + bool defaultNameFilterDisables() const override { return false; }; 0306 bool handleActivityResult(jint requestCode, jint resultCode, jobject data) override; 0307 0308 private: 0309 + QJNIObjectPrivate getFileDialogIntent(const QString &intentType); 0310 + void takePersistableUriPermission(const QJNIObjectPrivate &uri); 0311 + void setLocalFilesOnly(bool localOnly); 0312 + void setIntentTitle(const QString &title); 0313 + void setOpenableCategory(); 0314 + void setAllowMultipleSelections(bool allowMultiple); 0315 + void setMimeTypes(); 0316 + 0317 QEventLoop m_eventLoop; 0318 - QUrl m_selectedFile; 0319 + QList<QUrl> m_selectedFile; 0320 + QJNIObjectPrivate m_intent; 0321 + const QJNIObjectPrivate m_activity; 0322 }; 0323 0324 } 0325 -- 0326 2.33.0 0327