如何读取 Kivy + Python 中的 NFC 标签?

How to read NFC tag in Kivy + Python?

我正在尝试读取 NFC 标签,如果标签只有文本,则读取正常。但是,如果标签包含 URL 或为空,则它不起作用。我认为问题出在 nfc_filter.xml 文件上。

<intent-filter>
  <action android:name="android.nfc.action.TAG_DISCOVERED"/>
  <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
  <action android:name="android.nfc.action.TECH_DISCOVERED"/>
  <category android:name="android.intent.category.DEFAULT"/>
  <category android:name="android.intent.category.BROWSABLE"/>
  <data android:mimeType="*/*" />
</intent-filter>

Python代码:

def nfc_init(self):
    activity.bind(on_new_intent=self.on_new_intent)
    self.j_context = context = PythonActivity.mActivity
    self.nfc_adapter = NfcAdapter.getDefaultAdapter(context)
    self.nfc_pending_intent = PendingIntent.getActivity(context, 0, Intent(context, context.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0)
    return True

def on_new_intent(self, intent):
    print 'on_new_intent()', intent.getAction()
    # get TAG details
    tag = cast('android.nfc.Tag', intent.getParcelableExtra(NfcAdapter.EXTRA_TAG))
    details = self.get_ndef_details(tag)

def on_pause(self):
    print 'paused'
    return True

def on_resume(self):
    print 'resumed'

我想要的是,当我的应用程序处于活动状态并且您读取了 NFC 标签时,它总是会收到意图。现在我可以在日志中看到它不会从 on_pause 恢复,以防标签包含文本以外的内容或为空。

有人可以帮我解决这个问题吗?

由于清单中的 Intent 过滤器,您的应用目前正在接收 NFC 事件:

<intent-filter>
  <action android:name="android.nfc.action.TAG_DISCOVERED"/>
  <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
  <action android:name="android.nfc.action.TECH_DISCOVERED"/>
  <category android:name="android.intent.category.DEFAULT"/>
  <category android:name="android.intent.category.BROWSABLE"/>
  <data android:mimeType="*/*" />
</intent-filter>

这个 intent 过滤器有几个问题:

  1. 此意图过滤器将匹配具有意图操作 TAG_DISCOVEREDNDEF_DISCOVEREDTECH_DISCOVERED 和 [=88= 的意图] 同时包含类别 DEFAULT 或 BROWSABLE, 同时包含任何 (?) MIME 类型。

    问题是只有 NDEF_DISCOVERED 意图可能包含 MIME 类型。因此,TAG_DISCOVEREDTECH_DISCOVERED 永远不会匹配。

  2. MIME 类型 */*(即匹配任何 MIME 类型)不会(不应该?)在清单意图过滤器中工作,因为只有子类型部分(即斜线)可能包含通配符 (*)。参见 android:mimeType

  3. 类别 BROWSABLE 无用,因为 NFC 意图永远不会包含该类别。

  4. NDEF_DISCOVERED 包含 URL 的标签的意图不包含 MIME 类型。由于您将 NDEF_DISCOVERED 意向过滤器限制为包含 MIME 类型的意向,因此它不会匹配包含 URLs.

    的意向
  5. TECH_DISCOVERED 意图过滤器需要声明技术列表 XML 文件。

因此,您需要更改意图过滤器以匹配您的标签。如果你想匹配任何 NDEF 格式的标签,你可以简单地使用 intent 过滤器:

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

但是,这有一些限制:任何注册了更具体 NDEF_DISCOVERED 意图的应用程序(例如包含 MIME 类型过滤器或 URL 过滤器的应用程序)将优先于您的应用程序,您将不会收到意图。此外,有报告称不带 <data ...>NDEF_DISCOVERED intent 过滤器在某些设备上不起作用。

因此,为了匹配 MIME 类型和 URLs,您可能需要使用更具体的意图过滤器,例如为了匹配所有 text/image/application/ MIME 类型、所有 HTTP(S) URLs 和所有 NFC 论坛外部类型:

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:mimeType="text/*" />
    <data android:mimeType="image/*" />
    <data android:mimeType="application/*" />
</intent-filter>
<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="http" />
    <data android:scheme="https" />
    <data android:scheme="vnd.android.nfc" android:host="ext" android:pathPrefix="/" />
</intent-filter>

不过,如果其他一些应用注册了更具体的 Intent 过滤器,您的应用将不会收到任何符合这些“更具体”标准的 Intent(参见 How NFC Tags are Dispatched to Applications)。

如果您的应用还应收到关于非 NDEF 格式标签的通知,您可以使用 TECH_DISCOVERED 意图过滤器(请注意,无需为此特定意图过滤器指定任何类别)。在这种情况下,您还需要声明一个 XML 资源文件,其中包含应匹配的技术列表(声明必须在 <intent-filter ... /> 元素之外!):

<intent-filter>
    <action android:name="android.nfc.action.TECH_DISCOVERED" />
</intent-filter>
<meta-data android:name="android.nfc.action.TECH_DISCOVERED"
           android:resource="@xml/nfc_tech_filter" />

您还需要 XML 资源 nfc_tech_filter.xml(位于 res/xml/ 下)。为了匹配任何标签,您可以使用:

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.NfcA</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NfcB</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NfcF</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NfcV</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NfcBarcode</tech>
    </tech-list>
</resources>

最后,不要 在清单中使用 TAG_DISCOVERED 意图过滤器,除非您真的了解它的所有含义(特别是在用户体验和用户期望方面)。此 Intent 过滤器只是 API 级别 9(在 Android 2.3.3 之前)的兼容模式,其中 NFC 支持非常非常有限,并且是一种后备模式,可用于创建处理的应用程序任何其他应用程序都不支持的 NFC 标签。

检测前台应用程序中的标签

由于您写道您希望您的应用始终接收这些意图“当它处于活动状态并且您读取 NFC 标签”时,您可能需要考虑从完全显示并改用前台调度系统。在这种情况下,您的应用程序将 not 在读取 NFC 标签时启动,但它会收到 all NFC 发现事件,并且它会优先于所有 个处于前台的其他应用程序。

您可以通过简单地将其添加到您的应用程序中来做到这一点(虽然不太确定 Python 语法):

def on_pause(self):
    print 'paused'
    self.nfc_adapter.disableForegroundDispatch(PythonActivity.mActivity)
    return True

def on_resume(self):
    print 'resumed'
    self.nfc_adapter.enableForegroundDispatch(PythonActivity.mActivity, self.nfc_pending_intent, None, None)