2022-06-08 11:30:04 +00:00
|
|
|
// Copyright (C) 2021 The Qt Company Ltd.
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
2011-12-09 14:27:12 +00:00
|
|
|
|
|
|
|
#include "mainwindow.h"
|
|
|
|
#include "ui_mainwindow.h"
|
|
|
|
|
|
|
|
#include "textrecordeditor.h"
|
|
|
|
#include "urirecordeditor.h"
|
|
|
|
#include "mimeimagerecordeditor.h"
|
|
|
|
|
2017-12-21 12:48:28 +00:00
|
|
|
#include <QtNfc/qndefnfcurirecord.h>
|
|
|
|
#include <QtNfc/qndefnfctextrecord.h>
|
|
|
|
#include <QtNfc/qndefrecord.h>
|
|
|
|
#include <QtNfc/qndefmessage.h>
|
|
|
|
#include <QtNfc/qnearfieldmanager.h>
|
|
|
|
#include <QtNfc/qnearfieldtarget.h>
|
|
|
|
|
|
|
|
#include <QtWidgets/QMenu>
|
|
|
|
#include <QtWidgets/QVBoxLayout>
|
|
|
|
#include <QtWidgets/QFrame>
|
|
|
|
#include <QtWidgets/QLabel>
|
|
|
|
#include <QtWidgets/QFileDialog>
|
2021-06-10 15:14:08 +00:00
|
|
|
#include <QtWidgets/QScroller>
|
|
|
|
#include <QtWidgets/QApplication>
|
|
|
|
#include <QtGui/QScreen>
|
2011-12-09 14:27:12 +00:00
|
|
|
|
|
|
|
class EmptyRecordLabel : public QLabel
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
public:
|
|
|
|
EmptyRecordLabel() : QLabel(tr("Empty Record")) { }
|
|
|
|
~EmptyRecordLabel() { }
|
|
|
|
|
|
|
|
void setRecord(const QNdefRecord &record)
|
|
|
|
{
|
|
|
|
Q_UNUSED(record);
|
|
|
|
}
|
|
|
|
|
|
|
|
QNdefRecord record() const
|
|
|
|
{
|
|
|
|
return QNdefRecord();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class UnknownRecordLabel : public QLabel
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
public:
|
|
|
|
UnknownRecordLabel() : QLabel(tr("Unknown Record Type")) { }
|
|
|
|
~UnknownRecordLabel() { }
|
|
|
|
|
|
|
|
void setRecord(const QNdefRecord &record) { m_record = record; }
|
|
|
|
QNdefRecord record() const { return m_record; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
QNdefRecord m_record;
|
|
|
|
};
|
|
|
|
|
2021-06-10 15:14:08 +00:00
|
|
|
template<typename T>
|
2011-12-09 14:27:12 +00:00
|
|
|
void addRecord(Ui::MainWindow *ui, const QNdefRecord &record = QNdefRecord())
|
|
|
|
{
|
|
|
|
QVBoxLayout *vbox = qobject_cast<QVBoxLayout *>(ui->scrollAreaWidgetContents->layout());
|
|
|
|
if (!vbox)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!vbox->isEmpty()) {
|
|
|
|
QFrame *hline = new QFrame;
|
|
|
|
hline->setFrameShape(QFrame::HLine);
|
2014-05-14 12:10:33 +00:00
|
|
|
hline->setObjectName(QStringLiteral("line-spacer"));
|
2011-12-09 14:27:12 +00:00
|
|
|
|
|
|
|
vbox->addWidget(hline);
|
|
|
|
}
|
|
|
|
|
|
|
|
T *recordEditor = new T;
|
2014-05-14 12:10:33 +00:00
|
|
|
recordEditor->setObjectName(QStringLiteral("record-editor"));
|
2011-12-09 14:27:12 +00:00
|
|
|
|
2021-06-10 15:14:08 +00:00
|
|
|
vbox->addWidget(recordEditor);
|
|
|
|
|
2011-12-09 14:27:12 +00:00
|
|
|
if (!record.isEmpty())
|
|
|
|
recordEditor->setRecord(record);
|
|
|
|
}
|
|
|
|
|
|
|
|
MainWindow::MainWindow(QWidget *parent)
|
2014-03-10 13:52:00 +00:00
|
|
|
: QMainWindow(parent), ui(new Ui::MainWindow), m_touchAction(NoAction)
|
2011-12-09 14:27:12 +00:00
|
|
|
{
|
|
|
|
ui->setupUi(this);
|
|
|
|
|
2022-06-01 14:24:52 +00:00
|
|
|
connect(ui->addRecord, &QPushButton::clicked, this, &MainWindow::showMenu);
|
2011-12-09 14:27:12 +00:00
|
|
|
|
|
|
|
QVBoxLayout *vbox = new QVBoxLayout;
|
|
|
|
ui->scrollAreaWidgetContents->setLayout(vbox);
|
2021-06-10 15:14:08 +00:00
|
|
|
#if (defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)) || defined(Q_OS_IOS)
|
|
|
|
QScroller::grabGesture(ui->scrollArea, QScroller::TouchGesture);
|
|
|
|
ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
|
|
ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Detect keyboard show/hide. We can't directly update the UI to ensure
|
|
|
|
// that the focused widget is active. Instead we wait for a resizeEvent
|
|
|
|
// that happens shortly after the keyboard is shown, and do all the
|
|
|
|
// processing there.
|
|
|
|
QInputMethod *inputMethod = qApp->inputMethod();
|
|
|
|
connect(inputMethod, &QInputMethod::visibleChanged,
|
|
|
|
[this, inputMethod]() { m_keyboardVisible = inputMethod->isVisible(); });
|
2011-12-09 14:27:12 +00:00
|
|
|
|
2014-03-10 13:52:00 +00:00
|
|
|
//! [QNearFieldManager init]
|
|
|
|
m_manager = new QNearFieldManager(this);
|
2018-02-01 12:27:28 +00:00
|
|
|
connect(m_manager, &QNearFieldManager::targetDetected,
|
|
|
|
this, &MainWindow::targetDetected);
|
|
|
|
connect(m_manager, &QNearFieldManager::targetLost,
|
|
|
|
this, &MainWindow::targetLost);
|
2014-03-10 13:52:00 +00:00
|
|
|
//! [QNearFieldManager init]
|
2011-12-09 14:27:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
MainWindow::~MainWindow()
|
|
|
|
{
|
|
|
|
delete ui;
|
|
|
|
}
|
|
|
|
|
2021-06-10 15:14:08 +00:00
|
|
|
void MainWindow::resizeEvent(QResizeEvent *e)
|
|
|
|
{
|
|
|
|
QMainWindow::resizeEvent(e);
|
|
|
|
if (m_keyboardVisible) {
|
|
|
|
QWidget *areaWidget = ui->scrollAreaWidgetContents;
|
|
|
|
QList<QWidget *> childWidgets = areaWidget->findChildren<QWidget *>();
|
|
|
|
for (const auto widget : childWidgets) {
|
|
|
|
if (widget->hasFocus()) {
|
|
|
|
ui->scrollArea->ensureWidgetVisible(widget);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-09 14:27:12 +00:00
|
|
|
void MainWindow::addNfcTextRecord()
|
|
|
|
{
|
|
|
|
addRecord<TextRecordEditor>(ui);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::addNfcUriRecord()
|
|
|
|
{
|
|
|
|
addRecord<UriRecordEditor>(ui);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::addMimeImageRecord()
|
|
|
|
{
|
|
|
|
addRecord<MimeImageRecordEditor>(ui);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::addEmptyRecord()
|
|
|
|
{
|
|
|
|
addRecord<EmptyRecordLabel>(ui);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::loadMessage()
|
|
|
|
{
|
|
|
|
QString filename = QFileDialog::getOpenFileName(this, tr("Select NDEF Message"));
|
|
|
|
if (filename.isEmpty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
QFile file(filename);
|
|
|
|
if (!file.open(QIODevice::ReadOnly))
|
|
|
|
return;
|
|
|
|
|
|
|
|
QByteArray ndef = file.readAll();
|
|
|
|
|
|
|
|
ndefMessageRead(QNdefMessage::fromByteArray(ndef));
|
|
|
|
|
|
|
|
file.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::saveMessage()
|
|
|
|
{
|
|
|
|
QString filename = QFileDialog::getSaveFileName(this, tr("Select NDEF Message"));
|
|
|
|
if (filename.isEmpty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
QFile file(filename);
|
|
|
|
if (!file.open(QIODevice::WriteOnly))
|
|
|
|
return;
|
|
|
|
|
|
|
|
file.write(ndefMessage().toByteArray());
|
|
|
|
|
|
|
|
file.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::touchReceive()
|
|
|
|
{
|
2014-05-14 12:10:33 +00:00
|
|
|
ui->status->setStyleSheet(QStringLiteral("background: blue"));
|
2011-12-09 14:27:12 +00:00
|
|
|
|
|
|
|
m_touchAction = ReadNdef;
|
|
|
|
|
2014-03-10 13:52:00 +00:00
|
|
|
//! [QNearFieldManager start detection]
|
2020-07-31 04:39:29 +00:00
|
|
|
m_manager->startTargetDetection(QNearFieldTarget::NdefAccess);
|
2014-03-10 13:52:00 +00:00
|
|
|
//! [QNearFieldManager start detection]
|
2011-12-09 14:27:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::touchStore()
|
|
|
|
{
|
2014-05-14 12:10:33 +00:00
|
|
|
ui->status->setStyleSheet(QStringLiteral("background: yellow"));
|
2011-12-09 14:27:12 +00:00
|
|
|
|
|
|
|
m_touchAction = WriteNdef;
|
|
|
|
|
2020-07-31 04:39:29 +00:00
|
|
|
m_manager->startTargetDetection(QNearFieldTarget::NdefAccess);
|
2011-12-09 14:27:12 +00:00
|
|
|
}
|
|
|
|
|
2014-03-10 13:52:00 +00:00
|
|
|
//! [QNearFieldTarget detected]
|
2011-12-09 14:27:12 +00:00
|
|
|
void MainWindow::targetDetected(QNearFieldTarget *target)
|
|
|
|
{
|
|
|
|
switch (m_touchAction) {
|
|
|
|
case NoAction:
|
|
|
|
break;
|
|
|
|
case ReadNdef:
|
2017-12-21 12:48:28 +00:00
|
|
|
connect(target, &QNearFieldTarget::ndefMessageRead, this, &MainWindow::ndefMessageRead);
|
|
|
|
connect(target, &QNearFieldTarget::error, this, &MainWindow::targetError);
|
2011-12-09 14:27:12 +00:00
|
|
|
|
|
|
|
m_request = target->readNdefMessages();
|
2015-01-13 09:00:26 +00:00
|
|
|
if (!m_request.isValid()) // cannot read messages
|
|
|
|
targetError(QNearFieldTarget::NdefReadError, m_request);
|
2011-12-09 14:27:12 +00:00
|
|
|
break;
|
|
|
|
case WriteNdef:
|
2021-05-25 15:13:34 +00:00
|
|
|
connect(target, &QNearFieldTarget::requestCompleted, this, &MainWindow::ndefMessageWritten);
|
2017-12-21 12:48:28 +00:00
|
|
|
connect(target, &QNearFieldTarget::error, this, &MainWindow::targetError);
|
2011-12-09 14:27:12 +00:00
|
|
|
|
|
|
|
m_request = target->writeNdefMessages(QList<QNdefMessage>() << ndefMessage());
|
2015-01-13 09:00:26 +00:00
|
|
|
if (!m_request.isValid()) // cannot write messages
|
|
|
|
targetError(QNearFieldTarget::NdefWriteError, m_request);
|
2011-12-09 14:27:12 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-03-10 13:52:00 +00:00
|
|
|
//! [QNearFieldTarget detected]
|
2011-12-09 14:27:12 +00:00
|
|
|
|
2014-03-10 13:52:00 +00:00
|
|
|
//! [QNearFieldTarget lost]
|
2011-12-09 14:27:12 +00:00
|
|
|
void MainWindow::targetLost(QNearFieldTarget *target)
|
|
|
|
{
|
|
|
|
target->deleteLater();
|
|
|
|
}
|
2014-03-10 13:52:00 +00:00
|
|
|
//! [QNearFieldTarget lost]
|
2011-12-09 14:27:12 +00:00
|
|
|
|
|
|
|
void MainWindow::ndefMessageRead(const QNdefMessage &message)
|
|
|
|
{
|
|
|
|
clearMessage();
|
|
|
|
|
2018-08-14 09:08:18 +00:00
|
|
|
for (const QNdefRecord &record : message) {
|
2011-12-09 14:27:12 +00:00
|
|
|
if (record.isRecordType<QNdefNfcTextRecord>()) {
|
|
|
|
addRecord<TextRecordEditor>(ui, record);
|
|
|
|
} else if (record.isRecordType<QNdefNfcUriRecord>()) {
|
|
|
|
addRecord<UriRecordEditor>(ui, record);
|
|
|
|
} else if (record.typeNameFormat() == QNdefRecord::Mime &&
|
|
|
|
record.type().startsWith("image/")) {
|
|
|
|
addRecord<MimeImageRecordEditor>(ui, record);
|
|
|
|
} else if (record.isEmpty()) {
|
|
|
|
addRecord<EmptyRecordLabel>(ui);
|
|
|
|
} else {
|
|
|
|
addRecord<UnknownRecordLabel>(ui, record);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ui->status->setStyleSheet(QString());
|
2014-03-10 13:52:00 +00:00
|
|
|
//! [QNearFieldManager stop detection]
|
2011-12-09 14:27:12 +00:00
|
|
|
m_manager->stopTargetDetection();
|
2014-03-10 13:52:00 +00:00
|
|
|
//! [QNearFieldManager stop detection]
|
2011-12-09 14:27:12 +00:00
|
|
|
m_request = QNearFieldTarget::RequestId();
|
|
|
|
ui->statusBar->clearMessage();
|
|
|
|
}
|
|
|
|
|
2021-05-25 15:13:34 +00:00
|
|
|
void MainWindow::ndefMessageWritten(const QNearFieldTarget::RequestId &id)
|
2011-12-09 14:27:12 +00:00
|
|
|
{
|
2021-05-25 15:13:34 +00:00
|
|
|
if (id == m_request) {
|
|
|
|
ui->status->setStyleSheet(QString());
|
|
|
|
m_manager->stopTargetDetection();
|
|
|
|
m_request = QNearFieldTarget::RequestId();
|
|
|
|
ui->statusBar->clearMessage();
|
|
|
|
}
|
2011-12-09 14:27:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainWindow::targetError(QNearFieldTarget::Error error, const QNearFieldTarget::RequestId &id)
|
|
|
|
{
|
|
|
|
Q_UNUSED(error);
|
|
|
|
Q_UNUSED(id);
|
|
|
|
|
|
|
|
if (m_request == id) {
|
|
|
|
switch (error) {
|
|
|
|
case QNearFieldTarget::NoError:
|
|
|
|
ui->statusBar->clearMessage();
|
|
|
|
break;
|
|
|
|
case QNearFieldTarget::UnsupportedError:
|
|
|
|
ui->statusBar->showMessage(tr("Unsupported tag"));
|
|
|
|
break;
|
|
|
|
case QNearFieldTarget::TargetOutOfRangeError:
|
|
|
|
ui->statusBar->showMessage(tr("Tag removed from field"));
|
|
|
|
break;
|
|
|
|
case QNearFieldTarget::NoResponseError:
|
|
|
|
ui->statusBar->showMessage(tr("No response from tag"));
|
|
|
|
break;
|
|
|
|
case QNearFieldTarget::ChecksumMismatchError:
|
|
|
|
ui->statusBar->showMessage(tr("Checksum mismatch"));
|
|
|
|
break;
|
|
|
|
case QNearFieldTarget::InvalidParametersError:
|
|
|
|
ui->statusBar->showMessage(tr("Invalid parameters"));
|
|
|
|
break;
|
|
|
|
case QNearFieldTarget::NdefReadError:
|
|
|
|
ui->statusBar->showMessage(tr("NDEF read error"));
|
|
|
|
break;
|
|
|
|
case QNearFieldTarget::NdefWriteError:
|
|
|
|
ui->statusBar->showMessage(tr("NDEF write error"));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ui->statusBar->showMessage(tr("Unknown error"));
|
|
|
|
}
|
|
|
|
|
|
|
|
ui->status->setStyleSheet(QString());
|
|
|
|
m_manager->stopTargetDetection();
|
|
|
|
m_request = QNearFieldTarget::RequestId();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-01 14:24:52 +00:00
|
|
|
void MainWindow::showMenu()
|
|
|
|
{
|
|
|
|
// We have to manually call QMenu::popup() because of QTBUG-98651.
|
|
|
|
// And we need to re-create menu each time because of QTBUG-97482.
|
|
|
|
if (m_menu) {
|
|
|
|
m_menu->setParent(nullptr);
|
|
|
|
delete m_menu;
|
|
|
|
}
|
|
|
|
m_menu = new QMenu(this);
|
|
|
|
m_menu->addAction(tr("NFC Text Record"), this, &MainWindow::addNfcTextRecord);
|
|
|
|
m_menu->addAction(tr("NFC URI Record"), this, &MainWindow::addNfcUriRecord);
|
|
|
|
m_menu->addAction(tr("MIME Image Record"), this, &MainWindow::addMimeImageRecord);
|
|
|
|
m_menu->addAction(tr("Empty Record"), this, &MainWindow::addEmptyRecord);
|
|
|
|
|
|
|
|
// Use menu's sizeHint() to position it so that its right side is aligned
|
|
|
|
// with button's right side.
|
|
|
|
QPushButton *button = ui->addRecord;
|
|
|
|
const int x = button->x() + button->width() - m_menu->sizeHint().width();
|
|
|
|
const int y = button->y() + button->height();
|
|
|
|
m_menu->popup(mapToGlobal(QPoint(x, y)));
|
|
|
|
}
|
|
|
|
|
2011-12-09 14:27:12 +00:00
|
|
|
void MainWindow::clearMessage()
|
|
|
|
{
|
|
|
|
QWidget *scrollArea = ui->scrollAreaWidgetContents;
|
|
|
|
|
2014-05-14 12:10:33 +00:00
|
|
|
qDeleteAll(scrollArea->findChildren<QWidget *>(QStringLiteral("line-spacer")));
|
|
|
|
qDeleteAll(scrollArea->findChildren<QWidget *>(QStringLiteral("record-editor")));
|
2011-12-09 14:27:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QNdefMessage MainWindow::ndefMessage() const
|
|
|
|
{
|
|
|
|
QVBoxLayout *vbox = qobject_cast<QVBoxLayout *>(ui->scrollAreaWidgetContents->layout());
|
|
|
|
if (!vbox)
|
|
|
|
return QNdefMessage();
|
|
|
|
|
|
|
|
QNdefMessage message;
|
|
|
|
|
|
|
|
for (int i = 0; i < vbox->count(); ++i) {
|
|
|
|
QWidget *widget = vbox->itemAt(i)->widget();
|
|
|
|
|
|
|
|
if (TextRecordEditor *editor = qobject_cast<TextRecordEditor *>(widget)) {
|
|
|
|
message.append(editor->record());
|
|
|
|
} else if (UriRecordEditor *editor = qobject_cast<UriRecordEditor *>(widget)) {
|
|
|
|
message.append(editor->record());
|
|
|
|
} else if (MimeImageRecordEditor *editor = qobject_cast<MimeImageRecordEditor *>(widget)) {
|
|
|
|
message.append(editor->record());
|
|
|
|
} else if (qobject_cast<EmptyRecordLabel *>(widget)) {
|
|
|
|
message.append(QNdefRecord());
|
|
|
|
} else if (UnknownRecordLabel *label = qobject_cast<UnknownRecordLabel *>(widget)) {
|
|
|
|
message.append(label->record());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return message;
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "mainwindow.moc"
|