QtNfc: Rewrite NDEF Editor example using Qt Quick Components

This makes the example look more native on mobile platforms.
Leave only the functionality related to reading and writing
of text and URI NDEF records to simplify the code. Removed
functionality includes saving/reading messages to files,
and support for image NDEF records.

Pick-to: 6.5
Fixes: QTBUG-103949
Change-Id: Ie14e3009e2d0238b266eb9690795ca6b56c0010c
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Ievgenii Meshcheriakov 2022-11-09 15:56:22 +01:00
parent 849ba86ba9
commit 115b4d947a
61 changed files with 1090 additions and 1491 deletions

View File

@ -13,7 +13,8 @@ project(QtConnectivity
set(QT_REPO_NOT_WARNINGS_CLEAN TRUE)
find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS BuildInternals Core Network)
find_package(Qt6 ${PROJECT_VERSION} CONFIG OPTIONAL_COMPONENTS DBus Gui Widgets Quick)
find_package(Qt6 ${PROJECT_VERSION} CONFIG OPTIONAL_COMPONENTS
DBus Gui Widgets Quick QuickControls2)
if(NOT TARGET Qt::Network)
message(NOTICE "Skipping the build as the condition \"TARGET Qt::Network\" is not met.")

61
LICENSES/Apache-2.0.txt Normal file
View File

@ -0,0 +1,61 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -3,5 +3,7 @@
if(TARGET Qt::Widgets)
qt_internal_add_example(annotatedurl)
endif()
if(TARGET Qt::QuickControls2)
qt_internal_add_example(ndefeditor)
endif()

View File

@ -1,11 +1,10 @@
# Copyright (C) 2022 The Qt Company Ltd.
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(ndefeditor LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
@ -13,44 +12,55 @@ endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/nfc/ndefeditor")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Nfc Widgets)
find_package(Qt6 REQUIRED COMPONENTS Core Nfc Quick QuickControls2)
qt_add_executable(ndefeditor
MANUAL_FINALIZATION
main.cpp
mainwindow.cpp mainwindow.h mainwindow.ui
mimeimagerecordeditor.cpp mimeimagerecordeditor.h mimeimagerecordeditor.ui
textrecordeditor.cpp textrecordeditor.h textrecordeditor.ui
urirecordeditor.cpp urirecordeditor.h urirecordeditor.ui
)
set(icon_files)
foreach(icon IN ITEMS add arrow_back file_download file_upload link text_snippet)
foreach(scale IN ITEMS "" "@2" "@3" "@4")
list(APPEND icon_files "icons/ndefeditor/20x20${scale}/${icon}.png")
endforeach()
endforeach()
qt_add_resources(ndefeditor "theme" FILES
${icon_files}
icons/ndefeditor/index.theme
)
qt_add_qml_module(ndefeditor
URI NdefEditor
VERSION 1.0
AUTO_RESOURCE_PREFIX
QML_FILES
Main.qml
MainWindow.qml
NdefRecordDelegate.qml
SOURCES
nfcmanager.h nfcmanager.cpp
nfctarget.h nfctarget.cpp
ndefmessagemodel.h ndefmessagemodel.cpp
)
set_target_properties(ndefeditor PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE TRUE
)
if(IOS)
set_target_properties(ndefeditor PROPERTIES
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist"
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.cmake.plist"
)
endif()
target_link_libraries(ndefeditor PUBLIC
Qt::Core
Qt::Gui
Qt::Nfc
Qt::Widgets
target_link_libraries(ndefeditor PRIVATE
Qt6::Core
Qt6::Nfc
Qt6::Quick
)
if(ANDROID)
set_property(TARGET ndefeditor
APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
${CMAKE_CURRENT_SOURCE_DIR}/android
)
endif()
qt_finalize_target(ndefeditor)
install(TARGETS ndefeditor
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundleDisplayName</key>
<string>NDEF Editor</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleAllowMixedLocalizations</key>
<true/>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NFCReaderUsageDescription</key>
<string>Qt's ndefeditor wants to access your NFC hardware</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@ -1,41 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string>${ASSETCATALOG_COMPILER_APPICON_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${QMAKE_SHORT_VERSION}</string>
<key>CFBundleSignature</key>
<string>${QMAKE_PKGINFO_TYPEINFO}</string>
<key>CFBundleVersion</key>
<string>${QMAKE_FULL_VERSION}</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>MinimumOSVersion</key>
<string>${IPHONEOS_DEPLOYMENT_TARGET}</string>
<key>NFCReaderUsageDescription</key>
<string>Qt's ndefeditor wants to access your NFC hardware</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleDisplayName</key>
<string>NDEF Editor</string>
<key>CFBundleIdentifier</key>
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleVersion</key>
<string>${QMAKE_FULL_VERSION}</string>
<key>CFBundleShortVersionString</key>
<string>${QMAKE_SHORT_VERSION}</string>
<key>CFBundleIconFile</key>
<string>${ASSETCATALOG_COMPILER_APPICON_NAME}</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NFCReaderUsageDescription</key>
<string>Qt's ndefeditor wants to access your NFC hardware</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@ -0,0 +1,9 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
MainWindow {
width: 480
height: 640
visible: true
title: qsTr("NDEF Editor")
}

View File

@ -0,0 +1,385 @@
// 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() {
messageModel.clearMessage()
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
}
}
}
}
}
}

View File

@ -0,0 +1,50 @@
// 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.Layouts
import NdefEditor
SwipeDelegate {
required property int index
required property int recordType
required property string recordText
id: delegate
contentItem: ColumnLayout {
Label {
text: qsTr("Record %L1 - %2").arg(delegate.index + 1).arg(
delegate.describeRecordType(delegate.recordType))
font.bold: true
}
Label {
text: delegate.recordText
elide: Text.ElideRight
maximumLineCount: 1
Layout.fillWidth: true
}
}
swipe.left: Button {
text: qsTr("Delete")
padding: 12
height: delegate.height
anchors.left: delegate.left
SwipeDelegate.onClicked: delegate.deleteClicked()
}
function describeRecordType(type) {
switch (type) {
case NdefMessageModel.TextRecord: return qsTr("Text")
case NdefMessageModel.UriRecord: return qsTr("URI")
default: return qsTr("Other")
}
}
signal deleteClicked()
}

View File

@ -1,48 +0,0 @@
<?xml version="1.0"?>
<manifest package="org.qtproject.example" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="-- %%INSERT_VERSION_NAME%% --" android:versionCode="-- %%INSERT_VERSION_CODE%% --" android:installLocation="auto">
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
Remove the comment if you do not require these default permissions. -->
<!-- %%INSERT_PERMISSIONS -->
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
Remove the comment if you do not require these default features. -->
<!-- %%INSERT_FEATURES -->
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt.android.bindings.QtApplication" android:label="-- %%INSERT_APP_NAME%% --" android:extractNativeLibs="true" android:requestLegacyExternalStorage="true">
<activity android:windowSoftInputMode="adjustResize" android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="org.qtproject.qt.android.bindings.QtActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="unspecified" android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<!-- Application arguments -->
<meta-data android:name="android.app.arguments" android:value="-- %%INSERT_APP_ARGUMENTS%% --"/>
<!-- Application arguments -->
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
<!-- Background running -->
<!-- Warning: changing this value to true may cause unexpected crashes if the
application still try to draw after
"applicationStateChanged(Qt::ApplicationSuspended)"
signal is sent! -->
<meta-data android:name="android.app.background_running" android:value="false"/>
<!-- Background running -->
<!-- extract android style -->
<!-- available android:values :
* default - In most cases this will be the same as "full", but it can also be something else if needed, e.g., for compatibility reasons
* full - useful QWidget & Quick Controls 1 apps
* minimal - useful for Quick Controls 2 apps, it is much faster than "full"
* none - useful for apps that don't use any of the above Qt modules
-->
<meta-data android:name="android.app.extract_android_style" android:value="default"/>
<!-- extract android style -->
</activity>
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
</application>
</manifest>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -8,78 +8,9 @@
The NDEF Editor example reads and writes NFC Data Exchange Format
(NDEF) messages to NFC Forum Tags. NDEF messages can be composed by
adding records of supported types. Additionally, NDEF messages can be
loaded/saved from/into a file located in the file system of the
device where the application is running.
adding text and URI records. Records can be deleted by swiping them to the left.
\image ndefeditor.png
\section1 NFC Tag detection
The \c MainWindow class is able to detect if an NFC Tag is in the range
for read/write operations. It can also detect if the connection has been
lost. This is achieved by connecting the \c MainWindow class private
handlers to the \l QNearFieldManager::targetDetected and
\l QNearFieldManager::targetLost signals.
\snippet ndefeditor/mainwindow.cpp QNearFieldManager init
When \e Read or \e Write button is pressed, the detection of NFC tags is started
by calling the \l QNearFieldManager::startTargetDetection method.
\snippet ndefeditor/mainwindow.cpp QNearFieldManager start detection
Once the target is detected, the \c MainWindow connects the following
signals to its internal private slots:
\l QNearFieldTarget::ndefMessageRead, \l QNearFieldTarget::NdefReadError,
\l QNearFieldTarget::requestCompleted,
\l QNearFieldTarget::NdefWriteError and \l {QNearFieldTarget::error}.
\snippet ndefeditor/mainwindow.cpp QNearFieldTarget detected
If during the process of reading or writing to an NFC Tag the
connection is lost, the \c MainWindow reacts to this event by
scheduling the target deletion (using \l QObject::deleteLater).
\snippet ndefeditor/mainwindow.cpp QNearFieldTarget lost
\section1 Record creation
The main window of the NDEF Editor example manages the composition and
creation of NFC records. The UI contains a \l QScrollArea, which is used to
dynamically add the record editors. The following methods of the \c MainWindow
class provide an interface towards each of the record editing classes managing
the different types of records.
\snippet ndefeditor/mainwindow.h 0
The following sections explain each of the record editing classes.
\section1 Record editing classes
\section2 TextRecordEditor
The \c TextRecordEditor is a \l QWidget that allows to edit the contents of the
NDEF Text record. A new instance of this class is created for each text record.
\snippet ndefeditor/textrecordeditor.h 0
\section2 UriRecordEditor
The \c UriRecordEditor is a \l QWidget that allows to edit the contents of the
NDEF Uri record. A new instance of this class is created for each uri record.
\snippet ndefeditor/urirecordeditor.h 0
\section2 MimeImageRecordEditor
The \c MimeImageRecordEditor is a \l QWidget that allows to edit the contents of
the NDEF MIME record. In this example MIME record can be used to store an icon.
A new instance of this class is created for each MIME record.
\snippet ndefeditor/mimeimagerecordeditor.h 0
\include examples-run.qdocinc
\sa {Qt NFC}
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 B

View File

@ -0,0 +1,22 @@
[Icon Theme]
Name=ndefeditor
Directories=20x20,20x20@2,20x20@3,20x20@4
[20x20]
Size=20
Type=Fixed
[20x20@2]
Size=20
Scale=2
Type=Fixed
[20x20@3]
Size=20
Scale=3
Type=Fixed
[20x20@4]
Size=20
Scale=4
Type=Fixed

View File

@ -0,0 +1,11 @@
{
"Id": "ndefeditor",
"Name": "Selected Material Icons",
"QDocModule": "qtconnectivity",
"QtUsage": "Used in NDEF Editor Example for QtNfc.",
"Files": "*/*.png",
"Homepage": "https://fonts.google.com/icons",
"License": "Apache License Version 2.0",
"LicenseId": "Apache-2.0",
"Copyright": "Copyright 2018 Google, Inc. All Rights Reserved."
}

View File

@ -1,15 +1,22 @@
// Copyright (C) 2017 The Qt Company Ltd.
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include "mainwindow.h"
#include <QGuiApplication>
#include <QIcon>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
QGuiApplication app(argc, argv);
w.show();
QIcon::setThemeName("ndefeditor");
return a.exec();
QQmlApplicationEngine engine;
QObject::connect(
&engine, &QQmlApplicationEngine::objectCreationFailed, &app,
[]() { QCoreApplication::exit(1); }, Qt::QueuedConnection);
engine.loadFromModule("NdefEditor", "Main");
return app.exec();
}

View File

@ -1,376 +0,0 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "textrecordeditor.h"
#include "urirecordeditor.h"
#include "mimeimagerecordeditor.h"
#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>
#include <QtWidgets/QScroller>
#include <QtWidgets/QApplication>
#include <QtGui/QScreen>
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;
};
template<typename T>
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);
hline->setObjectName(QStringLiteral("line-spacer"));
vbox->addWidget(hline);
}
T *recordEditor = new T;
recordEditor->setObjectName(QStringLiteral("record-editor"));
vbox->addWidget(recordEditor);
if (!record.isEmpty())
recordEditor->setRecord(record);
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow), m_touchAction(NoAction)
{
ui->setupUi(this);
connect(ui->addRecord, &QPushButton::clicked, this, &MainWindow::showMenu);
QVBoxLayout *vbox = new QVBoxLayout;
ui->scrollAreaWidgetContents->setLayout(vbox);
#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(); });
//! [QNearFieldManager init]
m_manager = new QNearFieldManager(this);
connect(m_manager, &QNearFieldManager::targetDetected,
this, &MainWindow::targetDetected);
connect(m_manager, &QNearFieldManager::targetLost,
this, &MainWindow::targetLost);
//! [QNearFieldManager init]
}
MainWindow::~MainWindow()
{
delete ui;
}
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);
}
}
}
}
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()
{
ui->status->setStyleSheet(QStringLiteral("background: blue"));
m_touchAction = ReadNdef;
//! [QNearFieldManager start detection]
m_manager->startTargetDetection(QNearFieldTarget::NdefAccess);
//! [QNearFieldManager start detection]
}
void MainWindow::touchStore()
{
ui->status->setStyleSheet(QStringLiteral("background: yellow"));
m_touchAction = WriteNdef;
m_manager->startTargetDetection(QNearFieldTarget::NdefAccess);
}
//! [QNearFieldTarget detected]
void MainWindow::targetDetected(QNearFieldTarget *target)
{
switch (m_touchAction) {
case NoAction:
break;
case ReadNdef:
connect(target, &QNearFieldTarget::ndefMessageRead, this, &MainWindow::ndefMessageRead);
connect(target, &QNearFieldTarget::error, this, &MainWindow::targetError);
m_request = target->readNdefMessages();
if (!m_request.isValid()) // cannot read messages
targetError(QNearFieldTarget::NdefReadError, m_request);
break;
case WriteNdef:
connect(target, &QNearFieldTarget::requestCompleted, this, &MainWindow::ndefMessageWritten);
connect(target, &QNearFieldTarget::error, this, &MainWindow::targetError);
m_request = target->writeNdefMessages(QList<QNdefMessage>() << ndefMessage());
if (!m_request.isValid()) // cannot write messages
targetError(QNearFieldTarget::NdefWriteError, m_request);
break;
}
}
//! [QNearFieldTarget detected]
//! [QNearFieldTarget lost]
void MainWindow::targetLost(QNearFieldTarget *target)
{
target->deleteLater();
}
//! [QNearFieldTarget lost]
void MainWindow::ndefMessageRead(const QNdefMessage &message)
{
clearMessage();
for (const QNdefRecord &record : message) {
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());
//! [QNearFieldManager stop detection]
m_manager->stopTargetDetection();
//! [QNearFieldManager stop detection]
m_request = QNearFieldTarget::RequestId();
ui->statusBar->clearMessage();
}
void MainWindow::ndefMessageWritten(const QNearFieldTarget::RequestId &id)
{
if (id == m_request) {
ui->status->setStyleSheet(QString());
m_manager->stopTargetDetection();
m_request = QNearFieldTarget::RequestId();
ui->statusBar->clearMessage();
}
}
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();
}
}
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)));
}
void MainWindow::clearMessage()
{
QWidget *scrollArea = ui->scrollAreaWidgetContents;
qDeleteAll(scrollArea->findChildren<QWidget *>(QStringLiteral("line-spacer")));
qDeleteAll(scrollArea->findChildren<QWidget *>(QStringLiteral("record-editor")));
}
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"

View File

@ -1,79 +0,0 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtNfc/qnearfieldtarget.h>
#include <QtWidgets/QMainWindow>
QT_FORWARD_DECLARE_CLASS(QNearFieldManager)
QT_FORWARD_DECLARE_CLASS(QNdefMessage)
QT_FORWARD_DECLARE_CLASS(QScreen)
QT_FORWARD_DECLARE_CLASS(QMenu)
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
void resizeEvent(QResizeEvent *e) override;
private slots:
//! [0]
void addNfcTextRecord();
void addNfcUriRecord();
void addMimeImageRecord();
void addEmptyRecord();
//! [0]
void clearMessage();
void loadMessage();
void saveMessage();
void touchReceive();
void touchStore();
void targetDetected(QNearFieldTarget *target);
void targetLost(QNearFieldTarget *target);
void ndefMessageRead(const QNdefMessage &message);
void ndefMessageWritten(const QNearFieldTarget::RequestId &id);
void targetError(QNearFieldTarget::Error error, const QNearFieldTarget::RequestId &id);
void showMenu();
private:
enum TouchAction {
NoAction,
ReadNdef,
WriteNdef
};
QNdefMessage ndefMessage() const;
void handleScreenChange();
void updateWidgetLayout(Qt::ScreenOrientation orientation);
private:
Ui::MainWindow *ui;
QMenu *m_menu = nullptr;
QNearFieldManager *m_manager;
TouchAction m_touchAction;
QNearFieldTarget::RequestId m_request;
bool m_keyboardVisible = false;
QScreen *m_screen = nullptr;
};
#endif // MAINWINDOW_H

View File

@ -1,225 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>559</width>
<height>397</height>
</rect>
</property>
<property name="windowTitle">
<string>NDEF Message Editor</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<item>
<widget class="QWidget" name="status" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>25</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>25</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="touchRetrieve">
<property name="text">
<string>Read</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="touchStore">
<property name="text">
<string>Write</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="loadMessage">
<property name="text">
<string>Load</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="saveMessage">
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="clearMessage">
<property name="text">
<string>Clear</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addRecord">
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAsNeeded</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>559</width>
<height>327</height>
</rect>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QStatusBar" name="statusBar"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections>
<connection>
<sender>saveMessage</sender>
<signal>clicked()</signal>
<receiver>MainWindow</receiver>
<slot>saveMessage()</slot>
<hints>
<hint type="sourcelabel">
<x>419</x>
<y>47</y>
</hint>
<hint type="destinationlabel">
<x>275</x>
<y>198</y>
</hint>
</hints>
</connection>
<connection>
<sender>touchRetrieve</sender>
<signal>clicked()</signal>
<receiver>MainWindow</receiver>
<slot>touchReceive()</slot>
<hints>
<hint type="sourcelabel">
<x>142</x>
<y>47</y>
</hint>
<hint type="destinationlabel">
<x>275</x>
<y>198</y>
</hint>
</hints>
</connection>
<connection>
<sender>touchStore</sender>
<signal>clicked()</signal>
<receiver>MainWindow</receiver>
<slot>touchStore()</slot>
<hints>
<hint type="sourcelabel">
<x>244</x>
<y>47</y>
</hint>
<hint type="destinationlabel">
<x>275</x>
<y>198</y>
</hint>
</hints>
</connection>
<connection>
<sender>loadMessage</sender>
<signal>clicked()</signal>
<receiver>MainWindow</receiver>
<slot>loadMessage()</slot>
<hints>
<hint type="sourcelabel">
<x>334</x>
<y>47</y>
</hint>
<hint type="destinationlabel">
<x>275</x>
<y>198</y>
</hint>
</hints>
</connection>
<connection>
<sender>clearMessage</sender>
<signal>clicked()</signal>
<receiver>MainWindow</receiver>
<slot>clearMessage()</slot>
<hints>
<hint type="sourcelabel">
<x>419</x>
<y>22</y>
</hint>
<hint type="destinationlabel">
<x>275</x>
<y>198</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>saveMessage()</slot>
<slot>loadMessage()</slot>
<slot>touchReceive()</slot>
<slot>touchStore()</slot>
<slot>clearMessage()</slot>
</slots>
</ui>

View File

@ -1,170 +0,0 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "mimeimagerecordeditor.h"
#include "ui_mimeimagerecordeditor.h"
#include <QtGui/QImageReader>
#include <QtWidgets/QFileDialog>
#include <QtCore/QBuffer>
#include <QScreen>
#include <QLayout>
#if (defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)) || defined(Q_OS_IOS)
# define MOBILE_PLATFORM
#endif
static QString imageFormatToMimeType(const QByteArray &format)
{
if (format == "bmp")
return QStringLiteral("image/bmp");
else if (format == "gif")
return QStringLiteral("image/gif");
else if (format == "jpg" || format == "jpeg")
return QStringLiteral("image/jpeg");
else if (format == "mng")
return QStringLiteral("video/x-mng");
else if (format == "png")
return QStringLiteral("image/png");
else if (format == "pbm")
return QStringLiteral("image/x-portable-bitmap");
else if (format == "pgm")
return QStringLiteral("image/x-portable-graymap");
else if (format == "ppm")
return QStringLiteral("image/x-portable-pixmap");
else if (format == "tiff")
return QStringLiteral("image/tiff");
else if (format == "xbm")
return QStringLiteral("image/x-xbitmap");
else if (format == "xpm")
return QStringLiteral("image/x-xpixmap");
else if (format == "svg")
return QStringLiteral("image/svg+xml");
else
return QString();
}
MimeImageRecordEditor::MimeImageRecordEditor(QWidget *parent) :
QWidget(parent),
ui(new Ui::MimeImageRecordEditor)
{
ui->setupUi(this);
#ifdef MOBILE_PLATFORM
connect(screen(), &QScreen::orientationChanged, this,
&MimeImageRecordEditor::handleScreenOrientationChange);
#endif
}
MimeImageRecordEditor::~MimeImageRecordEditor()
{
delete ui;
}
void MimeImageRecordEditor::setRecord(const QNdefRecord &record)
{
m_record = record;
QByteArray data = record.payload();
QBuffer buffer(&data);
buffer.open(QIODevice::ReadOnly);
QImageReader reader(&buffer);
ui->mimeImageType->setText(imageFormatToMimeType(reader.format()));
ui->mimeImageFile->clear();
m_pixmap = QPixmap::fromImage(reader.read());
updatePixmap();
}
QNdefRecord MimeImageRecordEditor::record() const
{
return m_record;
}
void MimeImageRecordEditor::handleScreenOrientationChange(Qt::ScreenOrientation orientation)
{
Q_UNUSED(orientation);
#ifdef MOBILE_PLATFORM
if (m_imageSelected) {
ui->mimeImageImage->clear();
adjustSize();
m_screenRotated = true;
}
#endif
}
void MimeImageRecordEditor::resizeEvent(QResizeEvent *)
{
if (m_imageSelected) {
#ifdef MOBILE_PLATFORM
if (m_screenRotated) {
updatePixmap();
m_screenRotated = false;
}
#else
updatePixmap();
#endif
}
}
void MimeImageRecordEditor::updatePixmap()
{
// Calculate the desired width of the image. It's calculated based on the
// screen size minus the content margins.
const auto parentContentMargins = parentWidget()->layout()->contentsMargins();
const auto thisContentMargins = layout()->contentsMargins();
#ifdef MOBILE_PLATFORM
// Because of QTBUG-94459 the screen size might be incorrect, so we check
// the orientation to find the actual width
const auto w = screen()->availableSize().width();
const auto h = screen()->availableSize().height();
const auto screenWidth =
(screen()->orientation() == Qt::PortraitOrientation) ? qMin(w, h) : qMax(w, h);
#else
const auto screenWidth = width();
#endif
const auto imageWidth = screenWidth - parentContentMargins.right() - parentContentMargins.left()
- thisContentMargins.right() - thisContentMargins.left();
if (!m_pixmap.isNull()) {
if (m_pixmap.width() > imageWidth)
ui->mimeImageImage->setPixmap(m_pixmap.scaledToWidth(imageWidth));
else
ui->mimeImageImage->setPixmap(m_pixmap);
} else {
ui->mimeImageImage->setText("Can't show the image");
}
}
void MimeImageRecordEditor::on_mimeImageOpen_clicked()
{
QString mimeDataFile = QFileDialog::getOpenFileName(this, tr("Select Image File"));
if (mimeDataFile.isEmpty())
return;
QFile imageFile(mimeDataFile);
if (!imageFile.open(QIODevice::ReadOnly)) {
ui->mimeImageFile->clear();
ui->mimeImageImage->clear();
}
QByteArray imageData = imageFile.readAll();
QBuffer buffer(&imageData);
buffer.open(QIODevice::ReadOnly);
QImageReader reader(&buffer);
QString mimeType = imageFormatToMimeType(reader.format());
ui->mimeImageType->setText(mimeType);
ui->mimeImageFile->setText(mimeDataFile);
const QImage image = reader.read();
m_pixmap = QPixmap::fromImage(image);
m_imageSelected = true;
updatePixmap();
m_record.setTypeNameFormat(QNdefRecord::Mime);
m_record.setType(mimeType.toLatin1());
m_record.setPayload(imageData);
}

View File

@ -1,50 +0,0 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef MIMEIMAGERECORDEDITOR_H
#define MIMEIMAGERECORDEDITOR_H
#include <QtNfc/qndefrecord.h>
#include <QtWidgets/QWidget>
QT_USE_NAMESPACE
QT_BEGIN_NAMESPACE
namespace Ui {
class MimeImageRecordEditor;
}
QT_END_NAMESPACE
//! [0]
class MimeImageRecordEditor : public QWidget
{
Q_OBJECT
public:
explicit MimeImageRecordEditor(QWidget *parent = 0);
~MimeImageRecordEditor();
void setRecord(const QNdefRecord &record);
QNdefRecord record() const;
public slots:
void handleScreenOrientationChange(Qt::ScreenOrientation orientation);
protected:
void resizeEvent(QResizeEvent *) override;
private:
void updatePixmap();
Ui::MimeImageRecordEditor *ui;
QNdefRecord m_record;
QPixmap m_pixmap;
bool m_imageSelected = false;
bool m_screenRotated = false;
private slots:
void on_mimeImageOpen_clicked();
};
//! [0]
#endif // MIMEIMAGERECORDEDITOR_H

View File

@ -1,86 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MimeImageRecordEditor</class>
<widget class="QWidget" name="MimeImageRecordEditor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>403</width>
<height>126</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_4">
<property name="text">
<string>MIME Record &lt;image/*&gt;</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Type:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="mimeImageType">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>image/</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>File:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="mimeImageFile">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="mimeImageOpen">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0" colspan="2">
<widget class="QLabel" name="mimeImageImage">
<property name="text">
<string>Select an image</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1,36 +1,66 @@
QT += nfc widgets
requires(qtConfig(filedialog))
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
QT += nfc quick quickcontrols2
CONFIG += qmltypes
QML_IMPORT_NAME = NdefEditor
QML_IMPORT_MAJOR_VERSION = 1
TARGET = ndefeditor
TEMPLATE = app
SOURCES += \
main.cpp \
mainwindow.cpp \
textrecordeditor.cpp \
urirecordeditor.cpp \
mimeimagerecordeditor.cpp
nfcmanager.cpp \
nfctarget.cpp \
ndefmessagemodel.cpp
HEADERS += \
mainwindow.h \
textrecordeditor.h \
urirecordeditor.h \
mimeimagerecordeditor.h
nfcmanager.h \
nfctarget.h \
ndefmessagemodel.h
ios: QMAKE_INFO_PLIST = Info.plist
qml_resources.files = \
qmldir \
Main.qml \
MainWindow.qml \
NdefRecordDelegate.qml
FORMS += \
mainwindow.ui \
textrecordeditor.ui \
urirecordeditor.ui \
mimeimagerecordeditor.ui
qml_resources.prefix = /qt/qml/NdefEditor
android {
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
theme_resources.files = \
icons/ndefeditor/20x20@2/add.png \
icons/ndefeditor/20x20@2/arrow_back.png \
icons/ndefeditor/20x20@2/file_download.png \
icons/ndefeditor/20x20@2/file_upload.png \
icons/ndefeditor/20x20@2/link.png \
icons/ndefeditor/20x20@2/text_snippet.png \
icons/ndefeditor/20x20@3/add.png \
icons/ndefeditor/20x20@3/arrow_back.png \
icons/ndefeditor/20x20@3/file_download.png \
icons/ndefeditor/20x20@3/file_upload.png \
icons/ndefeditor/20x20@3/link.png \
icons/ndefeditor/20x20@3/text_snippet.png \
icons/ndefeditor/20x20@4/add.png \
icons/ndefeditor/20x20@4/arrow_back.png \
icons/ndefeditor/20x20@4/file_download.png \
icons/ndefeditor/20x20@4/file_upload.png \
icons/ndefeditor/20x20@4/link.png \
icons/ndefeditor/20x20@4/text_snippet.png \
icons/ndefeditor/20x20/add.png \
icons/ndefeditor/20x20/arrow_back.png \
icons/ndefeditor/20x20/file_download.png \
icons/ndefeditor/20x20/file_upload.png \
icons/ndefeditor/20x20/link.png \
icons/ndefeditor/20x20/text_snippet.png \
icons/ndefeditor/index.theme
DISTFILES += \
android/AndroidManifest.xml
}
theme_resources.prefix = /
RESOURCES += qml_resources theme_resources
ios: QMAKE_INFO_PLIST = Info.qmake.plist
target.path = $$[QT_INSTALL_EXAMPLES]/nfc/ndefeditor
INSTALLS += target

View File

@ -0,0 +1,156 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "ndefmessagemodel.h"
#include <QNdefNfcTextRecord>
#include <QNdefNfcUriRecord>
NdefMessageModel::NdefMessageModel(QObject *parent) : QAbstractListModel(parent) { }
int NdefMessageModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return m_message.size();
}
static NdefMessageModel::RecordType getRecordType(const QNdefRecord &record)
{
if (record.isRecordType<QNdefNfcTextRecord>()) {
return NdefMessageModel::TextRecord;
} else if (record.isRecordType<QNdefNfcUriRecord>()) {
return NdefMessageModel::UriRecord;
}
return NdefMessageModel::OtherRecord;
}
static QString getText(const QNdefRecord &record)
{
if (record.isRecordType<QNdefNfcTextRecord>()) {
QNdefNfcTextRecord r(record);
return r.text();
} else if (record.isRecordType<QNdefNfcUriRecord>()) {
QNdefNfcUriRecord r(record);
return r.uri().toString();
}
return record.payload().toHex(':');
}
QVariant NdefMessageModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return {};
if (index.row() < 0 || index.row() >= m_message.size())
return {};
const auto &record = m_message.at(index.row());
switch (role) {
case RecordTypeRole:
return getRecordType(record);
case RecordTextRole:
return getText(record);
default:
return {};
}
}
QHash<int, QByteArray> NdefMessageModel::roleNames() const
{
QHash<int, QByteArray> names;
names[RecordTypeRole] = "recordType";
names[RecordTextRole] = "recordText";
return names;
}
bool NdefMessageModel::removeRows(int row, int count, const QModelIndex &parent)
{
if (parent.isValid())
return false;
if (row < 0 || count <= 0)
return false;
if (row >= m_message.size() || (m_message.size() - row) < count)
return false;
beginRemoveRows(parent, row, row + count - 1);
m_message.remove(row, count);
endRemoveRows();
Q_EMIT messageChanged();
return true;
}
QNdefMessage NdefMessageModel::message() const
{
return m_message;
}
void NdefMessageModel::setMessage(const QNdefMessage &newMessage)
{
if (m_message == newMessage)
return;
beginResetModel();
m_message = newMessage;
endResetModel();
Q_EMIT messageChanged();
}
void NdefMessageModel::clearMessage()
{
if (m_message.isEmpty())
return;
removeRows(0, m_message.size(), {});
}
void NdefMessageModel::addTextRecord(const QString &text)
{
QNdefNfcTextRecord record;
record.setText(text);
const auto newRow = m_message.size();
beginInsertRows({}, newRow, newRow);
m_message.append(std::move(record));
endInsertRows();
Q_EMIT messageChanged();
}
void NdefMessageModel::addUriRecord(const QString &uri)
{
QNdefNfcUriRecord record;
record.setUri(QUrl(uri));
const auto newRow = m_message.size();
beginInsertRows({}, newRow, newRow);
m_message.append(std::move(record));
endInsertRows();
Q_EMIT messageChanged();
}
void NdefMessageModel::setTextData(int row, const QString &text)
{
if (row < 0 || row >= m_message.size())
return;
const auto &record = m_message.at(row);
if (record.isRecordType<QNdefNfcTextRecord>()) {
QNdefNfcTextRecord r(record);
r.setText(text);
m_message[row] = r;
} else if (record.isRecordType<QNdefNfcUriRecord>()) {
QNdefNfcUriRecord r(record);
r.setUri(text);
m_message[row] = r;
} else {
return;
}
const auto idx = index(row);
Q_EMIT dataChanged(idx, idx, { RecordTextRole });
Q_EMIT messageChanged();
}

View File

@ -0,0 +1,53 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef NDEFMESSAGEMODEL_H
#define NDEFMESSAGEMODEL_H
#include <QAbstractListModel>
#include <QNdefMessage>
#include <QQmlEngine>
class NdefMessageModel : public QAbstractListModel
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QNdefMessage message READ message WRITE setMessage NOTIFY messageChanged)
public:
explicit NdefMessageModel(QObject *parent = nullptr);
enum Role {
RecordTypeRole,
RecordTextRole,
};
enum RecordType {
OtherRecord,
TextRecord,
UriRecord,
};
Q_ENUM(RecordType)
int rowCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
QHash<int, QByteArray> roleNames() const override;
bool removeRows(int row, int count, const QModelIndex &parent) override;
QNdefMessage message() const;
void setMessage(const QNdefMessage &newMessage);
Q_INVOKABLE void clearMessage();
Q_INVOKABLE void addTextRecord(const QString &text);
Q_INVOKABLE void addUriRecord(const QString &uri);
Q_INVOKABLE void setTextData(int row, const QString &text);
Q_SIGNALS:
void messageChanged();
private:
QNdefMessage m_message;
};
#endif // NDEFMESSAGEMODEL_H

View File

@ -0,0 +1,29 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "nfcmanager.h"
#include <QNearFieldManager>
#include "nfctarget.h"
NfcManager::NfcManager(QObject *parent) : QObject(parent)
{
m_manager = new QNearFieldManager(this);
connect(m_manager, &QNearFieldManager::targetDetected, this, [this](QNearFieldTarget *target) {
auto jsTarget = new NfcTarget(target);
QJSEngine::setObjectOwnership(jsTarget, QJSEngine::JavaScriptOwnership);
Q_EMIT targetDetected(jsTarget);
});
}
void NfcManager::startTargetDetection()
{
m_manager->startTargetDetection(QNearFieldTarget::NdefAccess);
}
void NfcManager::stopTargetDetection()
{
m_manager->stopTargetDetection();
}

View File

@ -0,0 +1,33 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef NFCMANAGER_H
#define NFCMANAGER_H
#include <QObject>
#include <QQmlEngine>
#include "nfctarget.h"
QT_FORWARD_DECLARE_CLASS(QNearFieldManager);
class NfcManager : public QObject
{
Q_OBJECT
QML_ELEMENT
public:
explicit NfcManager(QObject *parent = nullptr);
Q_INVOKABLE void startTargetDetection();
Q_INVOKABLE void stopTargetDetection();
Q_SIGNALS:
void targetDetected(NfcTarget *target);
private:
QNearFieldManager *m_manager;
};
#endif // NFCMANAGER_H

View File

@ -0,0 +1,31 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "nfctarget.h"
NfcTarget::NfcTarget(QNearFieldTarget *target, QObject *parent) : QObject(parent), m_target(target)
{
target->setParent(this);
connect(target, &QNearFieldTarget::ndefMessageRead, this, &NfcTarget::ndefMessageRead);
connect(target, &QNearFieldTarget::requestCompleted, this, &NfcTarget::requestCompleted);
connect(target, &QNearFieldTarget::error, this, &NfcTarget::error);
}
bool NfcTarget::readNdefMessages()
{
if (m_target.isNull())
return false;
auto req = m_target->readNdefMessages();
return req.isValid();
}
bool NfcTarget::writeNdefMessage(const QNdefMessage &message)
{
if (m_target.isNull())
return false;
auto req = m_target->writeNdefMessages({ message });
return req.isValid();
}

View File

@ -0,0 +1,34 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef NFCTARGET_H
#define NFCTARGET_H
#include <QObject>
#include <QNdefMessage>
#include <QNearFieldTarget>
#include <QQmlEngine>
class NfcTarget : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("Created by NfcManager")
public:
explicit NfcTarget(QNearFieldTarget *target, QObject *parent = nullptr);
Q_INVOKABLE bool readNdefMessages();
Q_INVOKABLE bool writeNdefMessage(const QNdefMessage &message);
Q_SIGNALS:
void ndefMessageRead(const QNdefMessage &message);
void requestCompleted();
void error(QNearFieldTarget::Error error);
private:
QPointer<QNearFieldTarget> m_target;
};
#endif // NFCTARGET_H

View File

@ -0,0 +1,5 @@
module NdefEditor
prefer :/qt/qml/NdefEditor/
Main 1.0 Main.qml
MainWindow 1.0 MainWindow.qml
NdefRecordDelegate 1.0 NdefRecordDelegate.qml

View File

@ -1,44 +0,0 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "textrecordeditor.h"
#include "ui_textrecordeditor.h"
TextRecordEditor::TextRecordEditor(QWidget *parent) :
QWidget(parent),
ui(new Ui::TextRecordEditor)
{
ui->setupUi(this);
}
TextRecordEditor::~TextRecordEditor()
{
delete ui;
}
void TextRecordEditor::setRecord(const QNdefNfcTextRecord &textRecord)
{
ui->text->setText(textRecord.text());
ui->locale->setText(textRecord.locale());
if (textRecord.encoding() == QNdefNfcTextRecord::Utf8)
ui->encoding->setCurrentIndex(0);
else if (textRecord.encoding() == QNdefNfcTextRecord::Utf16)
ui->encoding->setCurrentIndex(1);
}
QNdefNfcTextRecord TextRecordEditor::record() const
{
QNdefNfcTextRecord record;
if (ui->encoding->currentIndex() == 0)
record.setEncoding(QNdefNfcTextRecord::Utf8);
else if (ui->encoding->currentIndex() == 1)
record.setEncoding(QNdefNfcTextRecord::Utf16);
record.setLocale(ui->locale->text());
record.setText(ui->text->text());
return record;
}

View File

@ -1,34 +0,0 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef TEXTRECORDEDITOR_H
#define TEXTRECORDEDITOR_H
#include <QtNfc/qndefnfctextrecord.h>
#include <QtWidgets/QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class TextRecordEditor;
}
QT_END_NAMESPACE
//! [0]
class TextRecordEditor : public QWidget
{
Q_OBJECT
public:
explicit TextRecordEditor(QWidget *parent = 0);
~TextRecordEditor();
void setRecord(const QNdefNfcTextRecord &textRecord);
QNdefNfcTextRecord record() const;
private:
Ui::TextRecordEditor *ui;
};
//! [0]
#endif // TEXTRECORDEDITOR_H

View File

@ -1,101 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TextRecordEditor</class>
<widget class="QWidget" name="TextRecordEditor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>344</width>
<height>115</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="rowWrapPolicy">
<enum>QFormLayout::WrapLongRows</enum>
</property>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_5">
<property name="text">
<string>NFC Text Record</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Text:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="text">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Locale:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="locale">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Encoding:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="encoding">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>UTF-8</string>
</property>
</item>
<item>
<property name="text">
<string>UTF-16</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1,33 +0,0 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "urirecordeditor.h"
#include "ui_urirecordeditor.h"
#include <QtCore/QUrl>
UriRecordEditor::UriRecordEditor(QWidget *parent) :
QWidget(parent),
ui(new Ui::UriRecordEditor)
{
ui->setupUi(this);
}
UriRecordEditor::~UriRecordEditor()
{
delete ui;
}
void UriRecordEditor::setRecord(const QNdefNfcUriRecord &uriRecord)
{
ui->uri->setText(uriRecord.uri().toString());
}
QNdefNfcUriRecord UriRecordEditor::record() const
{
QNdefNfcUriRecord record;
record.setUri(ui->uri->text());
return record;
}

View File

@ -1,33 +0,0 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef URIRECORDEDITOR_H
#define URIRECORDEDITOR_H
#include <QtNfc/qndefnfcurirecord.h>
#include <QtWidgets/QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class UriRecordEditor;
}
QT_END_NAMESPACE
//! [0]
class UriRecordEditor : public QWidget
{
Q_OBJECT
public:
explicit UriRecordEditor(QWidget *parent = 0);
~UriRecordEditor();
void setRecord(const QNdefNfcUriRecord &uriRecord);
QNdefNfcUriRecord record() const;
private:
Ui::UriRecordEditor *ui;
};
//! [0]
#endif // URIRECORDEDITOR_H

View File

@ -1,45 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>UriRecordEditor</class>
<widget class="QWidget" name="UriRecordEditor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>439</width>
<height>59</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_8">
<property name="text">
<string>NFC URI Record</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Uri:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="uri">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1,6 +1,7 @@
TEMPLATE = subdirs
qtHaveModule(widgets) {
SUBDIRS += \
annotatedurl \
ndefeditor
SUBDIRS += annotatedurl
}
qtHaveModule(quickcontrols2) {
SUBDIRS += ndefeditor
}