385 lines
12 KiB
QML
385 lines
12 KiB
QML
// Copyright (C) 2023 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
|
|
|
pragma ComponentBehavior: Bound
|
|
import QtQuick
|
|
import QtQuick.Controls
|
|
import QtQuick.Dialogs
|
|
import QtQuick.Layouts
|
|
|
|
import NdefEditor
|
|
|
|
ApplicationWindow {
|
|
id: window
|
|
|
|
enum TargetDetectedAction {
|
|
NoAction,
|
|
ReadMessage,
|
|
WriteMessage
|
|
}
|
|
|
|
StackView {
|
|
id: stack
|
|
initialItem: mainView
|
|
anchors.fill: parent
|
|
}
|
|
|
|
property int targetDetectedAction: MainWindow.NoAction
|
|
|
|
Component {
|
|
id: mainView
|
|
|
|
ColumnLayout {
|
|
ToolBar {
|
|
Layout.fillWidth: true
|
|
|
|
RowLayout {
|
|
anchors.fill: parent
|
|
Label {
|
|
text: qsTr("NDEF Editor")
|
|
elide: Label.ElideRight
|
|
horizontalAlignment: Qt.AlignHCenter
|
|
verticalAlignment: Qt.AlignVCenter
|
|
Layout.fillWidth: true
|
|
}
|
|
}
|
|
}
|
|
|
|
Pane {
|
|
id: writeTab
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
|
|
Flickable {
|
|
anchors.fill: parent
|
|
contentHeight: contentItem.childrenRect.height
|
|
|
|
ColumnLayout {
|
|
width: parent.width
|
|
|
|
ItemDelegate {
|
|
icon.name: "file_download"
|
|
text: qsTr("Read Tag")
|
|
Layout.fillWidth: true
|
|
|
|
onClicked: window.readTag()
|
|
}
|
|
|
|
ItemDelegate {
|
|
icon.name: "file_upload"
|
|
text: qsTr("Write to Tag")
|
|
Layout.fillWidth: true
|
|
|
|
onClicked: window.writeTag()
|
|
}
|
|
|
|
ItemDelegate {
|
|
icon.name: "add"
|
|
text: qsTr("Add a Record")
|
|
Layout.fillWidth: true
|
|
|
|
onClicked: addRecordMenu.open()
|
|
|
|
Menu {
|
|
id: addRecordMenu
|
|
title: qsTr("Add record")
|
|
|
|
MenuItem {
|
|
icon.name: "text_snippet"
|
|
text: qsTr("Text record")
|
|
onTriggered: stack.push(textRecordDialog)
|
|
}
|
|
MenuItem {
|
|
icon.name: "link"
|
|
text: qsTr("URI record")
|
|
onTriggered: stack.push(uriRecordDialog)
|
|
}
|
|
}
|
|
}
|
|
|
|
Item {
|
|
implicitHeight: 20
|
|
}
|
|
|
|
Label {
|
|
text: qsTr(
|
|
"Use buttons above to read an NFC tag or add records manually.\n\n"
|
|
+ "Once added, swipe to the right to access options for each record.")
|
|
wrapMode: Text.Wrap
|
|
visible: messages.count === 0
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
Repeater {
|
|
id: messages
|
|
model: messageModel
|
|
|
|
delegate: NdefRecordDelegate {
|
|
Layout.fillWidth: true
|
|
|
|
onDeleteClicked: messageModel.removeRow(index)
|
|
|
|
onClicked: {
|
|
if (recordType === NdefMessageModel.TextRecord) {
|
|
stack.push(textRecordDialog, {
|
|
text: recordText,
|
|
modelIndex: index
|
|
})
|
|
} else if (recordType === NdefMessageModel.UriRecord) {
|
|
stack.push(uriRecordDialog, {
|
|
uri: recordText,
|
|
modelIndex: index
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NdefMessageModel {
|
|
id: messageModel
|
|
}
|
|
|
|
NfcManager {
|
|
id: nfcManager
|
|
onTargetDetected: (target) => { window.handleTargetDetected(target) }
|
|
}
|
|
|
|
Connections {
|
|
id: targetConnections
|
|
target: null
|
|
|
|
function onNdefMessageRead(message) {
|
|
messageModel.message = message
|
|
}
|
|
|
|
function onRequestCompleted() {
|
|
communicationOverlay.close()
|
|
window.targetDetectedAction = MainWindow.NoAction
|
|
releaseTarget()
|
|
}
|
|
|
|
function onError(code) {
|
|
communicationOverlay.close()
|
|
errorNotification.errorMessage = window.describeError(code)
|
|
errorNotification.open()
|
|
window.targetDetectedAction = MainWindow.NoAction
|
|
releaseTarget()
|
|
}
|
|
|
|
function releaseTarget() {
|
|
target?.destroy()
|
|
}
|
|
}
|
|
|
|
function handleTargetDetected(target: NfcTarget) {
|
|
targetConnections.releaseTarget()
|
|
|
|
let ok = true
|
|
|
|
if (targetDetectedAction === MainWindow.ReadMessage) {
|
|
targetConnections.target = target
|
|
communicationOverlay.message = qsTr("Target detected. Reading messages...")
|
|
errorNotification.errorMessage = qsTr("NDEF read error")
|
|
ok = target.readNdefMessages()
|
|
} else if (targetDetectedAction === MainWindow.WriteMessage) {
|
|
targetConnections.target = target
|
|
communicationOverlay.message = qsTr("Target detected. Writing the message...")
|
|
errorNotification.errorMessage = qsTr("NDEF write error")
|
|
ok = target.writeNdefMessage(messageModel.message)
|
|
} else {
|
|
target.destroy()
|
|
return
|
|
}
|
|
|
|
if (!ok) {
|
|
communicationOverlay.close()
|
|
errorNotification.open()
|
|
targetConnections.releaseTarget()
|
|
}
|
|
}
|
|
|
|
function readTag() {
|
|
window.targetDetectedAction = MainWindow.ReadMessage
|
|
nfcManager.startTargetDetection()
|
|
communicationOverlay.title = qsTr("Read Tag")
|
|
communicationOverlay.message = qsTr("Approach an NFC tag.")
|
|
communicationOverlay.open()
|
|
}
|
|
|
|
function writeTag() {
|
|
window.targetDetectedAction = MainWindow.WriteMessage
|
|
nfcManager.startTargetDetection()
|
|
communicationOverlay.title = qsTr("Write to Tag")
|
|
communicationOverlay.message = qsTr("Approach an NFC tag.")
|
|
communicationOverlay.open()
|
|
}
|
|
|
|
function describeError(error) {
|
|
switch (error) {
|
|
case 2: return qsTr("Usupported feature")
|
|
case 3: return qsTr("Target out of range")
|
|
case 4: return qsTr("No response")
|
|
case 5: return qsTr("Checksum mismatch")
|
|
case 6: return qsTr("Invalid parameters")
|
|
case 7: return qsTr("Connection error")
|
|
case 8: return qsTr("NDEF read error")
|
|
case 9: return qsTr("NDEF write error")
|
|
case 10: return qsTr("Command error")
|
|
case 11: return qsTr("Timeout")
|
|
}
|
|
|
|
return qsTr("Unknown error")
|
|
}
|
|
|
|
Dialog {
|
|
id: communicationOverlay
|
|
|
|
property alias message: messageLabel.text
|
|
|
|
anchors.centerIn: Overlay.overlay
|
|
|
|
modal: true
|
|
standardButtons: Dialog.Cancel
|
|
|
|
ColumnLayout {
|
|
Label {
|
|
id: messageLabel
|
|
}
|
|
|
|
BusyIndicator {
|
|
Layout.fillWidth: true
|
|
}
|
|
}
|
|
|
|
onClosed: nfcManager.stopTargetDetection()
|
|
}
|
|
|
|
MessageDialog {
|
|
property alias errorMessage: errorNotification.text
|
|
|
|
id: errorNotification
|
|
|
|
buttons: MessageDialog.Close
|
|
title: qsTr("Error")
|
|
|
|
onButtonClicked: errorNotification.close()
|
|
}
|
|
|
|
Component {
|
|
id: uriRecordDialog
|
|
|
|
ColumnLayout {
|
|
property alias uri: uriEditor.text
|
|
property int modelIndex: -1
|
|
|
|
ToolBar {
|
|
Layout.fillWidth: true
|
|
|
|
RowLayout {
|
|
anchors.fill: parent
|
|
ToolButton {
|
|
icon.name: "arrow_back"
|
|
onClicked: stack.pop()
|
|
}
|
|
Label {
|
|
text: qsTr("URI Record")
|
|
elide: Label.ElideRight
|
|
horizontalAlignment: Qt.AlignHCenter
|
|
verticalAlignment: Qt.AlignVCenter
|
|
Layout.fillWidth: true
|
|
}
|
|
ToolButton {
|
|
text: qsTr("Save")
|
|
onClicked: {
|
|
if (modelIndex < 0) {
|
|
messageModel.addUriRecord(uri)
|
|
} else {
|
|
messageModel.setTextData(modelIndex, uri)
|
|
}
|
|
stack.pop()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Pane {
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
|
|
ColumnLayout {
|
|
anchors.fill: parent
|
|
|
|
TextField {
|
|
id: uriEditor
|
|
Layout.fillWidth: true
|
|
placeholderText: qsTr("https://qt.io")
|
|
inputMethodHints: Qt.ImhUrlCharactersOnly
|
|
}
|
|
Item {
|
|
Layout.fillHeight: true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: textRecordDialog
|
|
|
|
ColumnLayout {
|
|
property alias text: textEditor.text
|
|
property int modelIndex: -1
|
|
|
|
ToolBar {
|
|
Layout.fillWidth: true
|
|
|
|
RowLayout {
|
|
anchors.fill: parent
|
|
ToolButton {
|
|
icon.name: "arrow_back"
|
|
onClicked: stack.pop()
|
|
}
|
|
Label {
|
|
text: qsTr("Text Record")
|
|
elide: Label.ElideRight
|
|
horizontalAlignment: Qt.AlignHCenter
|
|
verticalAlignment: Qt.AlignVCenter
|
|
Layout.fillWidth: true
|
|
}
|
|
ToolButton {
|
|
text: qsTr("Save")
|
|
onClicked: {
|
|
if (modelIndex < 0) {
|
|
messageModel.addTextRecord(textEditor.text)
|
|
} else {
|
|
messageModel.setTextData(modelIndex, textEditor.text)
|
|
}
|
|
stack.pop()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Pane {
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
|
|
ScrollView {
|
|
anchors.fill: parent
|
|
|
|
TextArea {
|
|
id: textEditor
|
|
placeholderText: qsTr("Enter some text...")
|
|
Layout.fillWidth: true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|