InterfaceGPS 0.1.0
Interface embarquée Qt pour navigation, multimédia, caméra et télémétrie
Chargement...
Recherche...
Aucune correspondance
settingspage.cpp
Aller à la documentation de ce fichier.
1
10#include "settingspage.h"
11#include "ui_settingspage.h"
12#include "telemetrydata.h"
13#include <QMessageBox>
14#include <QDebug>
15#include <QProcess>
16#include <QTimer>
17
19 : QWidget(parent), ui(new Ui::SettingsPage)
20{
21 ui->setupUi(this);
22
23 m_lastActiveMac = "";
24 m_localDevice = new QBluetoothLocalDevice(this);
25
26 // --- GESTION DE LA DÉCOUVRABILITÉ (SÉCURITÉ) ---
27 // Le mode appairage est désactivé automatiquement après 2 minutes (120 000 ms)
28 // pour éviter que le véhicule ne reste vulnérable en permanence.
29 m_discoveryTimer = new QTimer(this);
30 m_discoveryTimer->setSingleShot(true);
31 m_discoveryTimer->setInterval(120000);
32 connect(m_discoveryTimer, &QTimer::timeout, this, &SettingsPage::stopDiscovery);
33
34 connect(m_localDevice, &QBluetoothLocalDevice::errorOccurred,
35 this, &SettingsPage::errorOccurred);
36
37 // Connexion des boutons de l'interface
38 connect(ui->btnVisible, &QPushButton::clicked, this, &SettingsPage::onVisibleClicked);
39 connect(ui->btnForget, &QPushButton::clicked, this, &SettingsPage::onForgetClicked);
40
41 // Le bouton "Oublier" ne s'active que si un appareil est explicitement sélectionné dans la liste
42 ui->btnForget->setEnabled(false);
43 connect(ui->listDevices, &QListWidget::itemSelectionChanged, this, [this](){
44 QListWidgetItem *item = ui->listDevices->currentItem();
45 bool hasSelection = item && !item->data(Qt::UserRole).toString().isEmpty();
46 ui->btnForget->setEnabled(hasSelection);
47 });
48
49 // --- SYNCHRONISATION EN TEMPS RÉEL (POLLING) ---
50 // Interroge le système d'exploitation toutes les 2 secondes pour voir les nouveaux appareils
51 m_pollTimer = new QTimer(this);
52 connect(m_pollTimer, &QTimer::timeout, this, &SettingsPage::refreshPairedList);
53 m_pollTimer->start(2000);
54
55 refreshPairedList();
56}
57
59 delete ui;
60}
61
62void SettingsPage::showAutoClosingMessage(const QString &title, const QString &text, int timeoutMs)
63{
64 // Crée une boîte de dialogue (Popup) non bloquante
65 QMessageBox *msgBox = new QMessageBox(this);
66 msgBox->setWindowTitle(title);
67 msgBox->setText(text);
68 msgBox->setStandardButtons(QMessageBox::NoButton); // Pas de bouton "OK" à cliquer
69 msgBox->setIcon(QMessageBox::Information);
70 msgBox->show();
71
72 // Fermeture propre et libération mémoire après le délai demandé
73 QTimer::singleShot(timeoutMs, msgBox, [msgBox]() {
74 msgBox->close();
75 msgBox->deleteLater();
76 });
77}
78
79void SettingsPage::refreshPairedList()
80{
81 // Source de vérité Bluetooth : bluetoothctl est interrogé périodiquement via des commandes shell
82 // pour refléter l'état système réel sous Linux (plus fiable que QBluetoothDeviceDiscoveryAgent dans ce contexte).
83 QProcess process;
84 process.start("bluetoothctl", QStringList() << "devices");
85 process.waitForFinished();
86 QString output = process.readAllStandardOutput().trimmed();
87
88 // Mémorisation de la sélection utilisateur pour ne pas la perdre lors du rafraîchissement
89 QString selectedMac;
90 if (ui->listDevices->currentItem()) {
91 selectedMac = ui->listDevices->currentItem()->data(Qt::UserRole).toString();
92 }
93
94 ui->listDevices->clear();
95
96 QStringList lines = output.split('\n');
97 bool found = false;
98
99 QString newcomerMac;
100 QString newcomerName;
101 QMap<QString, QString> connectedDevices;
102
103 // Analyse (Parsing) de la sortie de bluetoothctl
104 for (const QString &line : lines) {
105 // Format attendu: "Device <MAC> <Nom>". Les lignes invalides sont ignorées.
106 if (line.trimmed().isEmpty()) continue;
107
108 QStringList parts = line.split(' ');
109 if (parts.size() >= 3 && parts[0] == "Device") {
110 QString mac = parts[1];
111 QString name = parts.mid(2).join(' '); // Le nom peut contenir des espaces
112
113 // On lance une seconde requête pour savoir si cet appareil spécifique est actuellement connecté
114 QProcess infoProcess;
115 infoProcess.start("bluetoothctl", QStringList() << "info" << mac);
116 infoProcess.waitForFinished();
117 QString infoOutput = infoProcess.readAllStandardOutput();
118 bool isConnected = infoOutput.contains("Connected: yes");
119
120 if (isConnected) {
121 connectedDevices.insert(mac, name);
122
123 // Détection d'un nouvel appareil venant de se connecter
124 if (mac != m_lastActiveMac) {
125 newcomerMac = mac;
126 newcomerName = name;
127 }
128 }
129
130 // Trust automatique : Dès qu'un appareil est vu, on lui fait confiance au niveau système
131 // pour faciliter les reconnexions futures sans demande PIN.
132 if (!m_knownMacs.contains(mac)) {
133 m_knownMacs.insert(mac);
134 QProcess::execute("bluetoothctl", QStringList() << "trust" << mac);
135
136 // Si on était en mode "Visible" et qu'un appareil s'est appairé, on cache le véhicule
137 if (ui->btnVisible->isChecked()) setDiscoverable(false);
138 }
139
140 // Construction de l'élément visuel pour la liste
141 QString label = name + " (" + mac + ")";
142 if (isConnected) label += " (connecté)";
143
144 QListWidgetItem *item = new QListWidgetItem("📱 " + label, ui->listDevices);
145 item->setData(Qt::UserRole, mac); // Stockage de l'adresse MAC de manière invisible
146
147 // Mise en valeur visuelle (Vert) si l'appareil est connecté
148 if (isConnected) {
149 item->setForeground(Qt::green);
150 }
151
152 // Restauration de la sélection
153 if (mac == selectedMac) {
154 ui->listDevices->setCurrentItem(item);
155 }
156 found = true;
157 }
158 }
159
160 // --- RÈGLE MÉTIER : EXCLUSIVITÉ BLUETOOTH ---
161 // Un seul appareil audio doit être actif à la fois pour éviter les conflits de flux A2DP/HFP
162 // et les plantages du démon Bluetooth (BlueZ).
163 if (connectedDevices.size() > 1 && !newcomerMac.isEmpty()) {
164 QString oldDeviceName;
165
166 QMapIterator<QString, QString> i(connectedDevices);
167 while (i.hasNext()) {
168 i.next();
169 // On déconnecte de force tous les autres appareils sauf le petit nouveau
170 if (i.key() != newcomerMac) {
171 oldDeviceName = i.value();
172 QProcess::execute("bluetoothctl", QStringList() << "disconnect" << i.key());
173 qDebug() << "Exclusivité : Déconnexion de" << oldDeviceName;
174 }
175 }
176
177 // Information de l'utilisateur sur cette déconnexion automatique
178 showAutoClosingMessage("Changement d'appareil",
179 QString("Priorité à '%1'.\n'%2' a été déconnecté.")
180 .arg(newcomerName).arg(oldDeviceName),
181 3000);
182
183 m_lastActiveMac = newcomerMac;
184 }
185 else if (connectedDevices.size() == 1) {
186 m_lastActiveMac = connectedDevices.keys().first();
187 }
188 else if (connectedDevices.isEmpty()) {
189 m_lastActiveMac = "";
190 }
191
192 // Affichage par défaut si la liste est vide
193 if (!found) {
194 new QListWidgetItem("(Aucun appareil enregistré)", ui->listDevices);
195 ui->btnForget->setEnabled(false);
196 }
197}
198
199void SettingsPage::onVisibleClicked()
200{
201 setDiscoverable(ui->btnVisible->isChecked());
202}
203
204void SettingsPage::setDiscoverable(bool enable)
205{
206 if (enable) {
207 QProcess::execute("bluetoothctl", QStringList() << "discoverable" << "on");
208 ui->btnVisible->setText("Visible (120s max)...");
209 ui->btnVisible->setChecked(true);
210
211 // Fenêtre courte pour limiter l'exposition Bluetooth permanente du véhicule.
212 m_discoveryTimer->start();
213 } else {
214 QProcess::execute("bluetoothctl", QStringList() << "discoverable" << "off");
215 ui->btnVisible->setText("Rendre Visible (Appairage)");
216 ui->btnVisible->setChecked(false);
217 m_discoveryTimer->stop();
218 }
219}
220
221void SettingsPage::stopDiscovery()
222{
223 setDiscoverable(false);
224}
225
226void SettingsPage::onForgetClicked()
227{
228 QListWidgetItem *item = ui->listDevices->currentItem();
229 if (!item) return;
230
231 QString mac = item->data(Qt::UserRole).toString();
232 QString name = item->text().remove("📱 ").remove(" (connecté)"); // Nettoyage du texte affiché
233 if (mac.isEmpty()) return;
234
235 // Demande de confirmation à l'utilisateur
236 QMessageBox::StandardButton reply;
237 reply = QMessageBox::question(this, "Supprimer l'appareil",
238 QString("Voulez-vous vraiment oublier l'appareil %1 ?").arg(name),
239 QMessageBox::Yes | QMessageBox::No);
240
241 if (reply == QMessageBox::No) return;
242
243 // Suppression définitive au niveau du système d'exploitation
244 QProcess::execute("bluetoothctl", QStringList() << "remove" << mac);
245
246 m_knownMacs.remove(mac);
247 if(m_lastActiveMac == mac) m_lastActiveMac = "";
248
249 // Vidage manuel de la liste pour forcer un rafraîchissement visuel immédiat
250 ui->listDevices->clear();
251 ui->btnForget->setEnabled(false);
252
253 refreshPairedList();
254}
255
256void SettingsPage::errorOccurred(QBluetoothLocalDevice::Error error)
257{
258 qDebug() << "Erreur Bluetooth LocalDevice:" << error;
259}
Interface graphique de gestion des paramètres et des connexions sans fil. Permet de rendre le véhicul...
~SettingsPage()
Destructeur. Libère l'interface générée par Qt Designer.
SettingsPage(QWidget *parent=nullptr)
Constructeur de la page des paramètres.
Rôle architectural : Page de configuration système et Bluetooth utilisateur.
Rôle architectural : Modèle central de télémétrie partagé entre les modules C++ et l'interface QML.