Skip to content

Commit 6c97e1e

Browse files
committed
extended nfc reaction to all possible nfc techs by subscribing ACTION_TAG_DISCOVERED
1 parent 950ef2d commit 6c97e1e

4 files changed

Lines changed: 75 additions & 8 deletions

File tree

app/src/main/AndroidManifest.xml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@
7777
</activity>
7878
<!-- NfcControlActivity processes NFC tags with AAPS commands.
7979
The device must be unlocked before the activity can run.
80-
The intent filter limits it to tags with the specific MIME type. -->
80+
NDEF_DISCOVERED fires for tags carrying the app MIME type.
81+
TAG_DISCOVERED is a fallback for blank tags, finished Libre sensors,
82+
and any other tag whose UID is registered in My Tags. -->
8183
<activity
8284
android:name="app.aaps.plugins.main.general.nfcCommands.NfcControlActivity"
8385
android:exported="true"
@@ -87,6 +89,10 @@
8789
<category android:name="android.intent.category.DEFAULT" />
8890
<data android:mimeType="application/vnd.app.aaps.command" />
8991
</intent-filter>
92+
<intent-filter>
93+
<action android:name="android.nfc.action.TAG_DISCOVERED" />
94+
<category android:name="android.intent.category.DEFAULT" />
95+
</intent-filter>
9096
</activity>
9197

9298
<!-- Receive new BG readings from other local apps -->

plugins/main/src/main/kotlin/app/aaps/plugins/main/general/nfcCommands/NfcControlActivity.kt

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ open class NfcControlActivity : Activity() {
6060
return
6161
}
6262

63-
if (intent == null || NfcAdapter.ACTION_NDEF_DISCOVERED != intent.action) return
63+
if (intent == null) return
6464

6565
// Require a physical Tag object. Only the Android NFC subsystem can supply this;
6666
// an explicit intent crafted by another app cannot forge a real Tag instance,
@@ -73,6 +73,13 @@ open class NfcControlActivity : Activity() {
7373
return
7474
}
7575

76+
when (intent.action) {
77+
NfcAdapter.ACTION_NDEF_DISCOVERED -> handleNdefIntent(intent, nfcTag)
78+
NfcAdapter.ACTION_TAG_DISCOVERED -> handleTagIntent(nfcTag)
79+
}
80+
}
81+
82+
private fun handleNdefIntent(intent: Intent, nfcTag: Tag) {
7683
// getParcelableArrayExtra(String) deprecated in API 33; type-safe overload requires API 33+, minSdk=26
7784
@Suppress("DEPRECATION")
7885
val rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES) ?: return
@@ -90,11 +97,23 @@ open class NfcControlActivity : Activity() {
9097
}
9198

9299
val tagUid = NfcTokenSupport.tagUidHex(nfcTag.id) ?: return
93-
aapsLogger.debug(LTag.NFC, "NFC tag scanned, UID: $tagUid")
100+
executeByUid(tagUid, showErrorToast = true)
101+
}
94102

103+
private fun handleTagIntent(nfcTag: Tag) {
104+
val tagUid = NfcTokenSupport.tagUidHex(nfcTag.id) ?: return
105+
aapsLogger.debug(LTag.NFC, "TAG_DISCOVERED fallback, UID: $tagUid")
106+
// Silently ignore tags not registered in My Tags — TAG_DISCOVERED fires for all tags
107+
// (credit cards, transit cards, etc.) and an error toast for every unknown card is
108+
// intrusive. Only execute if the UID is explicitly registered.
109+
executeByUid(tagUid, showErrorToast = false)
110+
}
111+
112+
private fun executeByUid(tagUid: String, showErrorToast: Boolean) {
113+
aapsLogger.debug(LTag.NFC, "NFC tag scanned, UID: $tagUid")
95114
when (val prep = nfcPlugin.prepareExecution(tagUid)) {
96115
is NfcPrepareResult.Error -> {
97-
showToast(prep.message)
116+
if (showErrorToast) showToast(prep.message)
98117
return
99118
}
100119
is NfcPrepareResult.Ready -> {

plugins/main/src/test/kotlin/app/aaps/plugins/main/general/nfcCommands/NfcCommandsPluginTest.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -813,7 +813,7 @@ class NfcCommandsPluginTest : TestBaseWithProfile() {
813813
)
814814
whenever(rh.gs(eq(R.string.nfccommands_command_executed), any())).thenReturn("Command executed")
815815
whenever(dateUtil.now()).thenReturn(firstNow, callbackNow)
816-
whenever(commandQueue.bolus(any(), any())).thenReturn(true)
816+
817817
val callbackCaptor = argumentCaptor<Callback>()
818818

819819
val result = plugin.executeCommand("BOLUS 1.0")
@@ -840,7 +840,7 @@ class NfcCommandsPluginTest : TestBaseWithProfile() {
840840
)
841841
whenever(rh.gs(eq(R.string.nfccommands_command_executed), any())).thenReturn("Command executed")
842842
whenever(dateUtil.now()).thenReturn(firstNow)
843-
whenever(commandQueue.bolus(any(), any())).thenReturn(true)
843+
844844
val callbackCaptor = argumentCaptor<Callback>()
845845

846846
plugin.executeCommand("BOLUS 1.0")
@@ -865,7 +865,7 @@ class NfcCommandsPluginTest : TestBaseWithProfile() {
865865
app.aaps.core.objects.constraints.ConstraintObject(1.0, aapsLogger),
866866
)
867867
whenever(rh.gs(eq(R.string.nfccommands_command_executed), any())).thenReturn("Command executed")
868-
whenever(commandQueue.bolus(any(), any())).thenReturn(true)
868+
869869
whenever(preferences.get(StringNonKey.TempTargetPresets)).thenReturn(
870870
"""[{"id":"test","reason":"Eating Soon","targetValue":99.0,"duration":2700000,"isDeletable":false}]"""
871871
)
@@ -899,7 +899,7 @@ class NfcCommandsPluginTest : TestBaseWithProfile() {
899899
app.aaps.core.objects.constraints.ConstraintObject(1.0, aapsLogger),
900900
)
901901
whenever(rh.gs(eq(R.string.nfccommands_command_executed), any())).thenReturn("Command executed")
902-
whenever(commandQueue.bolus(any(), any())).thenReturn(true)
902+
903903
runTest {
904904
whenever(profileFunction.getProfile()).thenReturn(null)
905905
}

plugins/main/src/test/kotlin/app/aaps/plugins/main/general/nfcCommands/NfcControlActivityTest.kt

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,40 @@ class NfcControlActivityTest : TestBaseWithProfile() {
142142
verify(activity).appendReadLogEntry(org.mockito.kotlin.eq(uid), any())
143143
}
144144

145+
@Test
146+
fun `handleIntent executes commands on TAG_DISCOVERED for registered UID`() {
147+
val uid = NfcTokenSupport.tagUidHex(fakeUid)!!
148+
whenever(nfcPlugin.prepareExecution(uid))
149+
.thenReturn(NfcPrepareResult.Ready(uid, listOf("LOOP STOP")))
150+
whenever(nfcPlugin.executeCascade(listOf("LOOP STOP")))
151+
.thenReturn(NfcExecutionResult(true, "ok"))
152+
153+
activity.handleIntent(createTagDiscoveredIntent(mockNfcTag(fakeUid)))
154+
155+
verify(nfcPlugin).prepareExecution(uid)
156+
verify(nfcPlugin).executeCascade(listOf("LOOP STOP"))
157+
}
158+
159+
@Test
160+
fun `handleIntent does nothing silently on TAG_DISCOVERED for unregistered UID`() {
161+
val uid = NfcTokenSupport.tagUidHex(fakeUid)!!
162+
whenever(nfcPlugin.prepareExecution(uid))
163+
.thenReturn(NfcPrepareResult.Error("not registered"))
164+
165+
activity.handleIntent(createTagDiscoveredIntent(mockNfcTag(fakeUid)))
166+
167+
verify(nfcPlugin).prepareExecution(uid)
168+
verify(nfcPlugin, never()).executeCascade(any())
169+
}
170+
171+
@Test
172+
fun `handleIntent does nothing on TAG_DISCOVERED without physical tag extra`() {
173+
activity.handleIntent(createTagDiscoveredIntent(nfcTag = null))
174+
175+
verify(nfcPlugin, never()).prepareExecution(any())
176+
verify(nfcPlugin, never()).executeCascade(any())
177+
}
178+
145179
// ── helpers ────────────────────────────────────────────────────────────
146180

147181
private fun mockNfcTag(uid: ByteArray): Tag {
@@ -166,4 +200,12 @@ class NfcControlActivityTest : TestBaseWithProfile() {
166200
whenever(intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)).thenReturn(nfcTag)
167201
return intent
168202
}
203+
204+
private fun createTagDiscoveredIntent(nfcTag: Tag? = mockNfcTag(fakeUid)): Intent {
205+
val intent = mock<Intent>()
206+
whenever(intent.action).thenReturn(NfcAdapter.ACTION_TAG_DISCOVERED)
207+
@Suppress("DEPRECATION")
208+
whenever(intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)).thenReturn(nfcTag)
209+
return intent
210+
}
169211
}

0 commit comments

Comments
 (0)