MauiKit Controls

mauiandroid.cpp
1/*
2 * Copyright 2018 Camilo Higuita <milo.h@aol.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Library General Public License as
6 * published by the Free Software Foundation; either version 2, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20#include "mauiandroid.h"
21#include <QColor>
22#include <QCoreApplication>
23#include <QDebug>
24#include <QException>
25#include <QFile>
26#include <QFileInfo>
27#include <QImage>
28#include <QMimeData>
29#include <QMimeDatabase>
30#include <QProcess>
31#include <QUrl>
32
33#include <android/bitmap.h>
34// WindowManager.LayoutParams
35#define FLAG_TRANSLUCENT_STATUS 0x04000000
36#define FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS 0x80000000
37// View
38#define SYSTEM_UI_FLAG_LIGHT_STATUS_BAR 0x00002000
39#define SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR 0x00000010
40
41class InterfaceConnFailedException : public QException
42{
43public:
44 void raise() const
45 {
46 throw *this;
47 }
48 InterfaceConnFailedException *clone() const
49 {
50 return new InterfaceConnFailedException(*this);
51 }
52};
53
54static MAUIAndroid *m_instance = nullptr;
55
56MAUIAndroid::MAUIAndroid(QObject *parent)
57 : AbstractPlatform(parent)
58{
59 m_instance = this;
60// connect(qApp, &QCoreApplication::aboutToQuit, []()
61// {
62// qDebug() << "Lets remove MauiApp singleton instance";
63// delete m_instance;
64// m_instance = nullptr;
65// });
66
67}
68
69static QAndroidJniObject getAndroidWindow()
70{
71 QAndroidJniObject window = QtAndroid::androidActivity().callObjectMethod("getWindow", "()Landroid/view/Window;");
72 window.callMethod<void>("addFlags", "(I)V", FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
73 window.callMethod<void>("clearFlags", "(I)V", FLAG_TRANSLUCENT_STATUS);
74 return window;
75}
76
77void MAUIAndroid::statusbarColor(const QString &bg, const bool &light)
78{
79 if (QtAndroid::androidSdkVersion() <= 23)
80 return;
81
82 QtAndroid::runOnAndroidThread([=]() {
83 QAndroidJniObject window = getAndroidWindow();
84 QAndroidJniObject view = window.callObjectMethod("getDecorView", "()Landroid/view/View;");
85 int visibility = view.callMethod<int>("getSystemUiVisibility", "()I");
86 if (light)
87 visibility |= SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
88 else
89 visibility &= ~SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
90 view.callMethod<void>("setSystemUiVisibility", "(I)V", visibility);
91 window.callMethod<void>("setStatusBarColor", "(I)V", QColor(bg).rgba());
92 });
93}
94
95void MAUIAndroid::navBarColor(const QString &bg, const bool &light)
96{
97 if (QtAndroid::androidSdkVersion() <= 23)
98 return;
99
100 QtAndroid::runOnAndroidThread([=]() {
101 QAndroidJniObject window = getAndroidWindow();
102 QAndroidJniObject view = window.callObjectMethod("getDecorView", "()Landroid/view/View;");
103 int visibility = view.callMethod<int>("getSystemUiVisibility", "()I");
104 if (light)
105 visibility |= SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
106 else
107 visibility &= ~SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
108 view.callMethod<void>("setSystemUiVisibility", "(I)V", visibility);
109 window.callMethod<void>("setNavigationBarColor", "(I)V", QColor(bg).rgba());
110 });
111}
112
114{
115 QAndroidJniEnvironment _env;
116 QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;"); // activity is valid
117 if (_env->ExceptionCheck()) {
118 _env->ExceptionClear();
119 throw InterfaceConnFailedException();
120 }
121
122 if(urls.isEmpty())
123 return;
124
125 if (activity.isValid())
126 {
127 qDebug() << "trying to share dialog << valid";
128
129 QMimeDatabase mimedb;
130 QString mimeType = mimedb.mimeTypeForFile(urls.first().toLocalFile()).name();
131
132 jobjectArray stringArray = _env->NewObjectArray(urls.count(), _env->FindClass("java/lang/String"), NULL);
133
134 int index = -1;
135 for(auto url : urls)
136 {
137 _env->SetObjectArrayElement(stringArray, ++index, QAndroidJniObject::fromString(url.toLocalFile()).object<jstring>());
138 }
139
140 QAndroidJniObject::callStaticMethod<void>("com/kde/maui/tools/SendIntent",
141 "share",
142 "(Landroid/app/Activity;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
143 activity.object<jobject>(),
144 QAndroidJniObject::fromLocalRef(stringArray).object<jobjectArray>(),
145 QAndroidJniObject::fromString(mimeType).object<jstring>(),
146 QAndroidJniObject::fromString(QString("%1.fileprovider").arg(qApp->organizationDomain())).object<jstring>());
147
148 if (_env->ExceptionCheck())
149 {
150 qDebug() << "trying to share dialog << exception";
151
152 _env->ExceptionClear();
153 throw InterfaceConnFailedException();
154 }
155 } else
156 throw InterfaceConnFailedException();
157}
158
159
161{
162 qDebug() << "trying to share text";
163 QAndroidJniEnvironment _env;
164 QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;"); // activity is valid
165 if (_env->ExceptionCheck()) {
166 _env->ExceptionClear();
167 throw InterfaceConnFailedException();
168 }
169 if (activity.isValid()) {
170 QAndroidJniObject::callStaticMethod<void>("com/kde/maui/tools/SendIntent", "sendText", "(Landroid/app/Activity;Ljava/lang/String;)V", activity.object<jobject>(), QAndroidJniObject::fromString(text).object<jstring>());
171
172 if (_env->ExceptionCheck()) {
173 _env->ExceptionClear();
174 throw InterfaceConnFailedException();
175 }
176 } else
177 throw InterfaceConnFailedException();
178}
179
181{
182 qDebug() << "trying to share link";
183 QAndroidJniEnvironment _env;
184 QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;"); // activity is valid
185 if (_env->ExceptionCheck()) {
186 _env->ExceptionClear();
187 throw InterfaceConnFailedException();
188 }
189 if (activity.isValid()) {
190 QAndroidJniObject::callStaticMethod<void>("com/kde/maui/tools/SendIntent", "sendUrl", "(Landroid/app/Activity;Ljava/lang/String;)V", activity.object<jobject>(), QAndroidJniObject::fromString(link).object<jstring>());
191
192 if (_env->ExceptionCheck()) {
193 _env->ExceptionClear();
194 throw InterfaceConnFailedException();
195 }
196 } else
197 throw InterfaceConnFailedException();
198}
199
201{
202 qDebug() << "trying to open file with";
203 QAndroidJniEnvironment _env;
204 QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;"); // activity is valid
205 if (_env->ExceptionCheck()) {
206 _env->ExceptionClear();
207 throw InterfaceConnFailedException();
208 }
209 if (activity.isValid()) {
210 QAndroidJniObject::callStaticMethod<void>("com/kde/maui/tools/SendIntent",
211 "openUrl",
212 "(Landroid/app/Activity;Ljava/lang/String;Ljava/lang/String;)V",
213 activity.object<jobject>(),
214 QAndroidJniObject::fromString(url.toLocalFile()).object<jstring>(),
215 QAndroidJniObject::fromString(QString("%1.fileprovider").arg(qApp->organizationDomain())).object<jstring>());
216
217 if (_env->ExceptionCheck()) {
218 _env->ExceptionClear();
219 throw InterfaceConnFailedException();
220 }
221 } else
222 throw InterfaceConnFailedException();
223}
224
226{
227 QAndroidJniObject mediaDir = QAndroidJniObject::callStaticObjectMethod("android/os/Environment", "getExternalStorageDirectory", "()Ljava/io/File;");
228 QAndroidJniObject mediaPath = mediaDir.callObjectMethod("getAbsolutePath", "()Ljava/lang/String;");
229
230 return mediaPath.toString();
231}
232
234{
235 QStringList res;
236
237// QAndroidJniObject mediaDir = QAndroidJniObject::callStaticObjectMethod("com/kde/maui/tools/SDCard", "findSdCardPath", "(Landroid/content/Context;)Ljava/io/File;", QtAndroid::androidActivity().object<jobject>());
238
239// if (mediaDir == NULL)
240// return res;
241
242// QAndroidJniObject mediaPath = mediaDir.callObjectMethod("getAbsolutePath", "()Ljava/lang/String;");
243// QString dataAbsPath = mediaPath.toString();
244
245// res << QUrl::fromLocalFile(dataAbsPath).toString();
246 return res;
247}
248
249// void MAUIAndroid::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data)
250//{
251// qDebug() << "ACTIVITY RESULTS";
252// jint RESULT_OK = QAndroidJniObject::getStaticField<jint>("android/app/Activity", "RESULT_OK");
253
254// if (receiverRequestCode == 42 && resultCode == RESULT_OK) {
255// QString url = data.callObjectMethod("getData", "()Landroid/net/Uri;").callObjectMethod("getPath", "()Ljava/lang/String;").toString();
256// emit folderPicked(url);
257// }
258//}
259
261{
262 QAndroidJniEnvironment _env;
263 QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;"); // activity is valid
264 if (_env->ExceptionCheck()) {
265 _env->ExceptionClear();
266 throw InterfaceConnFailedException();
267 }
268 if (activity.isValid()) {
269 QAndroidJniObject::callStaticMethod<void>("com/example/android/tools/SendIntent", "fileChooser", "(Landroid/app/Activity;)V", activity.object<jobject>());
270 if (_env->ExceptionCheck()) {
271 _env->ExceptionClear();
272 throw InterfaceConnFailedException();
273 }
274 } else
275 throw InterfaceConnFailedException();
276}
277
278QVariantList MAUIAndroid::transform(const QAndroidJniObject &obj)
279{
280 QVariantList res;
281 const auto size = obj.callMethod<jint>("size", "()I");
282
283 for (auto i = 0; i < size; i++) {
284 QAndroidJniObject hashObj = obj.callObjectMethod("get", "(I)Ljava/lang/Object;", i);
285 res << createVariantMap(hashObj.object<jobject>());
286 }
287
288 return res;
289}
290
291QVariantMap MAUIAndroid::createVariantMap(jobject data)
292{
293 QVariantMap res;
294
295 QAndroidJniEnvironment env;
296 /* Reference : https://community.oracle.com/thread/1549999 */
297
298 // Get the HashMap Class
299 jclass jclass_of_hashmap = (env)->GetObjectClass(data);
300
301 // Get link to Method "entrySet"
302 jmethodID entrySetMethod = (env)->GetMethodID(jclass_of_hashmap, "entrySet", "()Ljava/util/Set;");
303
304 // Invoke the "entrySet" method on the HashMap object
305 jobject jobject_of_entryset = env->CallObjectMethod(data, entrySetMethod);
306
307 // Get the Set Class
308 jclass jclass_of_set = (env)->FindClass("java/util/Set"); // Problem during compilation !!!!!
309
310 if (jclass_of_set == 0) {
311 qWarning() << "java/util/Set lookup failed\n";
312 return res;
313 }
314
315 // Get link to Method "iterator"
316 jmethodID iteratorMethod = env->GetMethodID(jclass_of_set, "iterator", "()Ljava/util/Iterator;");
317
318 // Invoke the "iterator" method on the jobject_of_entryset variable of type Set
319 jobject jobject_of_iterator = env->CallObjectMethod(jobject_of_entryset, iteratorMethod);
320
321 // Get the "Iterator" class
322 jclass jclass_of_iterator = (env)->FindClass("java/util/Iterator");
323
324 // Get link to Method "hasNext"
325 jmethodID hasNextMethod = env->GetMethodID(jclass_of_iterator, "hasNext", "()Z");
326
327 jmethodID nextMethod = env->GetMethodID(jclass_of_iterator, "next", "()Ljava/lang/Object;");
328
329 while (env->CallBooleanMethod(jobject_of_iterator, hasNextMethod)) {
330 jobject jEntry = env->CallObjectMethod(jobject_of_iterator, nextMethod);
331 QAndroidJniObject entry = QAndroidJniObject(jEntry);
332 QAndroidJniObject key = entry.callObjectMethod("getKey", "()Ljava/lang/Object;");
333 QAndroidJniObject value = entry.callObjectMethod("getValue", "()Ljava/lang/Object;");
334 QString k = key.toString();
335
336 QVariant v = value.toString();
337
338 env->DeleteLocalRef(jEntry);
339
340 if (v.isNull()) {
341 continue;
342 }
343
344 res[k] = v;
345 }
346
347 if (env->ExceptionOccurred()) {
348 env->ExceptionDescribe();
349 env->ExceptionClear();
350 }
351
352 env->DeleteLocalRef(jclass_of_hashmap);
353 env->DeleteLocalRef(jobject_of_entryset);
354 env->DeleteLocalRef(jclass_of_set);
355 env->DeleteLocalRef(jobject_of_iterator);
356 env->DeleteLocalRef(jclass_of_iterator);
357
358 return res;
359}
360
362{
363 qDebug() << "CHECKIGN PERMISSSIONS";
364
365 for (const auto &permission : permissions) {
366 QtAndroid::PermissionResult r = QtAndroid::checkPermission(permission);
367 if (r == QtAndroid::PermissionResult::Denied) {
368 QtAndroid::requestPermissionsSync({permission});
369 r = QtAndroid::checkPermission(permission);
370 if (r == QtAndroid::PermissionResult::Denied) {
371 qWarning() << "Permission denied";
372 return false;
373 }
374 }
375 }
376
377 qDebug() << "Permissions granted!";
378 return true;
379}
380
381bool MAUIAndroid::hasKeyboard()
382{
383 QAndroidJniObject context = QtAndroid::androidContext().object<jobject>();
384
385 if (context.isValid()) {
386 QAndroidJniObject resources = context.callObjectMethod("getResources", "()Landroid/content/res/Resources;");
387 QAndroidJniObject config = resources.callObjectMethod("getConfiguration", "()Landroid/content/res/Configuration;");
388 int value = config.getField<jint>("keyboard");
389 // QVariant v = value.toString();
390 qDebug() << "KEYBOARD" << value;
391
392 return value == 2 || value == 3; // KEYBOARD_12KEY || KEYBOARD_QWERTY
393
394 } else
395 throw InterfaceConnFailedException();
396}
397
398bool MAUIAndroid::hasMouse()
399{
400 return false;
401}
402
403void MAUIAndroid::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data)
404{
405 qDebug() << "ACTIVITY RESULTS" << receiverRequestCode;
406 emit this->hasKeyboardChanged();
407 jint RESULT_OK = QAndroidJniObject::getStaticField<jint>("android/app/Activity", "RESULT_OK");
408
409 if (receiverRequestCode == 42 && resultCode == RESULT_OK) {
410 QString url = data.callObjectMethod("getData", "()Landroid/net/Uri;").callObjectMethod("getPath", "()Ljava/lang/String;").toString();
411 emit folderPicked(url);
412 }
413}
414
415
416bool MAUIAndroid::darkModeEnabled()
417{
418 jint res = QAndroidJniObject::callStaticMethod<jint>(
419 "com/kde/maui/tools/ConfigActivity",
420 "systemStyle",
421 "(Landroid/content/Context;)I",
422 QtAndroid::androidActivity().object());
423
424 return res == 1;
425}
426
427//JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/)
428//{
429// JNIEnv* env;
430// if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
431// return JNI_ERR;
432// }
433
434// jclass javaClass = env->FindClass("com/kde/maui/tools/ConfigActivity");
435// if (!javaClass)
436// return JNI_ERR;
437
438// if (env->RegisterNatives(javaClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
439// return JNI_ERR;
440// }
441// return JNI_VERSION_1_6;
442//}
The AbstractPlatform class Defines abstract methods and properties that are common to be implemeted b...
The MAUIAndroid class.
Definition mauiandroid.h:40
static bool checkRunTimePermissions(const QStringList &permissions)
checkRunTimePermissions
void folderPicked(QString path)
folderPicked
void shareText(const QString &text) override final
shareText
static void shareLink(const QString &link)
shareLink
static QString homePath()
homePath
static void openUrl(const QUrl &url)
openUrl
static QStringList sdDirs()
sdDirs
static void navBarColor(const QString &bg, const bool &light)
navBarColor
static void fileChooser()
fileChooser
static void statusbarColor(const QString &bg, const bool &light)
statusbarColor
void shareFiles(const QList< QUrl > &urls) override final
shareFiles
KGUIADDONS_EXPORT QWindow * window(QObject *job)
QRgb rgba() const const
qsizetype count() const const
T & first()
bool isEmpty() const const
QMimeType mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode) const const
QString toLocalFile() const const
bool isNull() const const
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 17 2024 11:56:16 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.