QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
AndroidInterface.cc
Go to the documentation of this file.
1#include "AndroidInterface.h"
2
3#include <QAndroidScreen.h>
4#include <QtAndroidHelpers/QAndroidPartialWakeLocker.h>
5#include <QtAndroidHelpers/QAndroidWiFiLocker.h>
6#include <QtCore/QCoreApplication>
7#include <QtCore/QDir>
8#include <QtCore/QFileInfo>
9#include <QtCore/QJniEnvironment>
10#include <QtCore/QJniObject>
11#include <QtCore/QMetaObject>
12#include <QtCore/QSharedPointer>
13#include <QtCore/QStandardPaths>
14
15#include "AppSettings.h"
16#include "QGCApplication.h"
17#include "QGCLoggingCategory.h"
18#include "SettingsFact.h"
19#include "SettingsManager.h"
20
21QGC_LOGGING_CATEGORY(AndroidInterfaceLog, "Android.AndroidInterface")
22
24
25static std::function<void(const QString&)> s_importCallback;
26
27static void jniLogDebug(JNIEnv*, jobject, jstring message)
28{
29 qCDebug(AndroidInterfaceLog) << QJniObject(message).toString();
30}
31
32static void jniLogWarning(JNIEnv*, jobject, jstring message)
33{
34 qCWarning(AndroidInterfaceLog) << QJniObject(message).toString();
35}
36
37static void jniStoragePermissionsResult(JNIEnv*, jobject, jboolean granted)
38{
39 if (!granted) {
40 qCWarning(AndroidInterfaceLog) << "Storage permission request denied";
41 return;
42 }
43
44 if (!qgcApp()) {
45 return;
46 }
47
48 (void)QMetaObject::invokeMethod(
49 qgcApp(),
50 []() {
51 SettingsManager* const settingsManager = SettingsManager::instance();
52 if (!settingsManager) {
53 return;
54 }
55
56 AppSettings* const appSettings = settingsManager->appSettings();
57 if (!appSettings || appSettings->androidDontSaveToSDCard()->rawValue().toBool()) {
58 return;
59 }
60
61 SettingsFact* const savePathFact = qobject_cast<SettingsFact*>(appSettings->savePath());
62 if (!savePathFact) {
63 return;
64 }
65
66 const QString appName = QCoreApplication::applicationName();
67 const QString currentSavePath = savePathFact->rawValue().toString();
68 const QString internalBasePath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
69 const QString internalSavePath = QDir(internalBasePath).filePath(appName);
70
71 if (!currentSavePath.isEmpty() && (currentSavePath != internalSavePath)) {
72 return;
73 }
74
75 const QString sdCardRootPath = getSDCardPath();
76 if (sdCardRootPath.isEmpty() || !QDir(sdCardRootPath).exists() || !QFileInfo(sdCardRootPath).isWritable()) {
77 return;
78 }
79
80 const QString sdSavePath = QDir(sdCardRootPath).filePath(appName);
81 if (currentSavePath != sdSavePath) {
82 qCDebug(AndroidInterfaceLog) << "Applying SD card save path after permission grant:" << sdSavePath;
83 savePathFact->setRawValue(sdSavePath);
84 }
85 },
86 Qt::QueuedConnection);
87}
88
89static void jniOnImportResult(JNIEnv* env, jobject, jstring filePathA)
90{
91 const char* const filePathCStr = env->GetStringUTFChars(filePathA, nullptr);
92 const QString filePath = QString::fromUtf8(filePathCStr);
93 env->ReleaseStringUTFChars(filePathA, filePathCStr);
94 (void)QJniEnvironment::checkAndClearExceptions(env);
95 auto callback = std::move(s_importCallback);
96 if (!callback) {
97 return;
98 }
99 callback(filePath);
100}
101
103{
104 qCDebug(AndroidInterfaceLog) << "Registering Native Functions";
105
106 const JNINativeMethod javaMethods[]{
107 {"qgcLogDebug", "(Ljava/lang/String;)V", reinterpret_cast<void*>(jniLogDebug)},
108 {"qgcLogWarning", "(Ljava/lang/String;)V", reinterpret_cast<void*>(jniLogWarning)},
109 {"nativeStoragePermissionsResult", "(Z)V", reinterpret_cast<void*>(jniStoragePermissionsResult)},
110 {"onImportResult", "(Ljava/lang/String;)V", reinterpret_cast<void*>(jniOnImportResult)}};
111
112 QJniEnvironment env;
113 if (!env.registerNativeMethods(kJniQGCActivityClassName, javaMethods, std::size(javaMethods))) {
114 qCWarning(AndroidInterfaceLog) << "Failed to register native methods for" << kJniQGCActivityClassName;
115 } else {
116 qCDebug(AndroidInterfaceLog) << "Native Functions Registered";
117 }
118}
119
121{
122 const bool hasPermission =
123 QJniObject::callStaticMethod<jboolean>(kJniQGCActivityClassName, "checkStoragePermissions", "()Z");
124 QJniEnvironment env;
125 if (env.checkAndClearExceptions()) {
126 qCWarning(AndroidInterfaceLog) << "Exception in checkStoragePermissions";
127 return false;
128 }
129
130 if (hasPermission) {
131 qCDebug(AndroidInterfaceLog) << "Storage permissions granted";
132 } else {
133 qCWarning(AndroidInterfaceLog) << "Storage permissions not granted";
134 }
135
136 return hasPermission;
137}
138
140{
142 qCWarning(AndroidInterfaceLog) << "Storage Permission Denied";
143 return QString();
144 }
145
146 const QJniObject result =
147 QJniObject::callStaticObjectMethod(kJniQGCActivityClassName, "getSDCardPath", "()Ljava/lang/String;");
148 QJniEnvironment env;
149 if (env.checkAndClearExceptions()) {
150 qCWarning(AndroidInterfaceLog) << "Exception in getSDCardPath";
151 return QString();
152 }
153 if (!result.isValid()) {
154 qCWarning(AndroidInterfaceLog) << "Call to java getSDCardPath failed: Invalid Result";
155 return QString();
156 }
157
158 return result.toString();
159}
160
161void openFileImportDialog(const QString& destPath, std::function<void(const QString&)> callback)
162{
163 s_importCallback = std::move(callback);
164
165 const QJniObject jDestPath = QJniObject::fromString(destPath);
166 QJniObject::callStaticMethod<void>(
168 "openFileImportDialog",
169 "(Ljava/lang/String;)V",
170 jDestPath.object<jstring>());
171
172 QJniEnvironment env;
173 if (env.checkAndClearExceptions()) {
174 qCWarning(AndroidInterfaceLog) << "Exception in openFileImportDialog";
175 if (s_importCallback) {
176 auto cb = std::move(s_importCallback);
177 cb(QString());
178 }
179 }
180}
181
182static QSharedPointer<QLocks::QLockBase> s_partialWakeLock;
183static QSharedPointer<QLocks::QLockBase> s_wifiLock;
184
185void setKeepScreenOn(bool on)
186{
187 if (!QAndroidScreen::instance()) {
188 new QAndroidScreen(QCoreApplication::instance());
189 }
190 QAndroidScreen::instance()->keepScreenOn(on);
191
192 if (on) {
193 s_partialWakeLock = QAndroidPartialWakeLocker::instance().getLock();
194 s_wifiLock = QAndroidWiFiLocker::instance().getLock();
195 } else {
196 s_partialWakeLock.reset();
197 s_wifiLock.reset();
198 }
199}
200
201} // namespace AndroidInterface
#define qgcApp()
#define QGC_LOGGING_CATEGORY(name, categoryStr)
Application Settings.
Definition AppSettings.h:10
void setRawValue(const QVariant &value)
Definition Fact.cc:128
QVariant rawValue() const
Value after translation.
Definition Fact.h:85
A SettingsFact is Fact which holds a QSettings value.
Provides access to all app settings.
static SettingsManager * instance()
AppSettings * appSettings() const
static void jniOnImportResult(JNIEnv *env, jobject, jstring filePathA)
static void jniLogDebug(JNIEnv *, jobject, jstring message)
static void jniStoragePermissionsResult(JNIEnv *, jobject, jboolean granted)
static std::function< void(const QString &)> s_importCallback
static void jniLogWarning(JNIEnv *, jobject, jstring message)
static QSharedPointer< QLocks::QLockBase > s_wifiLock
void openFileImportDialog(const QString &destPath, std::function< void(const QString &)> callback)
void setKeepScreenOn(bool on)
constexpr const char * kJniQGCActivityClassName
static QSharedPointer< QLocks::QLockBase > s_partialWakeLock