Enroute Flight Navigation
A navigation app for VFR pilots
FlightLog.h
1/***************************************************************************
2 * Copyright (C) 2026 by Stefan Kebekus *
3 * stefan.kebekus@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 3 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20
21#pragma once
22
23#include <memory>
24
25#include <QGeoPath>
26#include <QObjectBindableProperty>
27#include <QQmlEngine>
28#include <QTimer>
29#include <QUuid>
30#include <QVariant>
31
32#include "GlobalObject.h"
33#include "flightlog/Flight.h"
34#include "flightlog/FlightDetector.h"
35#include "geomaps/Waypoint.h"
36
37using namespace Qt::Literals::StringLiterals;
38
39namespace Flightlog {
40
42class FlightRecorder;
43
54
55class FlightLog : public GlobalObject
56{
57 Q_OBJECT
58 QML_ELEMENT
59 QML_SINGLETON
60
61public:
62
63 //
64 // Constructors and Destructors
65 //
66
71 explicit FlightLog(QObject* parent = nullptr);
72
73 // deferred initialization
74 void deferredInitialization() override;
75
76 // No default constructor, important for QML singleton
77 explicit FlightLog() = delete;
78
80 ~FlightLog() override;
81
82 // factory function for QML singleton
83 static FlightLog* create(QQmlEngine* /*unused*/, QJSEngine* /*unused*/)
84 {
86 }
87
88
89 //
90 // PROPERTIES
91 //
92
94 Q_PROPERTY(QList<Flightlog::Flight> flights READ flights NOTIFY flightsChanged)
95 [[nodiscard]] auto flights() const -> QList<Flight> { return m_flights; }
96
99 [[nodiscard]] auto detectionState() const -> FlightDetector::DetectionState;
100
102 Q_PROPERTY(int count READ count NOTIFY flightsChanged)
103 [[nodiscard]] auto count() const -> int { return static_cast<int>(m_flights.value().size()); }
104
112 [[nodiscard]] auto displayedTrackPath() const -> QGeoPath;
113
120 [[nodiscard]] auto displayedTrackUuid() const -> QString;
121
127 Q_PROPERTY(bool trackRecording READ trackRecording WRITE setTrackRecording NOTIFY trackRecordingChanged)
128 [[nodiscard]] auto trackRecording() const -> bool;
129 void setTrackRecording(bool enabled);
130
136 Q_PROPERTY(bool showCurrentFlightTrace READ showCurrentFlightTrace WRITE setShowCurrentFlightTrace NOTIFY showCurrentFlightTraceChanged)
137 [[nodiscard]] auto showCurrentFlightTrace() const -> bool;
138 void setShowCurrentFlightTrace(bool enabled);
139
140
141 //
142 // Methods
143 //
144
152 Q_INVOKABLE void addFlight(const Flightlog::Flight& flight);
153
160 Q_INVOKABLE void removeFlight(const QString& uuid);
161
170 Q_INVOKABLE void removeFlights(const QStringList& uuids);
171
177 Q_INVOKABLE void clearFlights();
178
190 Q_INVOKABLE void updateFlight(const QString& uuid, const Flightlog::Flight& flight);
191
209 Q_INVOKABLE static Flightlog::Flight createFlight(
210 const QString& departureICAO,
211 const QString& arrivalICAO,
212 const QString& date,
213 const QString& offBlockTimeStr,
214 const QString& startTimeStr,
215 const QString& landingTimeStr,
216 const QString& onBlockTimeStr,
217 const QString& pilotName,
218 const QString& aircraftCallsign,
219 const QString& comments);
220
227 Q_INVOKABLE void endFlight();
228
238 Q_INVOKABLE QString lastArrivalICAO(const QString& aircraftCallsign) const;
239
251 Q_INVOKABLE static GeoMaps::Waypoint nearestAirfield(const QGeoCoordinate& position = {}, double proximityM = 5000.0);
252
261 [[nodiscard]] Q_INVOKABLE QByteArray exportToIGC(const QString& uuid) const;
262
271 [[nodiscard]] Q_INVOKABLE QByteArray exportToForeFlight(const QStringList& uuids) const;
272
281 [[nodiscard]] Q_INVOKABLE QByteArray exportToJSON(const QStringList& uuids) const;
282
294 [[nodiscard]] Q_INVOKABLE QString importFromJSON(const QString& fileName);
295
303 Q_INVOKABLE void removeTrack(const QString& uuid);
304
312 Q_INVOKABLE void showTrack(const QString& uuid);
313
315 Q_INVOKABLE void hideTrack();
316
317signals:
320
323
326
329
332
337 void takeoffDetected(const QString& time);
338
343 void landingDetected(const QString& time);
344
352 void saveError(const QString& message);
353
364 void backgroundLocationUnavailable(const QString& message);
365
366private slots:
367 // Process position updates — delegates to the active FlightDetector
368 void onPositionUpdated();
369
370 // Handle detection state change — forwards signal
371 void onDetectionStateChanged();
372
373 // Handle auto-detection setting change — manages Android foreground service.
374 // userInitiated is true when the user changed the setting, and false for
375 // the persisted setting at startup. Only a user-initiated change may show
376 // a permission dialog.
377 void onAutoFlightDetectionChanged(bool userInitiated);
378
379#ifdef Q_OS_ANDROID
380 // Posted after a grace period when auto-detection is on but no position
381 // data is arriving. Cancelled when GPS resumes or detection is disabled.
382 void onReceivingPositionInfoChanged(bool receiving);
383
384 // Start or stop the Android foreground service to match the setting.
385 // Starting fails without location permission (Android 14+). Then, if
386 // userInitiated, request the permission and explain if it is declined;
387 // otherwise stay silent and retry when the window regains focus.
388 void updateForegroundService(bool userInitiated);
389#endif
390
391 // Handle takeoff detected by the FlightDetector
392 void onTakeoffDetected(const QString& departureICAO,
393 const QGeoCoordinate& departureCoordinate,
394 const QDateTime& startTime,
395 const QString& aircraftCallsign);
396
397 // Handle landing detected by the FlightDetector
398 void onLandingDetected(const QString& arrivalICAO,
399 const QGeoCoordinate& arrivalCoordinate,
400 const QDateTime& landingTime,
401 int landingCount);
402
403private:
404 Q_DISABLE_COPY_MOVE(FlightLog)
405
406 // Install signal connections for the given detector
407 void connectDetector(FlightDetector* detector);
408
409 // Load flights via m_storage
410 void load();
411
412 // Fill in a coordinate from its ICAO code via the GeoMapProvider, but
413 // only if the coordinate isn't already set (e.g. from a real GPS fix)
414 void resolveCoordinates(Flight& flight);
415
416 // Sort flights by startTime descending
417 void sortFlights(QList<Flight>& flights);
418
419 // Collect the flights matching the given UUID strings; returns all flights if the list is empty
420 [[nodiscard]] auto flightsForUuids(const QStringList& uuids) const -> QList<Flight>;
421
422 // Helper to parse a date+time string to QDateTime
423 static auto parseDateTime(const QString& date, const QString& timeStr) -> QDateTime;
424
425 Q_OBJECT_BINDABLE_PROPERTY(FlightLog, QList<Flightlog::Flight>, m_flights, &FlightLog::flightsChanged)
426
427 // UUID of the flight currently being recorded, or null if none
428 QUuid m_currentFlightUuid;
429
430 // Filename of the flight whose saved track is displayed, or empty
431 QString m_displayedTrackFile;
432
433 // Cached geo path for the displayed saved track
434 QGeoPath m_displayedTrackPath;
435
436 // The active flight detector (owned by this object)
437 FlightDetector* m_detector {nullptr};
438
439 // The flight recorder (owned by this object)
440 std::unique_ptr<FlightRecorder> m_recorder;
441
442 // The persistent storage backend (owned by this object)
443 std::unique_ptr<FlightLogStorage> m_storage;
444
445#ifdef Q_OS_ANDROID
446 // True if the last start of the Android foreground service was accepted
447 // and stop() has not been called since. Java (FlightLogService.start())
448 // owns the actual running state; this flag only decides whether to stop.
449 bool m_foregroundServiceRunning {false};
450
451 // Fires after a grace period to post the "no GPS" notification. Started
452 // when auto-detection is enabled with no position data; cancelled when
453 // position arrives or auto-detection is disabled.
454 QTimer m_noGPSTimer;
455#endif
456};
457
458} // namespace Flightlog
Abstract base class for automatic flight detection strategies.
DetectionState
Detection state for automatic takeoff/landing detection.
SQLite-backed storage for the flight log.
Q_INVOKABLE QByteArray exportToForeFlight(const QStringList &uuids) const
Generate ForeFlight CSV content for selected flights.
void flightsChanged()
Notifier signal.
Flightlog::FlightDetector::DetectionState detectionState
Current state of the automatic detection state machine.
Definition FlightLog.h:98
void displayedTrackPathChanged()
Notifier signal for displayedTrackPath and displayedTrackIndex.
void detectionStateChanged()
Notifier signal.
FlightLog(QObject *parent=nullptr)
Standard constructor.
Q_INVOKABLE QString importFromJSON(const QString &fileName)
Import flights from a JSON file.
void saveError(const QString &message)
Emitted when saving the flight log database fails.
Q_INVOKABLE QString lastArrivalICAO(const QString &aircraftCallsign) const
Find the last arrival ICAO for a given aircraft.
void takeoffDetected(const QString &time)
Emitted when a takeoff is detected.
Q_INVOKABLE QByteArray exportToJSON(const QStringList &uuids) const
Generate JSON content for selected flights.
int count
Number of recorded flights.
Definition FlightLog.h:102
Q_INVOKABLE void showTrack(const QString &uuid)
Show a flight's track on the map.
static Q_INVOKABLE GeoMaps::Waypoint nearestAirfield(const QGeoCoordinate &position={}, double proximityM=5000.0)
Find the nearest airfield within 5 km.
Q_INVOKABLE void removeFlights(const QStringList &uuids)
Remove multiple flights from the log by UUID.
Q_INVOKABLE void hideTrack()
Hide the currently displayed track from the map.
bool showCurrentFlightTrace
Whether the live trace of the current flight is shown on map.
Definition FlightLog.h:136
QString displayedTrackUuid
UUID of the flight whose track is displayed, or empty string if none.
Definition FlightLog.h:119
void backgroundLocationUnavailable(const QString &message)
Emitted when background location access is missing or insufficient.
Q_INVOKABLE void endFlight()
Manually end the current in-flight recording.
void trackRecordingChanged()
Notifier signal.
void deferredInitialization() override
Non-constructor initialization.
Q_INVOKABLE void clearFlights()
Remove all flights from the log.
void showCurrentFlightTraceChanged()
Notifier signal.
Q_INVOKABLE void removeTrack(const QString &uuid)
Delete the recorded track for a flight.
~FlightLog() override
Standard destructor.
bool trackRecording
Whether GPS track recording is enabled.
Definition FlightLog.h:127
static Q_INVOKABLE Flightlog::Flight createFlight(const QString &departureICAO, const QString &arrivalICAO, const QString &date, const QString &offBlockTimeStr, const QString &startTimeStr, const QString &landingTimeStr, const QString &onBlockTimeStr, const QString &pilotName, const QString &aircraftCallsign, const QString &comments)
Create a Flight value from individual field strings.
QList< Flightlog::Flight > flights
List of all recorded flights, newest first.
Definition FlightLog.h:94
Q_INVOKABLE void addFlight(const Flightlog::Flight &flight)
Add a new flight to the log.
Q_INVOKABLE void updateFlight(const QString &uuid, const Flightlog::Flight &flight)
Update an existing flight in the log.
void landingDetected(const QString &time)
Emitted when a landing is detected.
Q_INVOKABLE QByteArray exportToIGC(const QString &uuid) const
Get IGC track content for a flight.
Q_INVOKABLE void removeFlight(const QString &uuid)
Remove a flight from the log by UUID.
QGeoPath displayedTrackPath
Coordinates of the track currently displayed on the map.
Definition FlightLog.h:111
GPS track recorder and IGC file manager.
A single flight log entry.
Definition Flight.h:40
GlobalObject(QObject *parent=nullptr)
Standard constructor.
static Q_INVOKABLE Flightlog::FlightLog * flightLog()
Pointer to appplication-wide static FlightLog instance.