1From d947f2e5ca72f4a88aa71b545492550695464a41 Mon Sep 17 00:00:00 2001
2From: Andy Shaw <andy.shaw@qt.io>
3Date: Tue, 4 Feb 2020 11:39:29 +0100
4Subject: [PATCH 2/4] Android: Add support for getting information about
5 content uris
6
7This enables things like size(), exists() to work with Android content
8uris with the provided uri given from a filedialog. It is expected that
9it is always a full path due to the nature of content uris, so relative
10paths will not work.
11
12Change-Id: I9c9ea42833677eb9d937b33e9dd42ee2a7d9c7c5
13Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
14Reviewed-by: BogDan Vatra <bogdan@kdab.com>
15---
16 .../org/qtproject/qt5/android/QtNative.java   | 57 +++++++++++++++++++
17 .../android/androidcontentfileengine.cpp      | 47 ++++++++++++++-
18 .../android/androidcontentfileengine.h        |  6 ++
19 3 files changed, 108 insertions(+), 2 deletions(-)
20
21diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java
22index f3c4189400..caf6c6ea8a 100644
23--- a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java
24+++ b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java
25@@ -44,6 +44,7 @@ import java.io.File;
26 import java.io.FileNotFoundException;
27 import java.util.ArrayList;
28 import java.util.concurrent.Semaphore;
29+import java.io.IOException;
30
31 import android.app.Activity;
32 import android.app.Service;
33@@ -70,6 +71,7 @@ import android.view.Menu;
34 import android.view.MotionEvent;
35 import android.view.View;
36 import android.view.InputDevice;
37+import android.database.Cursor;
38
39 import java.lang.reflect.Method;
40 import java.security.KeyStore;
41@@ -231,6 +233,61 @@ public class QtNative
42         }
43     }
44
45+    public static long getSize(Context context, String contentUrl)
46+    {
47+        Uri uri = getUriWithValidPermission(context, contentUrl, "r");
48+        long size = -1;
49+
50+        if (uri == null) {
51+            Log.e(QtTAG, "getSize(): No permissions to open Uri");
52+            return size;
53+        }
54+
55+        try {
56+            ContentResolver resolver = context.getContentResolver();
57+            Cursor cur = resolver.query(uri, null, null, null, null);
58+            if (cur != null) {
59+                if (cur.moveToFirst())
60+                    size = cur.getLong(5); // size column
61+                cur.close();
62+            }
63+            return size;
64+        } catch (IllegalArgumentException e) {
65+            Log.e(QtTAG, "getSize(): Invalid Uri");
66+            return size;
67+        }  catch (UnsupportedOperationException e) {
68+            Log.e(QtTAG, "getSize(): Unsupported operation for given Uri");
69+            return size;
70+        }
71+    }
72+
73+    public static boolean checkFileExists(Context context, String contentUrl)
74+    {
75+        Uri uri = getUriWithValidPermission(context, contentUrl, "r");
76+        boolean exists = false;
77+
78+        if (uri == null) {
79+            Log.e(QtTAG, "checkFileExists(): No permissions to open Uri");
80+            return exists;
81+        }
82+
83+        try {
84+            ContentResolver resolver = context.getContentResolver();
85+            Cursor cur = resolver.query(uri, null, null, null, null);
86+            if (cur != null) {
87+                exists = true;
88+                cur.close();
89+            }
90+            return exists;
91+        } catch (IllegalArgumentException e) {
92+            Log.e(QtTAG, "checkFileExists(): Invalid Uri");
93+            return exists;
94+        } catch (UnsupportedOperationException e) {
95+            Log.e(QtTAG, "checkFileExists(): Unsupported operation for given Uri");
96+            return false;
97+        }
98+    }
99+
100     // this method loads full path libs
101     public static void loadQtLibraries(final ArrayList<String> libraries)
102     {
103diff --git a/src/plugins/platforms/android/androidcontentfileengine.cpp b/src/plugins/platforms/android/androidcontentfileengine.cpp
104index 1444407195..3e3bdc2592 100644
105--- a/src/plugins/platforms/android/androidcontentfileengine.cpp
106+++ b/src/plugins/platforms/android/androidcontentfileengine.cpp
107@@ -44,9 +44,10 @@
108
109 #include <QDebug>
110
111-AndroidContentFileEngine::AndroidContentFileEngine(const QString &fileName)
112-    : QFSFileEngine(fileName)
113+AndroidContentFileEngine::AndroidContentFileEngine(const QString &f)
114+    : m_file(f)
115 {
116+    setFileName(f);
117 }
118
119 bool AndroidContentFileEngine::open(QIODevice::OpenMode openMode)
120@@ -78,6 +79,48 @@ bool AndroidContentFileEngine::open(QIODevice::OpenMode openMode)
121     return QFSFileEngine::open(openMode, fd, QFile::AutoCloseHandle);
122 }
123
124+qint64 AndroidContentFileEngine::size() const
125+{
126+    const jlong size = QJNIObjectPrivate::callStaticMethod<jlong>(
127+            "org/qtproject/qt5/android/QtNative", "getSize",
128+            "(Landroid/content/Context;Ljava/lang/String;)J", QtAndroidPrivate::context(),
129+            QJNIObjectPrivate::fromString(fileName(DefaultName)).object());
130+    return (qint64)size;
131+}
132+
133+AndroidContentFileEngine::FileFlags AndroidContentFileEngine::fileFlags(FileFlags type) const
134+{
135+    FileFlags commonFlags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag);
136+    FileFlags flags;
137+    const bool exists = QJNIObjectPrivate::callStaticMethod<jboolean>(
138+            "org/qtproject/qt5/android/QtNative", "checkFileExists",
139+            "(Landroid/content/Context;Ljava/lang/String;)Z", QtAndroidPrivate::context(),
140+            QJNIObjectPrivate::fromString(fileName(DefaultName)).object());
141+    if (!exists)
142+        return flags;
143+    flags = FileType | commonFlags;
144+    return type & flags;
145+}
146+
147+QString AndroidContentFileEngine::fileName(FileName f) const
148+{
149+    switch (f) {
150+        case PathName:
151+        case AbsolutePathName:
152+        case CanonicalPathName:
153+        case DefaultName:
154+        case AbsoluteName:
155+        case CanonicalName:
156+            return m_file;
157+        case BaseName:
158+        {
159+            const int pos = m_file.lastIndexOf(QChar(QLatin1Char('/')));
160+            return m_file.mid(pos);
161+        }
162+        default:
163+            return QString();
164+    }
165+}
166
167 AndroidContentFileEngineHandler::AndroidContentFileEngineHandler() = default;
168 AndroidContentFileEngineHandler::~AndroidContentFileEngineHandler() = default;
169diff --git a/src/plugins/platforms/android/androidcontentfileengine.h b/src/plugins/platforms/android/androidcontentfileengine.h
170index db3def03d6..09e5d77553 100644
171--- a/src/plugins/platforms/android/androidcontentfileengine.h
172+++ b/src/plugins/platforms/android/androidcontentfileengine.h
173@@ -47,6 +47,12 @@ class AndroidContentFileEngine : public QFSFileEngine
174 public:
175     AndroidContentFileEngine(const QString &fileName);
176     bool open(QIODevice::OpenMode openMode) override;
177+    qint64 size() const override;
178+    FileFlags fileFlags(FileFlags type = FileInfoAll) const override;
179+    QString fileName(FileName file = DefaultName) const override;
180+private:
181+    QString m_file;
182+
183 };
184
185 class AndroidContentFileEngineHandler : public QAbstractFileEngineHandler
186--
1872.26.2
188
189