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