From c682ade6f8598b995cc20c78d45fed903e6375b8 Mon Sep 17 00:00:00 2001
From: Volker Krause <vkrause@kde.org>
Date: Sat, 26 Jan 2019 18:30:56 +0100
Subject: [PATCH 1/5] Add file engine for Android content URLs

The "file dialog" on Android returns such URLs, which so far required
special-casing this in application code. With this change QFile can
consume those URLs directly.

Change-Id: I489c0db112cf1dc7497e7a90f0e9a79ea8fa5237
Reviewed-by: Nicolas Fella <nicolas.fella@kdab.com>
Reviewed-by: BogDan Vatra <bogdan@kdab.com>
Reviewed-by: Aleix Pol Gonzalez <aleixpol@kde.org>
---
 .../org/qtproject/qt5/android/QtNative.java   | 13 +++
 src/plugins/platforms/android/android.pro     |  2 +
 .../android/androidcontentfileengine.cpp      | 92 +++++++++++++++++++
 .../android/androidcontentfileengine.h        | 60 ++++++++++++
 .../platforms/android/androidjnimain.cpp      |  5 +
 5 files changed, 172 insertions(+)
 create mode 100644 src/plugins/platforms/android/androidcontentfileengine.cpp
 create mode 100644 src/plugins/platforms/android/androidcontentfileengine.h

diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java
index 0d8bf7b8a0..34a8c7d788 100644
--- a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java
+++ b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java
@@ -41,6 +41,7 @@
 package org.qtproject.qt5.android;
 
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.util.ArrayList;
 import java.util.concurrent.Semaphore;
 
@@ -59,6 +60,7 @@ import android.os.Looper;
 import android.content.ClipboardManager;
 import android.content.ClipboardManager.OnPrimaryClipChangedListener;
 import android.content.ClipData;
+import android.os.ParcelFileDescriptor;
 import android.util.Log;
 import android.view.ContextMenu;
 import android.view.KeyEvent;
@@ -168,6 +170,17 @@ public class QtNative
         return ok;
     }
 
+    public static int openFdForContentUrl(Context context, String contentUrl, String openMode)
+    {
+        try {
+            ContentResolver resolver = context.getContentResolver();
+            ParcelFileDescriptor fdDesc = resolver.openFileDescriptor(Uri.parse(contentUrl), openMode);
+            return fdDesc.detachFd();
+        } catch (FileNotFoundException e) {
+            return -1;
+        }
+    }
+
     // this method loads full path libs
     public static void loadQtLibraries(final ArrayList<String> libraries)
     {
diff --git a/src/plugins/platforms/android/android.pro b/src/plugins/platforms/android/android.pro
index 73db9e93a3..940bd5fc55 100644
--- a/src/plugins/platforms/android/android.pro
+++ b/src/plugins/platforms/android/android.pro
@@ -20,6 +20,7 @@ INCLUDEPATH += \
     $$QT_SOURCE_TREE/src/3rdparty/android
 
 SOURCES += $$PWD/androidplatformplugin.cpp \
+           $$PWD/androidcontentfileengine.cpp \
            $$PWD/androiddeadlockprotector.cpp \
            $$PWD/androidjnimain.cpp \
            $$PWD/androidjniaccessibility.cpp \
@@ -49,6 +50,7 @@ SOURCES += $$PWD/androidplatformplugin.cpp \
            $$PWD/qandroidplatformoffscreensurface.cpp
 
 HEADERS += $$PWD/qandroidplatformintegration.h \
+           $$PWD/androidcontentfileengine.h \
            $$PWD/androiddeadlockprotector.h \
            $$PWD/androidjnimain.h \
            $$PWD/androidjniaccessibility.h \
diff --git a/src/plugins/platforms/android/androidcontentfileengine.cpp b/src/plugins/platforms/android/androidcontentfileengine.cpp
new file mode 100644
index 0000000000..1444407195
--- /dev/null
+++ b/src/plugins/platforms/android/androidcontentfileengine.cpp
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Volker Krause <vkrause@kde.org>
+** 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$
+**
+****************************************************************************/
+
+#include "androidcontentfileengine.h"
+
+#include <private/qjni_p.h>
+#include <private/qjnihelpers_p.h>
+
+#include <QDebug>
+
+AndroidContentFileEngine::AndroidContentFileEngine(const QString &fileName)
+    : QFSFileEngine(fileName)
+{
+}
+
+bool AndroidContentFileEngine::open(QIODevice::OpenMode openMode)
+{
+    QString openModeStr;
+    if (openMode & QFileDevice::ReadOnly) {
+        openModeStr += QLatin1Char('r');
+    }
+    if (openMode & QFileDevice::WriteOnly) {
+        openModeStr += QLatin1Char('w');
+    }
+    if (openMode & QFileDevice::Truncate) {
+        openModeStr += QLatin1Char('t');
+    } else if (openMode & QFileDevice::Append) {
+        openModeStr += QLatin1Char('a');
+    }
+
+    const auto fd = QJNIObjectPrivate::callStaticMethod<jint>("org/qtproject/qt5/android/QtNative",
+        "openFdForContentUrl",
+        "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I",
+        QtAndroidPrivate::context(),
+        QJNIObjectPrivate::fromString(fileName(DefaultName)).object(),
+        QJNIObjectPrivate::fromString(openModeStr).object());
+
+    if (fd < 0) {
+        return false;
+    }
+
+    return QFSFileEngine::open(openMode, fd, QFile::AutoCloseHandle);
+}
+
+
+AndroidContentFileEngineHandler::AndroidContentFileEngineHandler() = default;
+AndroidContentFileEngineHandler::~AndroidContentFileEngineHandler() = default;
+
+QAbstractFileEngine* AndroidContentFileEngineHandler::create(const QString &fileName) const
+{
+    if (!fileName.startsWith(QLatin1String("content"))) {
+        return nullptr;
+    }
+
+    return new AndroidContentFileEngine(fileName);
+}
diff --git a/src/plugins/platforms/android/androidcontentfileengine.h b/src/plugins/platforms/android/androidcontentfileengine.h
new file mode 100644
index 0000000000..db3def03d6
--- /dev/null
+++ b/src/plugins/platforms/android/androidcontentfileengine.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Volker Krause <vkrause@kde.org>
+** 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$
+**
+****************************************************************************/
+
+#ifndef ANDROIDCONTENTFILEENGINE_H
+#define ANDROIDCONTENTFILEENGINE_H
+
+#include <private/qfsfileengine_p.h>
+
+class AndroidContentFileEngine : public QFSFileEngine
+{
+public:
+    AndroidContentFileEngine(const QString &fileName);
+    bool open(QIODevice::OpenMode openMode) override;
+};
+
+class AndroidContentFileEngineHandler : public QAbstractFileEngineHandler
+{
+public:
+    AndroidContentFileEngineHandler();
+    ~AndroidContentFileEngineHandler();
+    QAbstractFileEngine *create(const QString &fileName) const override;
+};
+
+#endif // ANDROIDCONTENTFILEENGINE_H
diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp
index 74edfd8356..6ae429b24e 100644
--- a/src/plugins/platforms/android/androidjnimain.cpp
+++ b/src/plugins/platforms/android/androidjnimain.cpp
@@ -49,6 +49,7 @@
 #include "androidjniinput.h"
 #include "androidjniclipboard.h"
 #include "androidjnimenu.h"
+#include "androidcontentfileengine.h"
 #include "androiddeadlockprotector.h"
 #include "qandroidplatformdialoghelpers.h"
 #include "qandroidplatformintegration.h"
@@ -116,6 +117,7 @@ static double m_scaledDensity = 0;
 static double m_density = 1.0;
 
 static AndroidAssetsFileEngineHandler *m_androidAssetsFileEngineHandler = nullptr;
+static AndroidContentFileEngineHandler *m_androidContentFileEngineHandler = nullptr;
 
 
 
@@ -445,6 +447,7 @@ static jboolean startQtAndroidPlugin(JNIEnv *env, jobject /*object*/, jstring pa
 {
     m_androidPlatformIntegration = nullptr;
     m_androidAssetsFileEngineHandler = new AndroidAssetsFileEngineHandler();
+    m_androidContentFileEngineHandler = new AndroidContentFileEngineHandler();
     m_mainLibraryHnd = nullptr;
     { // Set env. vars
         const char *nativeString = env->GetStringUTFChars(environmentString, 0);
@@ -555,6 +558,8 @@ static void quitQtAndroidPlugin(JNIEnv *env, jclass /*clazz*/)
     m_androidPlatformIntegration = nullptr;
     delete m_androidAssetsFileEngineHandler;
     m_androidAssetsFileEngineHandler = nullptr;
+    delete m_androidContentFileEngineHandler;
+    m_androidContentFileEngineHandler = nullptr;
 }
 
 static void terminateQt(JNIEnv *env, jclass /*clazz*/)
-- 
2.26.2

