diff options
Diffstat (limited to 'examples/nfc')
59 files changed, 1027 insertions, 1490 deletions
diff --git a/examples/nfc/CMakeLists.txt b/examples/nfc/CMakeLists.txt index 68488355..47bc9416 100644 --- a/examples/nfc/CMakeLists.txt +++ b/examples/nfc/CMakeLists.txt @@ -3,5 +3,7 @@ if(TARGET Qt::Widgets) qt_internal_add_example(annotatedurl) +endif() +if(TARGET Qt::QuickControls2) qt_internal_add_example(ndefeditor) endif() diff --git a/examples/nfc/ndefeditor/CMakeLists.txt b/examples/nfc/ndefeditor/CMakeLists.txt index 2d7bb60b..a3b2c80c 100644 --- a/examples/nfc/ndefeditor/CMakeLists.txt +++ b/examples/nfc/ndefeditor/CMakeLists.txt @@ -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}" diff --git a/examples/nfc/ndefeditor/Info.cmake.plist b/examples/nfc/ndefeditor/Info.cmake.plist new file mode 100644 index 00000000..ee87b3b2 --- /dev/null +++ b/examples/nfc/ndefeditor/Info.cmake.plist @@ -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> diff --git a/examples/nfc/ndefeditor/Info.plist b/examples/nfc/ndefeditor/Info.plist deleted file mode 100644 index b567f11a..00000000 --- a/examples/nfc/ndefeditor/Info.plist +++ /dev/null @@ -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> diff --git a/examples/nfc/ndefeditor/Info.qmake.plist b/examples/nfc/ndefeditor/Info.qmake.plist new file mode 100644 index 00000000..079533e6 --- /dev/null +++ b/examples/nfc/ndefeditor/Info.qmake.plist @@ -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> diff --git a/examples/nfc/ndefeditor/Main.qml b/examples/nfc/ndefeditor/Main.qml new file mode 100644 index 00000000..c5435e26 --- /dev/null +++ b/examples/nfc/ndefeditor/Main.qml @@ -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") +} diff --git a/examples/nfc/ndefeditor/MainWindow.qml b/examples/nfc/ndefeditor/MainWindow.qml new file mode 100644 index 00000000..0dc5ee46 --- /dev/null +++ b/examples/nfc/ndefeditor/MainWindow.qml @@ -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 + } + } + } + } + } +} diff --git a/examples/nfc/ndefeditor/NdefRecordDelegate.qml b/examples/nfc/ndefeditor/NdefRecordDelegate.qml new file mode 100644 index 00000000..f01aff05 --- /dev/null +++ b/examples/nfc/ndefeditor/NdefRecordDelegate.qml @@ -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() +} diff --git a/examples/nfc/ndefeditor/android/AndroidManifest.xml b/examples/nfc/ndefeditor/android/AndroidManifest.xml deleted file mode 100644 index ee3ae1b7..00000000 --- a/examples/nfc/ndefeditor/android/AndroidManifest.xml +++ /dev/null @@ -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> diff --git a/examples/nfc/ndefeditor/doc/images/ndefeditor.png b/examples/nfc/ndefeditor/doc/images/ndefeditor.png Binary files differindex 9ce8ade5..2be32f60 100644 --- a/examples/nfc/ndefeditor/doc/images/ndefeditor.png +++ b/examples/nfc/ndefeditor/doc/images/ndefeditor.png diff --git a/examples/nfc/ndefeditor/doc/src/ndefeditor.qdoc b/examples/nfc/ndefeditor/doc/src/ndefeditor.qdoc index 2448b3e5..48afc35d 100644 --- a/examples/nfc/ndefeditor/doc/src/ndefeditor.qdoc +++ b/examples/nfc/ndefeditor/doc/src/ndefeditor.qdoc @@ -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} */ diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20/add.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20/add.png Binary files differnew file mode 100644 index 00000000..49659bf3 --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20/add.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20/arrow_back.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20/arrow_back.png Binary files differnew file mode 100644 index 00000000..8d39f677 --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20/arrow_back.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20/file_download.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20/file_download.png Binary files differnew file mode 100644 index 00000000..405a67b0 --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20/file_download.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20/file_upload.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20/file_upload.png Binary files differnew file mode 100644 index 00000000..7a13c507 --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20/file_upload.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20/link.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20/link.png Binary files differnew file mode 100644 index 00000000..6d663387 --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20/link.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20/text_snippet.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20/text_snippet.png Binary files differnew file mode 100644 index 00000000..bada4338 --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20/text_snippet.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20@2/add.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@2/add.png Binary files differnew file mode 100644 index 00000000..8114d615 --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@2/add.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20@2/arrow_back.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@2/arrow_back.png Binary files differnew file mode 100644 index 00000000..843444e2 --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@2/arrow_back.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20@2/file_download.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@2/file_download.png Binary files differnew file mode 100644 index 00000000..d848b217 --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@2/file_download.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20@2/file_upload.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@2/file_upload.png Binary files differnew file mode 100644 index 00000000..22364309 --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@2/file_upload.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20@2/link.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@2/link.png Binary files differnew file mode 100644 index 00000000..0567312a --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@2/link.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20@2/text_snippet.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@2/text_snippet.png Binary files differnew file mode 100644 index 00000000..96ab459a --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@2/text_snippet.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20@3/add.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@3/add.png Binary files differnew file mode 100644 index 00000000..c55d4144 --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@3/add.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20@3/arrow_back.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@3/arrow_back.png Binary files differnew file mode 100644 index 00000000..bb620d68 --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@3/arrow_back.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20@3/file_download.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@3/file_download.png Binary files differnew file mode 100644 index 00000000..db8e7b13 --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@3/file_download.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20@3/file_upload.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@3/file_upload.png Binary files differnew file mode 100644 index 00000000..1b240284 --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@3/file_upload.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20@3/link.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@3/link.png Binary files differnew file mode 100644 index 00000000..e0569cbb --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@3/link.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20@3/text_snippet.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@3/text_snippet.png Binary files differnew file mode 100644 index 00000000..9dc6683f --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@3/text_snippet.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20@4/add.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@4/add.png Binary files differnew file mode 100644 index 00000000..4813a26f --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@4/add.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20@4/arrow_back.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@4/arrow_back.png Binary files differnew file mode 100644 index 00000000..54354ddd --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@4/arrow_back.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20@4/file_download.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@4/file_download.png Binary files differnew file mode 100644 index 00000000..20148b92 --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@4/file_download.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20@4/file_upload.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@4/file_upload.png Binary files differnew file mode 100644 index 00000000..22a98cc2 --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@4/file_upload.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20@4/link.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@4/link.png Binary files differnew file mode 100644 index 00000000..f8bedeef --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@4/link.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/20x20@4/text_snippet.png b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@4/text_snippet.png Binary files differnew file mode 100644 index 00000000..74a5f7fe --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/20x20@4/text_snippet.png diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/index.theme b/examples/nfc/ndefeditor/icons/ndefeditor/index.theme new file mode 100644 index 00000000..756b1232 --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/index.theme @@ -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 diff --git a/examples/nfc/ndefeditor/icons/ndefeditor/qt_attribution.json b/examples/nfc/ndefeditor/icons/ndefeditor/qt_attribution.json new file mode 100644 index 00000000..974e9dbc --- /dev/null +++ b/examples/nfc/ndefeditor/icons/ndefeditor/qt_attribution.json @@ -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." +} diff --git a/examples/nfc/ndefeditor/main.cpp b/examples/nfc/ndefeditor/main.cpp index f76d38b4..a9e5218d 100644 --- a/examples/nfc/ndefeditor/main.cpp +++ b/examples/nfc/ndefeditor/main.cpp @@ -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(); } diff --git a/examples/nfc/ndefeditor/mainwindow.cpp b/examples/nfc/ndefeditor/mainwindow.cpp deleted file mode 100644 index aa5b79a0..00000000 --- a/examples/nfc/ndefeditor/mainwindow.cpp +++ /dev/null @@ -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" diff --git a/examples/nfc/ndefeditor/mainwindow.h b/examples/nfc/ndefeditor/mainwindow.h deleted file mode 100644 index 8a60a1a1..00000000 --- a/examples/nfc/ndefeditor/mainwindow.h +++ /dev/null @@ -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 diff --git a/examples/nfc/ndefeditor/mainwindow.ui b/examples/nfc/ndefeditor/mainwindow.ui deleted file mode 100644 index cb8c2c35..00000000 --- a/examples/nfc/ndefeditor/mainwindow.ui +++ /dev/null @@ -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> diff --git a/examples/nfc/ndefeditor/mimeimagerecordeditor.cpp b/examples/nfc/ndefeditor/mimeimagerecordeditor.cpp deleted file mode 100644 index a5109757..00000000 --- a/examples/nfc/ndefeditor/mimeimagerecordeditor.cpp +++ /dev/null @@ -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); -} diff --git a/examples/nfc/ndefeditor/mimeimagerecordeditor.h b/examples/nfc/ndefeditor/mimeimagerecordeditor.h deleted file mode 100644 index 80c79c00..00000000 --- a/examples/nfc/ndefeditor/mimeimagerecordeditor.h +++ /dev/null @@ -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 diff --git a/examples/nfc/ndefeditor/mimeimagerecordeditor.ui b/examples/nfc/ndefeditor/mimeimagerecordeditor.ui deleted file mode 100644 index 6403972b..00000000 --- a/examples/nfc/ndefeditor/mimeimagerecordeditor.ui +++ /dev/null @@ -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 <image/*></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> diff --git a/examples/nfc/ndefeditor/ndefeditor.pro b/examples/nfc/ndefeditor/ndefeditor.pro index 1ab9724a..5e2a80c7 100644 --- a/examples/nfc/ndefeditor/ndefeditor.pro +++ b/examples/nfc/ndefeditor/ndefeditor.pro @@ -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 + +qml_resources.files = \ + qmldir \ + Main.qml \ + MainWindow.qml \ + NdefRecordDelegate.qml + +qml_resources.prefix = /qt/qml/NdefEditor -ios: QMAKE_INFO_PLIST = Info.plist +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 -FORMS += \ - mainwindow.ui \ - textrecordeditor.ui \ - urirecordeditor.ui \ - mimeimagerecordeditor.ui +theme_resources.prefix = / -android { - ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android +RESOURCES += qml_resources theme_resources - DISTFILES += \ - android/AndroidManifest.xml -} +ios: QMAKE_INFO_PLIST = Info.qmake.plist target.path = $$[QT_INSTALL_EXAMPLES]/nfc/ndefeditor INSTALLS += target diff --git a/examples/nfc/ndefeditor/ndefmessagemodel.cpp b/examples/nfc/ndefeditor/ndefmessagemodel.cpp new file mode 100644 index 00000000..177d21dc --- /dev/null +++ b/examples/nfc/ndefeditor/ndefmessagemodel.cpp @@ -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(); +} diff --git a/examples/nfc/ndefeditor/ndefmessagemodel.h b/examples/nfc/ndefeditor/ndefmessagemodel.h new file mode 100644 index 00000000..5f9ddf24 --- /dev/null +++ b/examples/nfc/ndefeditor/ndefmessagemodel.h @@ -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 diff --git a/examples/nfc/ndefeditor/nfcmanager.cpp b/examples/nfc/ndefeditor/nfcmanager.cpp new file mode 100644 index 00000000..bc09b89e --- /dev/null +++ b/examples/nfc/ndefeditor/nfcmanager.cpp @@ -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(); +} diff --git a/examples/nfc/ndefeditor/nfcmanager.h b/examples/nfc/ndefeditor/nfcmanager.h new file mode 100644 index 00000000..b30f6f36 --- /dev/null +++ b/examples/nfc/ndefeditor/nfcmanager.h @@ -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 diff --git a/examples/nfc/ndefeditor/nfctarget.cpp b/examples/nfc/ndefeditor/nfctarget.cpp new file mode 100644 index 00000000..68b26df5 --- /dev/null +++ b/examples/nfc/ndefeditor/nfctarget.cpp @@ -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(); +} diff --git a/examples/nfc/ndefeditor/nfctarget.h b/examples/nfc/ndefeditor/nfctarget.h new file mode 100644 index 00000000..8631687c --- /dev/null +++ b/examples/nfc/ndefeditor/nfctarget.h @@ -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 diff --git a/examples/nfc/ndefeditor/qmldir b/examples/nfc/ndefeditor/qmldir new file mode 100644 index 00000000..0a77d792 --- /dev/null +++ b/examples/nfc/ndefeditor/qmldir @@ -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 diff --git a/examples/nfc/ndefeditor/textrecordeditor.cpp b/examples/nfc/ndefeditor/textrecordeditor.cpp deleted file mode 100644 index 0657a212..00000000 --- a/examples/nfc/ndefeditor/textrecordeditor.cpp +++ /dev/null @@ -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; -} diff --git a/examples/nfc/ndefeditor/textrecordeditor.h b/examples/nfc/ndefeditor/textrecordeditor.h deleted file mode 100644 index 0289ab85..00000000 --- a/examples/nfc/ndefeditor/textrecordeditor.h +++ /dev/null @@ -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 diff --git a/examples/nfc/ndefeditor/textrecordeditor.ui b/examples/nfc/ndefeditor/textrecordeditor.ui deleted file mode 100644 index 41950e7d..00000000 --- a/examples/nfc/ndefeditor/textrecordeditor.ui +++ /dev/null @@ -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> diff --git a/examples/nfc/ndefeditor/urirecordeditor.cpp b/examples/nfc/ndefeditor/urirecordeditor.cpp deleted file mode 100644 index 43ed23f6..00000000 --- a/examples/nfc/ndefeditor/urirecordeditor.cpp +++ /dev/null @@ -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; -} diff --git a/examples/nfc/ndefeditor/urirecordeditor.h b/examples/nfc/ndefeditor/urirecordeditor.h deleted file mode 100644 index 2a2a3736..00000000 --- a/examples/nfc/ndefeditor/urirecordeditor.h +++ /dev/null @@ -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 diff --git a/examples/nfc/ndefeditor/urirecordeditor.ui b/examples/nfc/ndefeditor/urirecordeditor.ui deleted file mode 100644 index 929c1eb4..00000000 --- a/examples/nfc/ndefeditor/urirecordeditor.ui +++ /dev/null @@ -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> diff --git a/examples/nfc/nfc.pro b/examples/nfc/nfc.pro index f76426de..49d54525 100644 --- a/examples/nfc/nfc.pro +++ b/examples/nfc/nfc.pro @@ -1,6 +1,7 @@ TEMPLATE = subdirs qtHaveModule(widgets) { - SUBDIRS += \ - annotatedurl \ - ndefeditor + SUBDIRS += annotatedurl +} +qtHaveModule(quickcontrols2) { + SUBDIRS += ndefeditor } |