summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp')
-rw-r--r--src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp273
1 files changed, 186 insertions, 87 deletions
diff --git a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp
index fb979ab6cc..d8a5c58d2c 100644
--- a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp
+++ b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp
@@ -1,58 +1,31 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB)
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB)
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidplatformfiledialoghelper.h"
#include <androidjnimain.h>
-#include <private/qjni_p.h>
-#include <jni.h>
+#include <QtCore/QJniObject>
+
+#include <QMimeDatabase>
+#include <QMimeType>
+#include <QRegularExpression>
+#include <QUrl>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
namespace QtAndroidFileDialogHelper {
#define RESULT_OK -1
#define REQUEST_CODE 1305 // Arbitrary
+const char JniIntentClass[] = "android/content/Intent";
+
QAndroidPlatformFileDialogHelper::QAndroidPlatformFileDialogHelper()
- : QPlatformFileDialogHelper()
- , m_selectedFile()
+ : QPlatformFileDialogHelper(),
+ m_activity(QtAndroidPrivate::activity())
{
}
@@ -61,92 +34,218 @@ bool QAndroidPlatformFileDialogHelper::handleActivityResult(jint requestCode, ji
if (requestCode != REQUEST_CODE)
return false;
- if (resultCode == RESULT_OK) {
- const QJNIObjectPrivate intent = QJNIObjectPrivate::fromLocalRef(data);
- const QJNIObjectPrivate uri = intent.callObjectMethod("getData", "()Landroid/net/Uri;");
- const QString uriStr = uri.callObjectMethod("toString", "()Ljava/lang/String;").toString();
- m_selectedFile = QUrl(uriStr);
- Q_EMIT fileSelected(m_selectedFile);
- Q_EMIT accept();
- } else {
+ if (resultCode != RESULT_OK) {
Q_EMIT reject();
+ return true;
+ }
+
+ const QJniObject intent = QJniObject::fromLocalRef(data);
+
+ const QJniObject uri = intent.callObjectMethod("getData", "()Landroid/net/Uri;");
+ if (uri.isValid()) {
+ takePersistableUriPermission(uri);
+ m_selectedFile.append(QUrl(uri.toString()));
+ Q_EMIT fileSelected(m_selectedFile.constFirst());
+ Q_EMIT currentChanged(m_selectedFile.constFirst());
+ Q_EMIT accept();
+
+ return true;
+ }
+
+ const QJniObject uriClipData =
+ intent.callObjectMethod("getClipData", "()Landroid/content/ClipData;");
+ if (uriClipData.isValid()) {
+ const int size = uriClipData.callMethod<jint>("getItemCount");
+ for (int i = 0; i < size; ++i) {
+ QJniObject item = uriClipData.callObjectMethod(
+ "getItemAt", "(I)Landroid/content/ClipData$Item;", i);
+
+ QJniObject itemUri = item.callObjectMethod("getUri", "()Landroid/net/Uri;");
+ takePersistableUriPermission(itemUri);
+ m_selectedFile.append(itemUri.toString());
+ }
+ Q_EMIT filesSelected(m_selectedFile);
+ Q_EMIT currentChanged(m_selectedFile.constFirst());
+ Q_EMIT accept();
}
return true;
}
-bool QAndroidPlatformFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent)
+void QAndroidPlatformFileDialogHelper::takePersistableUriPermission(const QJniObject &uri)
{
- Q_UNUSED(windowFlags)
- Q_UNUSED(windowModality)
- Q_UNUSED(parent)
-
- if (options()->fileMode() != QFileDialogOptions::FileMode::ExistingFile)
- return false;
+ int modeFlags = QJniObject::getStaticField<jint>(
+ JniIntentClass, "FLAG_GRANT_READ_URI_PERMISSION");
- QtAndroidPrivate::registerActivityResultListener(this);
-
- const QJNIObjectPrivate ACTION_OPEN_DOCUMENT = QJNIObjectPrivate::getStaticObjectField("android/content/Intent", "ACTION_OPEN_DOCUMENT", "Ljava/lang/String;");
- QJNIObjectPrivate intent("android/content/Intent", "(Ljava/lang/String;)V", ACTION_OPEN_DOCUMENT.object());
- const QJNIObjectPrivate CATEGORY_OPENABLE = QJNIObjectPrivate::getStaticObjectField("android/content/Intent", "CATEGORY_OPENABLE", "Ljava/lang/String;");
- intent.callObjectMethod("addCategory", "(Ljava/lang/String;)Landroid/content/Intent;", CATEGORY_OPENABLE.object());
- intent.callObjectMethod("setType", "(Ljava/lang/String;)Landroid/content/Intent;", QJNIObjectPrivate::fromString(QStringLiteral("*/*")).object());
+ if (options()->acceptMode() == QFileDialogOptions::AcceptSave) {
+ modeFlags |= QJniObject::getStaticField<jint>(
+ JniIntentClass, "FLAG_GRANT_WRITE_URI_PERMISSION");
+ }
- const QJNIObjectPrivate activity(QtAndroid::activity());
- activity.callMethod<void>("startActivityForResult", "(Landroid/content/Intent;I)V", intent.object(), REQUEST_CODE);
+ QJniObject contentResolver = m_activity.callObjectMethod(
+ "getContentResolver", "()Landroid/content/ContentResolver;");
+ contentResolver.callMethod<void>("takePersistableUriPermission", "(Landroid/net/Uri;I)V",
+ uri.object(), modeFlags);
+}
- return true;
+void QAndroidPlatformFileDialogHelper::setInitialFileName(const QString &title)
+{
+ const QJniObject extraTitle = QJniObject::getStaticObjectField(
+ JniIntentClass, "EXTRA_TITLE", "Ljava/lang/String;");
+ m_intent.callObjectMethod("putExtra",
+ "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;",
+ extraTitle.object(), QJniObject::fromString(title).object());
}
-void QAndroidPlatformFileDialogHelper::exec()
+void QAndroidPlatformFileDialogHelper::setInitialDirectoryUri(const QString &directory)
{
- m_eventLoop.exec(QEventLoop::DialogExec);
+ if (directory.isEmpty())
+ return;
+
+ if (QNativeInterface::QAndroidApplication::sdkVersion() < 26)
+ return;
+
+ const auto extraInitialUri = QJniObject::getStaticObjectField(
+ "android/provider/DocumentsContract", "EXTRA_INITIAL_URI", "Ljava/lang/String;");
+ m_intent.callObjectMethod("putExtra",
+ "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;",
+ extraInitialUri.object(),
+ QJniObject::fromString(directory).object());
}
-void QAndroidPlatformFileDialogHelper::hide()
+void QAndroidPlatformFileDialogHelper::setOpenableCategory()
{
- if (m_eventLoop.isRunning())
- m_eventLoop.exit();
- QtAndroidPrivate::unregisterActivityResultListener(this);
+ const QJniObject CATEGORY_OPENABLE = QJniObject::getStaticObjectField(
+ JniIntentClass, "CATEGORY_OPENABLE", "Ljava/lang/String;");
+ m_intent.callObjectMethod("addCategory", "(Ljava/lang/String;)Landroid/content/Intent;",
+ CATEGORY_OPENABLE.object());
}
-QString QAndroidPlatformFileDialogHelper::selectedNameFilter() const
+void QAndroidPlatformFileDialogHelper::setAllowMultipleSelections(bool allowMultiple)
{
- return QString();
+ const QJniObject allowMultipleSelections = QJniObject::getStaticObjectField(
+ JniIntentClass, "EXTRA_ALLOW_MULTIPLE", "Ljava/lang/String;");
+ m_intent.callObjectMethod("putExtra", "(Ljava/lang/String;Z)Landroid/content/Intent;",
+ allowMultipleSelections.object(), allowMultiple);
}
-void QAndroidPlatformFileDialogHelper::selectNameFilter(const QString &filter)
+QStringList nameFilterExtensions(const QString nameFilters)
{
- Q_UNUSED(filter)
+ QStringList ret;
+#if QT_CONFIG(regularexpression)
+ QRegularExpression re("(\\*\\.[a-z .]+)");
+ QRegularExpressionMatchIterator i = re.globalMatch(nameFilters);
+ while (i.hasNext())
+ ret << i.next().captured(1).trimmed();
+#endif // QT_CONFIG(regularexpression)
+ ret.removeAll("*");
+ return ret;
}
-void QAndroidPlatformFileDialogHelper::setFilter()
+void QAndroidPlatformFileDialogHelper::setMimeTypes()
{
+ QStringList mimeTypes = options()->mimeTypeFilters();
+ const QStringList nameFilters = options()->nameFilters();
+
+ if (!nameFilters.isEmpty()) {
+ QMimeDatabase db;
+ for (auto filter : nameFilters) {
+ if (!filter.isEmpty()) {
+ for (const QString &filter : nameFilterExtensions(filter))
+ mimeTypes.append(db.mimeTypeForFile(filter, QMimeDatabase::MatchExtension).name());
+ }
+ }
+ }
+
+ const QString initialType = mimeTypes.size() == 1 ? mimeTypes.at(0) : "*/*"_L1;
+ m_intent.callObjectMethod("setType", "(Ljava/lang/String;)Landroid/content/Intent;",
+ QJniObject::fromString(initialType).object());
+
+ if (!mimeTypes.isEmpty()) {
+ const QJniObject extraMimeType = QJniObject::getStaticObjectField(
+ JniIntentClass, "EXTRA_MIME_TYPES", "Ljava/lang/String;");
+
+ const QJniObject mimeTypesArray = QJniObject::callStaticObjectMethod(
+ "org/qtproject/qt/android/QtNative",
+ "getStringArray",
+ "(Ljava/lang/String;)[Ljava/lang/String;",
+ QJniObject::fromString(mimeTypes.join(",")).object());
+
+ m_intent.callObjectMethod(
+ "putExtra", "(Ljava/lang/String;[Ljava/lang/String;)Landroid/content/Intent;",
+ extraMimeType.object(), mimeTypesArray.object());
+ }
}
-QList<QUrl> QAndroidPlatformFileDialogHelper::selectedFiles() const
+QJniObject QAndroidPlatformFileDialogHelper::getFileDialogIntent(const QString &intentType)
{
- return {m_selectedFile};
+ const QJniObject ACTION_OPEN_DOCUMENT = QJniObject::getStaticObjectField(
+ JniIntentClass, intentType.toLatin1(), "Ljava/lang/String;");
+ return QJniObject(JniIntentClass, "(Ljava/lang/String;)V",
+ ACTION_OPEN_DOCUMENT.object());
}
-void QAndroidPlatformFileDialogHelper::selectFile(const QUrl &file)
+bool QAndroidPlatformFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent)
{
- Q_UNUSED(file)
+ Q_UNUSED(windowFlags);
+ Q_UNUSED(windowModality);
+ Q_UNUSED(parent);
+
+ bool isDirDialog = false;
+
+ m_selectedFile.clear();
+
+ if (options()->acceptMode() == QFileDialogOptions::AcceptSave) {
+ m_intent = getFileDialogIntent("ACTION_CREATE_DOCUMENT");
+ const QList<QUrl> selectedFiles = options()->initiallySelectedFiles();
+ if (selectedFiles.size() > 0)
+ setInitialFileName(selectedFiles.first().fileName());
+ } else if (options()->acceptMode() == QFileDialogOptions::AcceptOpen) {
+ switch (options()->fileMode()) {
+ case QFileDialogOptions::FileMode::DirectoryOnly:
+ case QFileDialogOptions::FileMode::Directory:
+ m_intent = getFileDialogIntent("ACTION_OPEN_DOCUMENT_TREE");
+ isDirDialog = true;
+ break;
+ case QFileDialogOptions::FileMode::ExistingFiles:
+ m_intent = getFileDialogIntent("ACTION_OPEN_DOCUMENT");
+ setAllowMultipleSelections(true);
+ break;
+ case QFileDialogOptions::FileMode::AnyFile:
+ case QFileDialogOptions::FileMode::ExistingFile:
+ m_intent = getFileDialogIntent("ACTION_OPEN_DOCUMENT");
+ break;
+ }
+ }
+
+ if (!isDirDialog) {
+ setOpenableCategory();
+ setMimeTypes();
+ }
+
+ setInitialDirectoryUri(m_directory.toString());
+
+ QtAndroidPrivate::registerActivityResultListener(this);
+ m_activity.callMethod<void>("startActivityForResult", "(Landroid/content/Intent;I)V",
+ m_intent.object(), REQUEST_CODE);
+ return true;
}
-QUrl QAndroidPlatformFileDialogHelper::directory() const
+void QAndroidPlatformFileDialogHelper::hide()
{
- return QUrl();
+ if (m_eventLoop.isRunning())
+ m_eventLoop.exit();
+ QtAndroidPrivate::unregisterActivityResultListener(this);
}
void QAndroidPlatformFileDialogHelper::setDirectory(const QUrl &directory)
{
- Q_UNUSED(directory)
+ m_directory = directory;
}
-bool QAndroidPlatformFileDialogHelper::defaultNameFilterDisables() const
+void QAndroidPlatformFileDialogHelper::exec()
{
- return false;
+ m_eventLoop.exec(QEventLoop::DialogExec);
}
}