1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtNfc module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:BSD$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** BSD License Usage |
18 | ** Alternatively, you may use this file under the terms of the BSD license |
19 | ** as follows: |
20 | ** |
21 | ** "Redistribution and use in source and binary forms, with or without |
22 | ** modification, are permitted provided that the following conditions are |
23 | ** met: |
24 | ** * Redistributions of source code must retain the above copyright |
25 | ** notice, this list of conditions and the following disclaimer. |
26 | ** * Redistributions in binary form must reproduce the above copyright |
27 | ** notice, this list of conditions and the following disclaimer in |
28 | ** the documentation and/or other materials provided with the |
29 | ** distribution. |
30 | ** * Neither the name of The Qt Company Ltd nor the names of its |
31 | ** contributors may be used to endorse or promote products derived |
32 | ** from this software without specific prior written permission. |
33 | ** |
34 | ** |
35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
46 | ** |
47 | ** $QT_END_LICENSE$ |
48 | ** |
49 | ****************************************************************************/ |
50 | |
51 | #include "mainwindow.h" |
52 | #include "ui_mainwindow.h" |
53 | |
54 | #include "textrecordeditor.h" |
55 | #include "urirecordeditor.h" |
56 | #include "mimeimagerecordeditor.h" |
57 | |
58 | #include <QtNfc/qndefnfcurirecord.h> |
59 | #include <QtNfc/qndefnfctextrecord.h> |
60 | #include <QtNfc/qndefrecord.h> |
61 | #include <QtNfc/qndefmessage.h> |
62 | #include <QtNfc/qnearfieldmanager.h> |
63 | #include <QtNfc/qnearfieldtarget.h> |
64 | |
65 | #include <QtWidgets/QMenu> |
66 | #include <QtWidgets/QVBoxLayout> |
67 | #include <QtWidgets/QFrame> |
68 | #include <QtWidgets/QLabel> |
69 | #include <QtWidgets/QFileDialog> |
70 | |
71 | class EmptyRecordLabel : public QLabel |
72 | { |
73 | Q_OBJECT |
74 | |
75 | public: |
76 | EmptyRecordLabel() : QLabel(tr(s: "Empty Record" )) { } |
77 | ~EmptyRecordLabel() { } |
78 | |
79 | void setRecord(const QNdefRecord &record) |
80 | { |
81 | Q_UNUSED(record); |
82 | } |
83 | |
84 | QNdefRecord record() const |
85 | { |
86 | return QNdefRecord(); |
87 | } |
88 | }; |
89 | |
90 | class UnknownRecordLabel : public QLabel |
91 | { |
92 | Q_OBJECT |
93 | |
94 | public: |
95 | UnknownRecordLabel() : QLabel(tr(s: "Unknown Record Type" )) { } |
96 | ~UnknownRecordLabel() { } |
97 | |
98 | void setRecord(const QNdefRecord &record) { m_record = record; } |
99 | QNdefRecord record() const { return m_record; } |
100 | |
101 | private: |
102 | QNdefRecord m_record; |
103 | }; |
104 | |
105 | template <typename T> |
106 | void addRecord(Ui::MainWindow *ui, const QNdefRecord &record = QNdefRecord()) |
107 | { |
108 | QVBoxLayout *vbox = qobject_cast<QVBoxLayout *>(object: ui->scrollAreaWidgetContents->layout()); |
109 | if (!vbox) |
110 | return; |
111 | |
112 | if (!vbox->isEmpty()) { |
113 | QFrame *hline = new QFrame; |
114 | hline->setFrameShape(QFrame::HLine); |
115 | hline->setObjectName(QStringLiteral("line-spacer" )); |
116 | |
117 | vbox->addWidget(hline); |
118 | } |
119 | |
120 | T *recordEditor = new T; |
121 | recordEditor->setObjectName(QStringLiteral("record-editor" )); |
122 | |
123 | if (!record.isEmpty()) |
124 | recordEditor->setRecord(record); |
125 | |
126 | vbox->addWidget(recordEditor); |
127 | } |
128 | |
129 | MainWindow::MainWindow(QWidget *parent) |
130 | : QMainWindow(parent), ui(new Ui::MainWindow), m_touchAction(NoAction) |
131 | { |
132 | ui->setupUi(this); |
133 | |
134 | QMenu * = new QMenu(this); |
135 | addRecordMenu->addAction(text: tr(s: "NFC Text Record" ), receiver: this, SLOT(addNfcTextRecord())); |
136 | addRecordMenu->addAction(text: tr(s: "NFC URI Record" ), receiver: this, SLOT(addNfcUriRecord())); |
137 | addRecordMenu->addAction(text: tr(s: "MIME Image Record" ), receiver: this, SLOT(addMimeImageRecord())); |
138 | addRecordMenu->addAction(text: tr(s: "Empty Record" ), receiver: this, SLOT(addEmptyRecord())); |
139 | ui->addRecord->setMenu(addRecordMenu); |
140 | |
141 | QVBoxLayout *vbox = new QVBoxLayout; |
142 | ui->scrollAreaWidgetContents->setLayout(vbox); |
143 | |
144 | //! [QNearFieldManager init] |
145 | m_manager = new QNearFieldManager(this); |
146 | connect(sender: m_manager, signal: &QNearFieldManager::targetDetected, |
147 | receiver: this, slot: &MainWindow::targetDetected); |
148 | connect(sender: m_manager, signal: &QNearFieldManager::targetLost, |
149 | receiver: this, slot: &MainWindow::targetLost); |
150 | //! [QNearFieldManager init] |
151 | } |
152 | |
153 | MainWindow::~MainWindow() |
154 | { |
155 | delete ui; |
156 | } |
157 | |
158 | void MainWindow::addNfcTextRecord() |
159 | { |
160 | addRecord<TextRecordEditor>(ui); |
161 | } |
162 | |
163 | void MainWindow::addNfcUriRecord() |
164 | { |
165 | addRecord<UriRecordEditor>(ui); |
166 | } |
167 | |
168 | void MainWindow::addMimeImageRecord() |
169 | { |
170 | addRecord<MimeImageRecordEditor>(ui); |
171 | } |
172 | |
173 | void MainWindow::addEmptyRecord() |
174 | { |
175 | addRecord<EmptyRecordLabel>(ui); |
176 | } |
177 | |
178 | void MainWindow::loadMessage() |
179 | { |
180 | QString filename = QFileDialog::getOpenFileName(parent: this, caption: tr(s: "Select NDEF Message" )); |
181 | if (filename.isEmpty()) |
182 | return; |
183 | |
184 | QFile file(filename); |
185 | if (!file.open(flags: QIODevice::ReadOnly)) |
186 | return; |
187 | |
188 | QByteArray ndef = file.readAll(); |
189 | |
190 | ndefMessageRead(message: QNdefMessage::fromByteArray(message: ndef)); |
191 | |
192 | file.close(); |
193 | } |
194 | |
195 | void MainWindow::saveMessage() |
196 | { |
197 | QString filename = QFileDialog::getSaveFileName(parent: this, caption: tr(s: "Select NDEF Message" )); |
198 | if (filename.isEmpty()) |
199 | return; |
200 | |
201 | QFile file(filename); |
202 | if (!file.open(flags: QIODevice::WriteOnly)) |
203 | return; |
204 | |
205 | file.write(data: ndefMessage().toByteArray()); |
206 | |
207 | file.close(); |
208 | } |
209 | |
210 | void MainWindow::touchReceive() |
211 | { |
212 | ui->status->setStyleSheet(QStringLiteral("background: blue" )); |
213 | |
214 | m_touchAction = ReadNdef; |
215 | |
216 | m_manager->setTargetAccessModes(QNearFieldManager::NdefReadTargetAccess); |
217 | //! [QNearFieldManager start detection] |
218 | m_manager->startTargetDetection(); |
219 | //! [QNearFieldManager start detection] |
220 | } |
221 | |
222 | void MainWindow::touchStore() |
223 | { |
224 | ui->status->setStyleSheet(QStringLiteral("background: yellow" )); |
225 | |
226 | m_touchAction = WriteNdef; |
227 | |
228 | m_manager->setTargetAccessModes(QNearFieldManager::NdefWriteTargetAccess); |
229 | m_manager->startTargetDetection(); |
230 | } |
231 | |
232 | //! [QNearFieldTarget detected] |
233 | void MainWindow::targetDetected(QNearFieldTarget *target) |
234 | { |
235 | switch (m_touchAction) { |
236 | case NoAction: |
237 | break; |
238 | case ReadNdef: |
239 | connect(sender: target, signal: &QNearFieldTarget::ndefMessageRead, receiver: this, slot: &MainWindow::ndefMessageRead); |
240 | connect(sender: target, signal: &QNearFieldTarget::error, receiver: this, slot: &MainWindow::targetError); |
241 | |
242 | m_request = target->readNdefMessages(); |
243 | if (!m_request.isValid()) // cannot read messages |
244 | targetError(error: QNearFieldTarget::NdefReadError, id: m_request); |
245 | break; |
246 | case WriteNdef: |
247 | connect(sender: target, signal: &QNearFieldTarget::ndefMessagesWritten, receiver: this, slot: &MainWindow::ndefMessageWritten); |
248 | connect(sender: target, signal: &QNearFieldTarget::error, receiver: this, slot: &MainWindow::targetError); |
249 | |
250 | m_request = target->writeNdefMessages(messages: QList<QNdefMessage>() << ndefMessage()); |
251 | if (!m_request.isValid()) // cannot write messages |
252 | targetError(error: QNearFieldTarget::NdefWriteError, id: m_request); |
253 | break; |
254 | } |
255 | } |
256 | //! [QNearFieldTarget detected] |
257 | |
258 | //! [QNearFieldTarget lost] |
259 | void MainWindow::targetLost(QNearFieldTarget *target) |
260 | { |
261 | target->deleteLater(); |
262 | } |
263 | //! [QNearFieldTarget lost] |
264 | |
265 | void MainWindow::ndefMessageRead(const QNdefMessage &message) |
266 | { |
267 | clearMessage(); |
268 | |
269 | for (const QNdefRecord &record : message) { |
270 | if (record.isRecordType<QNdefNfcTextRecord>()) { |
271 | addRecord<TextRecordEditor>(ui, record); |
272 | } else if (record.isRecordType<QNdefNfcUriRecord>()) { |
273 | addRecord<UriRecordEditor>(ui, record); |
274 | } else if (record.typeNameFormat() == QNdefRecord::Mime && |
275 | record.type().startsWith(c: "image/" )) { |
276 | addRecord<MimeImageRecordEditor>(ui, record); |
277 | } else if (record.isEmpty()) { |
278 | addRecord<EmptyRecordLabel>(ui); |
279 | } else { |
280 | addRecord<UnknownRecordLabel>(ui, record); |
281 | } |
282 | } |
283 | |
284 | ui->status->setStyleSheet(QString()); |
285 | m_manager->setTargetAccessModes(QNearFieldManager::NoTargetAccess); |
286 | //! [QNearFieldManager stop detection] |
287 | m_manager->stopTargetDetection(); |
288 | //! [QNearFieldManager stop detection] |
289 | m_request = QNearFieldTarget::RequestId(); |
290 | ui->statusBar->clearMessage(); |
291 | } |
292 | |
293 | void MainWindow::ndefMessageWritten() |
294 | { |
295 | ui->status->setStyleSheet(QString()); |
296 | m_manager->setTargetAccessModes(QNearFieldManager::NoTargetAccess); |
297 | m_manager->stopTargetDetection(); |
298 | m_request = QNearFieldTarget::RequestId(); |
299 | ui->statusBar->clearMessage(); |
300 | } |
301 | |
302 | void MainWindow::targetError(QNearFieldTarget::Error error, const QNearFieldTarget::RequestId &id) |
303 | { |
304 | Q_UNUSED(error); |
305 | Q_UNUSED(id); |
306 | |
307 | if (m_request == id) { |
308 | switch (error) { |
309 | case QNearFieldTarget::NoError: |
310 | ui->statusBar->clearMessage(); |
311 | break; |
312 | case QNearFieldTarget::UnsupportedError: |
313 | ui->statusBar->showMessage(text: tr(s: "Unsupported tag" )); |
314 | break; |
315 | case QNearFieldTarget::TargetOutOfRangeError: |
316 | ui->statusBar->showMessage(text: tr(s: "Tag removed from field" )); |
317 | break; |
318 | case QNearFieldTarget::NoResponseError: |
319 | ui->statusBar->showMessage(text: tr(s: "No response from tag" )); |
320 | break; |
321 | case QNearFieldTarget::ChecksumMismatchError: |
322 | ui->statusBar->showMessage(text: tr(s: "Checksum mismatch" )); |
323 | break; |
324 | case QNearFieldTarget::InvalidParametersError: |
325 | ui->statusBar->showMessage(text: tr(s: "Invalid parameters" )); |
326 | break; |
327 | case QNearFieldTarget::NdefReadError: |
328 | ui->statusBar->showMessage(text: tr(s: "NDEF read error" )); |
329 | break; |
330 | case QNearFieldTarget::NdefWriteError: |
331 | ui->statusBar->showMessage(text: tr(s: "NDEF write error" )); |
332 | break; |
333 | default: |
334 | ui->statusBar->showMessage(text: tr(s: "Unknown error" )); |
335 | } |
336 | |
337 | ui->status->setStyleSheet(QString()); |
338 | m_manager->setTargetAccessModes(QNearFieldManager::NoTargetAccess); |
339 | m_manager->stopTargetDetection(); |
340 | m_request = QNearFieldTarget::RequestId(); |
341 | } |
342 | } |
343 | |
344 | void MainWindow::clearMessage() |
345 | { |
346 | QWidget *scrollArea = ui->scrollAreaWidgetContents; |
347 | |
348 | qDeleteAll(c: scrollArea->findChildren<QWidget *>(QStringLiteral("line-spacer" ))); |
349 | qDeleteAll(c: scrollArea->findChildren<QWidget *>(QStringLiteral("record-editor" ))); |
350 | } |
351 | |
352 | QNdefMessage MainWindow::ndefMessage() const |
353 | { |
354 | QVBoxLayout *vbox = qobject_cast<QVBoxLayout *>(object: ui->scrollAreaWidgetContents->layout()); |
355 | if (!vbox) |
356 | return QNdefMessage(); |
357 | |
358 | QNdefMessage message; |
359 | |
360 | for (int i = 0; i < vbox->count(); ++i) { |
361 | QWidget *widget = vbox->itemAt(i)->widget(); |
362 | |
363 | if (TextRecordEditor *editor = qobject_cast<TextRecordEditor *>(object: widget)) { |
364 | message.append(t: editor->record()); |
365 | } else if (UriRecordEditor *editor = qobject_cast<UriRecordEditor *>(object: widget)) { |
366 | message.append(t: editor->record()); |
367 | } else if (MimeImageRecordEditor *editor = qobject_cast<MimeImageRecordEditor *>(object: widget)) { |
368 | message.append(t: editor->record()); |
369 | } else if (qobject_cast<EmptyRecordLabel *>(object: widget)) { |
370 | message.append(t: QNdefRecord()); |
371 | } else if (UnknownRecordLabel *label = qobject_cast<UnknownRecordLabel *>(object: widget)) { |
372 | message.append(t: label->record()); |
373 | } |
374 | } |
375 | |
376 | return message; |
377 | } |
378 | |
379 | #include "mainwindow.moc" |
380 | |