| 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 | |