-
Notifications
You must be signed in to change notification settings - Fork 31
Expand file tree
/
Copy pathmain.cpp
More file actions
408 lines (344 loc) · 17.3 KB
/
main.cpp
File metadata and controls
408 lines (344 loc) · 17.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
// Copyright (C) 2024 Michael Torrie and the QtAgOpenGPS Dev Team
// SPDX-License-Identifier: GNU General Public License v3.0 or later
//
// main
#include "formgps.h"
#include "backend.h"
#include <QApplication>
#include <QCoreApplication>
#include <QLabel>
#include "classes/settingsmanager.h" // MASSIVE MIGRATION: New SettingsManager
#include "aogrenderer.h"
#include "classes/agioservice.h" // For auto-registration + C++ usage
#include "classes/ctrack.h" // For auto-registration + C++ usage
#include "classes/cvehicle.h" // For auto-registration + C++ usage
#include "classes/pgnparser.h" // Phase 6.0.21: For ParsedData metatype registration
#include <QProcess>
#include <QSysInfo>
#include <QTranslator> //for translations
#include <QtQml/QQmlEngine>
#include <QtQml/QJSEngine>
#include <QtQml/qqmlregistration.h>
#include <QLoggingCategory>
#include <QIcon>
#include <QPermissions>
#include <QTimer>
#ifdef Q_OS_ANDROID
#include <QJniObject>
#include <QJniEnvironment>
#endif
Q_LOGGING_CATEGORY (mainLog, "main.qtagopengps")
QLabel *grnPixelsWindow;
QLabel *overlapPixelsWindow;
QString findIconPath() {
QString appDir = QCoreApplication::applicationDirPath();
#ifdef Q_OS_WIN
return appDir + "/icons/icon.ico";
#else
return appDir + "/icons/64x64/icon.png";
#endif
}
int main(int argc, char *argv[])
{
qputenv("QSG_RENDER_LOOP", "threaded");
#ifdef Q_OS_ANDROID
QNativeInterface::QAndroidApplication::runOnAndroidMainThread([]() {
QJniObject activity = QNativeInterface::QAndroidApplication::context();
if (activity.isValid()) {
//Keep screen on while app is running
QJniObject window = activity.callObjectMethod("getWindow", "()Landroid/view/Window;");
if (window.isValid()) {
const int FLAG_KEEP_SCREEN_ON = 128;
window.callMethod<void>("addFlags", "(I)V", FLAG_KEEP_SCREEN_ON);
}
// Acquire wake lock to keep CPU running in background
QJniObject serviceName = QJniObject::getStaticObjectField(
"android/content/Context",
"POWER_SERVICE",
"Ljava/lang/String;");
QJniObject powerManager = activity.callObjectMethod(
"getSystemService",
"(Ljava/lang/String;)Ljava/lang/Object;",
serviceName.object());
if (powerManager.isValid()) {
// Check if battery optimization is disabled
QJniObject packageName = activity.callObjectMethod(
"getPackageName",
"()Ljava/lang/String;");
bool isIgnoringBatteryOptimizations = powerManager.callMethod<jboolean>(
"isIgnoringBatteryOptimizations",
"(Ljava/lang/String;)Z",
packageName.object<jstring>());
if (!isIgnoringBatteryOptimizations) {
qDebug(mainLog) << "Battery optimization is enabled - opening settings for user to disable it";
// Open battery optimization settings
QJniObject intent("android/content/Intent", "(Ljava/lang/String;)V",
QJniObject::fromString("android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS").object());
QJniObject uri = QJniObject::callStaticObjectMethod(
"android/net/Uri",
"parse",
"(Ljava/lang/String;)Landroid/net/Uri;",
QJniObject::fromString("package:org.qtagopengps.qtagopengps").object());
intent.callObjectMethod("setData", "(Landroid/net/Uri;)Landroid/content/Intent;", uri.object());
activity.callMethod<void>("startActivity", "(Landroid/content/Intent;)V", intent.object());
qDebug(mainLog) << "Battery optimization settings opened";
} else {
qDebug(mainLog) << "Battery optimization already disabled - app can run unrestricted";
}
// PARTIAL_WAKE_LOCK = 1 (keeps CPU running but allows screen to turn off)
QJniObject tag = QJniObject::fromString("QtAgOpenGPS:WakeLock");
QJniObject wakeLock = powerManager.callObjectMethod(
"newWakeLock",
"(ILjava/lang/String;)Landroid/os/PowerManager$WakeLock;",
1, // PARTIAL_WAKE_LOCK
tag.object<jstring>());
if (wakeLock.isValid()) {
wakeLock.callMethod<void>("acquire", "()V");
qDebug(mainLog) << "Wake lock acquired - CPU will stay active in background";
}
}
// Create and show a persistent notification to indicate the app is running
// This helps prevent Android from killing the app
QJniObject notificationService = QJniObject::getStaticObjectField(
"android/content/Context",
"NOTIFICATION_SERVICE",
"Ljava/lang/String;");
QJniObject notificationManager = activity.callObjectMethod(
"getSystemService",
"(Ljava/lang/String;)Ljava/lang/Object;",
notificationService.object());
if (notificationManager.isValid()) {
// Check Android version
jint sdkInt = QJniObject::getStaticField<jint>("android/os/Build$VERSION", "SDK_INT");
// Create notification channel for Android 8.0+ (API 26+)
if (sdkInt >= 26) {
QJniObject channelId = QJniObject::fromString("qtagopengps_service");
QJniObject channelName = QJniObject::fromString("QtAgOpenGPS Service");
jint importance = 2; // IMPORTANCE_LOW - no sound
QJniObject channel("android/app/NotificationChannel",
"(Ljava/lang/String;Ljava/lang/CharSequence;I)V",
channelId.object<jstring>(),
channelName.object<jstring>(),
importance);
QJniObject description = QJniObject::fromString("Shows when QtAgOpenGPS is running");
channel.callMethod<void>("setDescription", "(Ljava/lang/String;)V",
description.object<jstring>());
notificationManager.callMethod<void>("createNotificationChannel",
"(Landroid/app/NotificationChannel;)V",
channel.object());
}
// Create notification builder
QJniObject builder;
if (sdkInt >= 26) {
QJniObject channelId = QJniObject::fromString("qtagopengps_service");
builder = QJniObject("android/app/Notification$Builder",
"(Landroid/content/Context;Ljava/lang/String;)V",
activity.object(),
channelId.object<jstring>());
} else {
builder = QJniObject("android/app/Notification$Builder",
"(Landroid/content/Context;)V",
activity.object());
}
// Set notification content
QJniObject title = QJniObject::fromString("QtAgOpenGPS Running");
QJniObject text = QJniObject::fromString("GPS guidance active");
builder.callObjectMethod("setContentTitle",
"(Ljava/lang/CharSequence;)Landroid/app/Notification$Builder;",
title.object());
builder.callObjectMethod("setContentText",
"(Ljava/lang/CharSequence;)Landroid/app/Notification$Builder;",
text.object());
// Get the application icon from the manifest
QJniObject appInfo = activity.callObjectMethod("getApplicationInfo",
"()Landroid/content/pm/ApplicationInfo;");
jint iconResId = appInfo.getField<jint>("icon");
qDebug(mainLog) << "Using application icon resource ID:" << iconResId;
builder.callObjectMethod("setSmallIcon",
"(I)Landroid/app/Notification$Builder;",
iconResId);
// Set ongoing = true to make it persistent
builder.callObjectMethod("setOngoing",
"(Z)Landroid/app/Notification$Builder;",
true);
// Build and show notification
QJniObject notification = builder.callObjectMethod("build",
"()Landroid/app/Notification;");
notificationManager.callMethod<void>("notify",
"(ILandroid/app/Notification;)V",
1, // notification ID
notification.object());
qDebug(mainLog) << "Persistent notification created - app marked as running in background";
}
}
});
#endif
// PHASE 6.0.23.1: Disable debug logs to prevent performance issues (40Hz PGN spam)
// Phase 6.0.24: Allow selective debug logging for AgIOService (change agioservice.debug=false to true)
QLoggingCategory::setFilterRules(QStringLiteral(
"*.debug=false\n"
"agioservice.debug=false\n" // Change to true to enable AgIOService debug logs
"*.qtagopengps.debug=true\n"
"formgps_position.qtagopengps=false\n"
"ctool.qtagopengps=false\n"
"formgps_opengl.qtagopengps=false\n"
"qt.scenegraph.general=true\n"
"*.warning=true\n"
"*.critical=true\n"
"*.fatal=true"
));
qSetMessagePattern("%{time hh:mm:ss.zzz} [%{type}] %{function}:%{line} - %{message}");
QApplication a(argc, argv);
QString iconPath = findIconPath();
if (QFile::exists(iconPath)) {
a.setWindowIcon(QIcon(iconPath));
}
QFont f = a.font();
f.setPointSize(16);
a.setFont(f);
QCoreApplication::setOrganizationName("QtAgOpenGPS");
QCoreApplication::setOrganizationDomain("qtagopengps");
QCoreApplication::setApplicationName("QtAgOpenGPS");
//QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);
QSettings::setDefaultFormat(QSettings::IniFormat);
QSettings::setPath(QSettings::IniFormat,
QSettings::UserScope,
QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation));
//We're supposed to be compatible with the saved data
//from this version of AOG:
QCoreApplication::setApplicationVersion("4.1.0");
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#error Requires Qt 6
#endif
// Phase 6.0.21: Register PGNParser::ParsedData for Qt::QueuedConnection signals
qRegisterMetaType<PGNParser::ParsedData>("PGNParser::ParsedData");
//QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
// Explicit QML module registration (ensures static initializer runs)
// qt_add_qml_module generates this function in qtagopengps_qmltyperegistrations.cpp
extern void qml_register_types_AOG();
qml_register_types_AOG();
#ifdef Q_OS_ANDROID
// Request storage permissions for accessing Documents folder on Android
// Use QTimer to defer the request slightly to ensure proper context
QTimer::singleShot(100, []() {
qDebug(mainLog) << "Checking storage permissions...";
// Check Android version
jint sdkInt = QJniObject::getStaticField<jint>("android/os/Build$VERSION", "SDK_INT");
qDebug(mainLog) << "Android SDK version:" << sdkInt;
bool hasPermission = false;
if (sdkInt >= 30) { // Android 11+
// Check for MANAGE_EXTERNAL_STORAGE permission
hasPermission = QJniObject::callStaticMethod<jboolean>(
"android/os/Environment",
"isExternalStorageManager",
"()Z");
qDebug(mainLog) << "MANAGE_EXTERNAL_STORAGE permission:" << hasPermission;
if (!hasPermission) {
qDebug(mainLog) << "Opening Settings to request MANAGE_EXTERNAL_STORAGE...";
// For Android 11+, need to open Settings for MANAGE_EXTERNAL_STORAGE
QJniObject activity = QJniObject::callStaticObjectMethod(
"org/qtproject/qt/android/QtNative",
"activity",
"()Landroid/app/Activity;");
if (activity.isValid()) {
QJniObject intent("android/content/Intent", "(Ljava/lang/String;)V",
QJniObject::fromString("android.settings.MANAGE_APP_ALL_FILES_ACCESS_PERMISSION").object());
QJniObject uri = QJniObject::callStaticObjectMethod(
"android/net/Uri",
"parse",
"(Ljava/lang/String;)Landroid/net/Uri;",
QJniObject::fromString("package:org.qtagopengps.qtagopengps").object());
intent.callObjectMethod("setData", "(Landroid/net/Uri;)Landroid/content/Intent;", uri.object());
activity.callMethod<void>("startActivity", "(Landroid/content/Intent;)V", intent.object());
qDebug(mainLog) << "Settings activity started";
}
}
} else { // Android 10 and below
QJniObject activity = QJniObject::callStaticObjectMethod(
"org/qtproject/qt/android/QtNative",
"activity",
"()Landroid/app/Activity;");
if (activity.isValid()) {
QJniObject permissionString = QJniObject::fromString("android.permission.READ_EXTERNAL_STORAGE");
jint result = activity.callMethod<jint>("checkSelfPermission", "(Ljava/lang/String;)I",
permissionString.object<jstring>());
qDebug(mainLog) << "READ_EXTERNAL_STORAGE permission check result:" << result << "(0 = granted)";
hasPermission = (result == 0);
if (!hasPermission) {
qDebug(mainLog) << "Storage permissions not granted for Android <11";
qDebug(mainLog) << "Note: Standard permission request may not work with Qt Activity";
qDebug(mainLog) << "Consider granting permissions manually in Android Settings";
}
}
}
if (hasPermission) {
qDebug(mainLog) << "Storage permissions already granted";
}
});
#endif
// Request location permissions for GPS functionality
// Use Always availability to get background location access
QLocationPermission locationPermission;
locationPermission.setAccuracy(QLocationPermission::Precise);
locationPermission.setAvailability(QLocationPermission::Always);
switch (a.checkPermission(locationPermission)) {
case Qt::PermissionStatus::Undetermined:
qDebug(mainLog) << "Location permission undetermined, requesting...";
a.requestPermission(locationPermission, [](const QPermission &permission) {
if (qApp->checkPermission(permission) == Qt::PermissionStatus::Granted) {
qDebug(mainLog) << "Location permission granted";
} else {
qWarning() << "Location permission denied - GPS functionality will not work";
}
});
break;
case Qt::PermissionStatus::Denied:
qWarning() << "Location permission denied - GPS functionality will not work";
break;
case Qt::PermissionStatus::Granted:
qDebug(mainLog) << "Location permission already granted";
break;
}
FormGPS *w = new FormGPS();
if (!w) {
qFatal(mainLog) << "Could not allocate FormGPS on the stack!";
}
#ifdef Q_OS_ANDROID
// Monitor application state to log when going to background
QObject::connect(&a, &QGuiApplication::applicationStateChanged,
[](Qt::ApplicationState state) {
switch (state) {
case Qt::ApplicationActive:
qDebug(mainLog) << "App became ACTIVE (foreground)";
break;
case Qt::ApplicationInactive:
qDebug(mainLog) << "App became INACTIVE (transitioning)";
break;
case Qt::ApplicationHidden:
qDebug(mainLog) << "App became HIDDEN";
break;
case Qt::ApplicationSuspended:
qDebug(mainLog) << "App became SUSPENDED (background)";
qDebug(mainLog) << "WARNING: Qt Activity is paused - OpenGL context may be destroyed";
qDebug(mainLog) << "Critical processing should continue due to wake lock";
break;
}
});
#endif
// MASSIVE MIGRATION: Validate SettingsManager accessible
qDebug(mainLog) << "SettingsManager instance:" << SettingsManager::instance();
qDebug(mainLog) << "SettingsManager initialization: completed";
if (SettingsManager::instance()->display_showBack()) {
grnPixelsWindow = new QLabel("Back Buffer");
grnPixelsWindow->setFixedWidth(500);
grnPixelsWindow->setFixedHeight(500);
grnPixelsWindow->show();
overlapPixelsWindow = new QLabel("overlap buffer");
//overlapPixelsWindow->setFixedWidth(1300);
//overlapPixelsWindow->setFixedHeight(900);
overlapPixelsWindow->show();
}
int result = a.exec();
delete w;
return result;
}