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 (!qgcApp()) {
40 return;
41 }
42
43 if (!granted) {
44 qCWarning(AndroidInterfaceLog) << "Storage permission request denied; disabling save to SD card";
45
46 (void)QMetaObject::invokeMethod(
47 qgcApp(),
48 []() {
49 SettingsManager* const settingsManager = SettingsManager::instance();
50 if (!settingsManager) {
51 return;
52 }
53
54 AppSettings* const appSettings = settingsManager->appSettings();
55 if (!appSettings) {
56 return;
57 }
58
59 if (!appSettings->androidDontSaveToSDCard()->rawValue().toBool()) {
60 appSettings->androidDontSaveToSDCard()->setRawValue(true);
61 }
62 },
63 Qt::QueuedConnection);
64 return;
65 }
66
67 (void)QMetaObject::invokeMethod(
68 qgcApp(),
69 []() {
70 SettingsManager* const settingsManager = SettingsManager::instance();
71 if (!settingsManager) {
72 return;
73 }
74
75 AppSettings* const appSettings = settingsManager->appSettings();
76 if (!appSettings || appSettings->androidDontSaveToSDCard()->rawValue().toBool()) {
77 return;
78 }
79
80 SettingsFact* const savePathFact = qobject_cast<SettingsFact*>(appSettings->savePath());
81 if (!savePathFact) {
82 return;
83 }
84
85 const QString appName = QCoreApplication::applicationName();
86 const QString currentSavePath = savePathFact->rawValue().toString();
87 const QString internalBasePath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
88 const QString internalSavePath = QDir(internalBasePath).filePath(appName);
89
90 if (!currentSavePath.isEmpty() && (currentSavePath != internalSavePath)) {
91 return;
92 }
93
94 const QString sdCardRootPath = getSDCardPath();
95 if (sdCardRootPath.isEmpty() || !QDir(sdCardRootPath).exists() || !QFileInfo(sdCardRootPath).isWritable()) {
96 return;
97 }
98
99 const QString sdSavePath = QDir(sdCardRootPath).filePath(appName);
100 if (currentSavePath != sdSavePath) {
101 qCDebug(AndroidInterfaceLog) << "Applying SD card save path after permission grant:" << sdSavePath;
102 savePathFact->setRawValue(sdSavePath);
103 }
104 },
105 Qt::QueuedConnection);
106}
107
108static void jniOnImportResult(JNIEnv* env, jobject, jstring filePathA)
109{
110 const char* const filePathCStr = env->GetStringUTFChars(filePathA, nullptr);
111 const QString filePath = QString::fromUtf8(filePathCStr);
112 env->ReleaseStringUTFChars(filePathA, filePathCStr);
113 (void)QJniEnvironment::checkAndClearExceptions(env);
114 auto callback = std::move(s_importCallback);
115 if (!callback) {
116 return;
117 }
118 callback(filePath);
119}
120
122{
123 qCDebug(AndroidInterfaceLog) << "Registering Native Functions";
124
125 const JNINativeMethod javaMethods[]{
126 {"qgcLogDebug", "(Ljava/lang/String;)V", reinterpret_cast<void*>(jniLogDebug)},
127 {"qgcLogWarning", "(Ljava/lang/String;)V", reinterpret_cast<void*>(jniLogWarning)},
128 {"nativeStoragePermissionsResult", "(Z)V", reinterpret_cast<void*>(jniStoragePermissionsResult)},
129 {"onImportResult", "(Ljava/lang/String;)V", reinterpret_cast<void*>(jniOnImportResult)}};
130
131 QJniEnvironment env;
132 if (!env.registerNativeMethods(kJniQGCActivityClassName, javaMethods, std::size(javaMethods))) {
133 qCWarning(AndroidInterfaceLog) << "Failed to register native methods for" << kJniQGCActivityClassName;
134 } else {
135 qCDebug(AndroidInterfaceLog) << "Native Functions Registered";
136 }
137}
138
140{
141 const bool hasPermission =
142 QJniObject::callStaticMethod<jboolean>(kJniQGCActivityClassName, "checkStoragePermissions", "()Z");
143 QJniEnvironment env;
144 if (env.checkAndClearExceptions()) {
145 qCWarning(AndroidInterfaceLog) << "Exception in checkStoragePermissions";
146 return false;
147 }
148
149 if (hasPermission) {
150 qCDebug(AndroidInterfaceLog) << "Storage permissions granted";
151 } else {
152 qCWarning(AndroidInterfaceLog) << "Storage permissions not granted";
153 }
154
155 return hasPermission;
156}
157
159{
161 qCWarning(AndroidInterfaceLog) << "Storage Permission Denied";
162 return QString();
163 }
164
165 const QJniObject result =
166 QJniObject::callStaticObjectMethod(kJniQGCActivityClassName, "getSDCardPath", "()Ljava/lang/String;");
167 QJniEnvironment env;
168 if (env.checkAndClearExceptions()) {
169 qCWarning(AndroidInterfaceLog) << "Exception in getSDCardPath";
170 return QString();
171 }
172 if (!result.isValid()) {
173 qCWarning(AndroidInterfaceLog) << "Call to java getSDCardPath failed: Invalid Result";
174 return QString();
175 }
176
177 return result.toString();
178}
179
180void openFileImportDialog(const QString& destPath, std::function<void(const QString&)> callback)
181{
182 s_importCallback = std::move(callback);
183
184 const QJniObject jDestPath = QJniObject::fromString(destPath);
185 QJniObject::callStaticMethod<void>(
187 "openFileImportDialog",
188 "(Ljava/lang/String;)V",
189 jDestPath.object<jstring>());
190
191 QJniEnvironment env;
192 if (env.checkAndClearExceptions()) {
193 qCWarning(AndroidInterfaceLog) << "Exception in openFileImportDialog";
194 if (s_importCallback) {
195 auto cb = std::move(s_importCallback);
196 cb(QString());
197 }
198 }
199}
200
201static QSharedPointer<QLocks::QLockBase> s_partialWakeLock;
202static QSharedPointer<QLocks::QLockBase> s_wifiLock;
203
204void setKeepScreenOn(bool on)
205{
206 if (!QAndroidScreen::instance()) {
207 new QAndroidScreen(QCoreApplication::instance());
208 }
209 QAndroidScreen::instance()->keepScreenOn(on);
210
211 if (on) {
212 s_partialWakeLock = QAndroidPartialWakeLocker::instance().getLock();
213 s_wifiLock = QAndroidWiFiLocker::instance().getLock();
214 } else {
215 s_partialWakeLock.reset();
216 s_wifiLock.reset();
217 }
218}
219
220} // namespace AndroidInterface
#define qgcApp()
#define QGC_LOGGING_CATEGORY(name, categoryStr)
Application Settings.
Definition AppSettings.h:10
void setRawValue(const QVariant &value)
Definition Fact.cc:134
QVariant rawValue() const
Definition Fact.h:90
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