Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qnearfieldmanager_ios.mm
Go to the documentation of this file.
1// Copyright (C) 2020 Governikus GmbH & Co. KG
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include "ios/qiosnfcndefsessiondelegate_p.h"
7#include "ios/qiostagreaderdelegate_p.h"
8#include "ios/qiosndefnotifier_p.h"
9
11
12#include <QDateTime>
13
14#include <memory>
15
16#import <CoreNFC/NFCReaderSession.h>
17#import <CoreNFC/NFCNDEFReaderSession.h>
18#import <CoreNFC/NFCTagReaderSession.h>
19
21
22QNearFieldManagerPrivateImpl::QNearFieldManagerPrivateImpl()
23{
24 auto notifier = std::make_unique<QNfcNdefNotifier>();
25
26 if (@available(iOS 13, *))
27 delegate = [[QT_MANGLE_NAMESPACE(QIosTagReaderDelegate) alloc] initWithListener:this];
28
29 connect(this, &QNearFieldManagerPrivateImpl::tagDiscovered,
30 this, &QNearFieldManagerPrivateImpl::onTagDiscovered,
31 Qt::QueuedConnection);
32 connect(this, &QNearFieldManagerPrivateImpl::didInvalidateWithError,
33 this, &QNearFieldManagerPrivateImpl::onDidInvalidateWithError,
34 Qt::QueuedConnection);
35
36 ndefDelegate = [[QIosNfcNdefSessionDelegate alloc] initWithNotifier:notifier.get()];
37 if (ndefDelegate) {
38 auto watchDog = notifier.release(); // Delegate took the ownership.
39
40 connect(watchDog, &QNfcNdefNotifier::tagDetected,
41 this, &QNearFieldManagerPrivateImpl::onTagDiscovered,
42 Qt::QueuedConnection);
43 connect(watchDog, &QNfcNdefNotifier::invalidateWithError,
44 this, &QNearFieldManagerPrivateImpl::onDidInvalidateWithError,
45 Qt::QueuedConnection);
46 } else {
47 qCWarning(QT_IOS_NFC, "Failed to allocate NDEF reading session's delegate");
48 }
49
50 sessionTimer.setInterval(2000);
51 sessionTimer.setSingleShot(true);
52 connect(&sessionTimer, &QTimer::timeout, this, &QNearFieldManagerPrivateImpl::onSessionTimer);
53}
54
56{
57 if (@available(iOS 13, *))
58 [delegate release];
59
60 if (!ndefDelegate)
61 return;
62
63 if (auto queue = qt_Nfc_Queue()) {
64 dispatch_sync(queue, ^{
65 [ndefDelegate abort];
66 });
67 }
68
69 [ndefDelegate release];
70}
71
72bool QNearFieldManagerPrivateImpl::isSupported(QNearFieldTarget::AccessMethod accessMethod) const
73{
74 switch (accessMethod) {
75 case QNearFieldTarget::AnyAccess:
76 case QNearFieldTarget::NdefAccess:
77 return NFCNDEFReaderSession.readingAvailable;
78 case QNearFieldTarget::TagTypeSpecificAccess:
79 if (@available(iOS 13, *))
80 return NFCTagReaderSession.readingAvailable;
81 Q_FALLTHROUGH();
82 case QNearFieldTarget::UnknownAccess:
83 return false;
84 }
85}
86
87bool QNearFieldManagerPrivateImpl::startTargetDetection(QNearFieldTarget::AccessMethod accessMethod)
88{
89 if (detectionRunning)
90 return false;
91
92 activeAccessMethod = QNearFieldTarget::UnknownAccess;
93
94 switch (accessMethod) {
95 case QNearFieldTarget::UnknownAccess:
96 case QNearFieldTarget::AnyAccess:
97 return false;
98 case QNearFieldTarget::TagTypeSpecificAccess:
99 if (@available(iOS 13, *))
100 if (NFCTagReaderSession.readingAvailable) {
101 detectionRunning = scheduleSession(accessMethod);
102 if (detectionRunning)
103 activeAccessMethod = accessMethod;
104 return detectionRunning;
105 }
106 return false;
107 case QNearFieldTarget::NdefAccess:
108 if (NFCNDEFReaderSession.readingAvailable) {
109 detectionRunning = scheduleSession(accessMethod);
110 if (detectionRunning)
111 activeAccessMethod = accessMethod;
112 return detectionRunning;
113 }
114 return false;
115 }
116
117 return false;
118}
119
120void QNearFieldManagerPrivateImpl::stopTargetDetection(const QString &errorMessage)
121{
122 if (!detectionRunning)
123 return;
124
125 isSessionScheduled = false;
126
127 if (activeAccessMethod == QNearFieldTarget::TagTypeSpecificAccess) {
128 stopSession(errorMessage);
129 } else if (activeAccessMethod == QNearFieldTarget::NdefAccess) {
130 stopNdefSession(errorMessage);
131 } else {
132 qCWarning(QT_IOS_NFC, "Unknown access method, cannot stop target detection");
133 return;
134 }
135
136 detectionRunning = false;
137 Q_EMIT targetDetectionStopped();
138}
139
140bool QNearFieldManagerPrivateImpl::scheduleSession(QNearFieldTarget::AccessMethod accessMethod)
141{
142 if (sessionTimer.isActive()) {
143 isSessionScheduled = true;
144 return true;
145 }
146 isSessionScheduled = false;
147
148 if (accessMethod == QNearFieldTarget::TagTypeSpecificAccess) {
149 startSession();
150 return true;
151 } else if (accessMethod == QNearFieldTarget::NdefAccess) {
152 return startNdefSession();
153 }
154
155 return false;
156}
157
158void QNearFieldManagerPrivateImpl::startSession()
159{
160 if (@available(iOS 13, *)) {
161 [delegate startSession];
162 }
163}
164
165bool QNearFieldManagerPrivateImpl::startNdefSession()
166{
167 if (!ndefDelegate)
168 return false;
169
170 if (auto queue = qt_Nfc_Queue()) {
171 __block bool startSessionSucceded = false;
172 dispatch_sync(queue, ^{ startSessionSucceded = [ndefDelegate startSession]; });
173 return startSessionSucceded;
174 }
175
176 return false;
177}
178
179void QNearFieldManagerPrivateImpl::stopSession(const QString &error)
180{
181 Q_ASSERT(activeAccessMethod == QNearFieldTarget::TagTypeSpecificAccess);
182
183 clearTargets();
184
185 if (@available(iOS 13, *)) {
186 [delegate stopSession:error];
187 }
188}
189
190void QNearFieldManagerPrivateImpl::stopNdefSession(const QString &error)
191{
192 Q_ASSERT(activeAccessMethod == QNearFieldTarget::NdefAccess);
193
194 clearTargets();
195
196 if (auto queue = qt_Nfc_Queue()) {
197 dispatch_sync(queue, ^{
198 [ndefDelegate stopSession:error];
199 });
200 }
201}
202
203void QNearFieldManagerPrivateImpl::clearTargets()
204{
205 auto i = detectedTargets.begin();
206 while (i != detectedTargets.end()) {
207 if (*i)
208 (*i)->invalidate();
209 else
210 qCWarning(QT_IOS_NFC, "Stale private near field target found");
211 i = detectedTargets.erase(i);
212 }
213}
214
215
216void QNearFieldManagerPrivateImpl::setUserInformation(const QString &message)
217{
218 if (activeAccessMethod != QNearFieldTarget::NdefAccess)
219 [delegate alertMessage:message];
220
221 if (detectionRunning) {
222 // Too late!
223 qCWarning(QT_IOS_NFC,
224 "User information must be set prior before the target detection started");
225 return;
226 }
227
228 if (auto queue = qt_Nfc_Queue()) {
229 dispatch_sync(queue, ^{
230 [ndefDelegate setAlertMessage:message];
231 });
232 }
233}
234
235void QNearFieldManagerPrivateImpl::onTagDiscovered(void *tag)
236{
237 QNearFieldTargetPrivateImpl *target = nullptr;
238 if (activeAccessMethod == QNearFieldTarget::NdefAccess) {
239 [id(tag) retain];
240 target = new QNearFieldTargetPrivateImpl(ndefDelegate, tag);
241 } else {
242 target = new QNearFieldTargetPrivateImpl(tag);
243 }
244
245 detectedTargets += target;
246
247 connect(target, &QNearFieldTargetPrivateImpl::targetLost,
248 this, &QNearFieldManagerPrivateImpl::onTargetLost);
249 Q_EMIT targetDetected(new QNearFieldTarget(target, this));
250}
251
253{
254 detectedTargets.removeOne(target);
255 Q_EMIT targetLost(target->q_ptr);
256
257 if (detectionRunning && detectedTargets.isEmpty())
258 onDidInvalidateWithError(true);
259}
260
261void QNearFieldManagerPrivateImpl::onDidInvalidateWithError(bool doRestart)
262{
263 clearTargets();
264 sessionTimer.start();
265
266 if (detectionRunning && doRestart && scheduleSession(activeAccessMethod))
267 return;
268
269 detectionRunning = false;
270 Q_EMIT targetDetectionStopped();
271}
272
273void QNearFieldManagerPrivateImpl::onSessionTimer()
274{
275 if (isSessionScheduled && !scheduleSession(activeAccessMethod)) {
276 detectionRunning = false;
277 Q_EMIT targetDetectionStopped();
278 }
279}
280
281QT_END_NAMESPACE
bool isSupported(QNearFieldTarget::AccessMethod accessMethod) const override
bool startTargetDetection(QNearFieldTarget::AccessMethod accessMethod) override