8 changed files with 236 additions and 55 deletions
@ -1,86 +1,243 @@ |
|||||||
|
'''This is the Android implementatoin of NFC Scanning using the |
||||||
|
built in NFC adapter of some android phones. |
||||||
|
''' |
||||||
|
|
||||||
|
from kivy.app import App |
||||||
|
from kivy.clock import Clock |
||||||
|
#Detect which platform we are on |
||||||
from kivy.utils import platform |
from kivy.utils import platform |
||||||
if platform != 'android': |
if platform != 'android': |
||||||
raise ImportError |
raise ImportError |
||||||
|
import threading |
||||||
|
|
||||||
from electrum_gui.kivy.nfc_scanner import NFCBase |
from electrum_gui.kivy.nfc_scanner import NFCBase |
||||||
from jnius import autoclass, cast |
from jnius import autoclass, cast |
||||||
from android.runnable import run_on_ui_thread |
from android.runnable import run_on_ui_thread |
||||||
from android import activity |
from android import activity |
||||||
|
|
||||||
|
BUILDVERSION = autoclass('android.os.Build$VERSION').SDK_INT |
||||||
NfcAdapter = autoclass('android.nfc.NfcAdapter') |
NfcAdapter = autoclass('android.nfc.NfcAdapter') |
||||||
PythonActivity = autoclass('org.renpy.android.PythonActivity') |
PythonActivity = autoclass('org.renpy.android.PythonActivity') |
||||||
|
JString = autoclass('java.lang.String') |
||||||
|
Charset = autoclass('java.nio.charset.Charset') |
||||||
|
locale = autoclass('java.util.Locale') |
||||||
Intent = autoclass('android.content.Intent') |
Intent = autoclass('android.content.Intent') |
||||||
IntentFilter = autoclass('android.content.IntentFilter') |
IntentFilter = autoclass('android.content.IntentFilter') |
||||||
PendingIntent = autoclass('android.app.PendingIntent') |
PendingIntent = autoclass('android.app.PendingIntent') |
||||||
|
Ndef = autoclass('android.nfc.tech.Ndef') |
||||||
NdefRecord = autoclass('android.nfc.NdefRecord') |
NdefRecord = autoclass('android.nfc.NdefRecord') |
||||||
NdefMessage = autoclass('android.nfc.NdefMessage') |
NdefMessage = autoclass('android.nfc.NdefMessage') |
||||||
|
|
||||||
|
app = None |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ScannerAndroid(NFCBase): |
class ScannerAndroid(NFCBase): |
||||||
|
''' This is the class responsible for handling the interace with the |
||||||
|
Android NFC adapter. See Module Documentation for deatils. |
||||||
|
''' |
||||||
|
|
||||||
|
name = 'NFCAndroid' |
||||||
|
|
||||||
def nfc_init(self): |
def nfc_init(self): |
||||||
# print 'nfc_init()' |
''' This is where we initialize NFC adapter. |
||||||
|
''' |
||||||
|
# Initialize NFC |
||||||
|
global app |
||||||
|
app = App.get_running_app() |
||||||
|
|
||||||
# print 'configure nfc' |
# Make sure we are listening to new intent |
||||||
|
activity.bind(on_new_intent=self.on_new_intent) |
||||||
|
|
||||||
|
# Configure nfc |
||||||
self.j_context = context = PythonActivity.mActivity |
self.j_context = context = PythonActivity.mActivity |
||||||
self.nfc_adapter = NfcAdapter.getDefaultAdapter(context) |
self.nfc_adapter = NfcAdapter.getDefaultAdapter(context) |
||||||
|
# Check if adapter exists |
||||||
|
if not self.nfc_adapter: |
||||||
|
return False |
||||||
|
|
||||||
|
# specify that we want our activity to remain on top whan a new intent |
||||||
|
# is fired |
||||||
self.nfc_pending_intent = PendingIntent.getActivity(context, 0, |
self.nfc_pending_intent = PendingIntent.getActivity(context, 0, |
||||||
Intent(context, context.getClass()).addFlags( |
Intent(context, context.getClass()).addFlags( |
||||||
Intent.FLAG_ACTIVITY_SINGLE_TOP), 0) |
Intent.FLAG_ACTIVITY_SINGLE_TOP), 0) |
||||||
|
|
||||||
# print 'p2p filter' |
# Filter for different types of action, by default we enable all. |
||||||
|
# These are only for handling different NFC technologies when app is in foreground |
||||||
self.ndef_detected = IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED) |
self.ndef_detected = IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED) |
||||||
self.ndef_detected.addDataType('text/plain') |
#self.tech_detected = IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED) |
||||||
self.ndef_exchange_filters = [self.ndef_detected] |
#self.tag_detected = IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED) |
||||||
|
|
||||||
|
# setup tag discovery for ourt tag type |
||||||
|
try: |
||||||
|
self.ndef_detected.addCategory(Intent.CATEGORY_DEFAULT) |
||||||
|
# setup the foreground dispatch to detect all mime types |
||||||
|
self.ndef_detected.addDataType('*/*') |
||||||
|
|
||||||
|
self.ndef_exchange_filters = [self.ndef_detected] |
||||||
|
except Exception as err: |
||||||
|
raise Exception(repr(err)) |
||||||
|
return True |
||||||
|
|
||||||
|
def get_ndef_details(self, tag): |
||||||
|
''' Get all the details from the tag. |
||||||
|
''' |
||||||
|
details = {} |
||||||
|
|
||||||
|
try: |
||||||
|
#print 'id' |
||||||
|
details['uid'] = ':'.join(['{:02x}'.format(bt & 0xff) for bt in tag.getId()]) |
||||||
|
#print 'technologies' |
||||||
|
details['Technologies'] = tech_list = [tech.split('.')[-1] for tech in tag.getTechList()] |
||||||
|
#print 'get NDEF tag details' |
||||||
|
ndefTag = cast('android.nfc.tech.Ndef', Ndef.get(tag)) |
||||||
|
#print 'tag size' |
||||||
|
details['MaxSize'] = ndefTag.getMaxSize() |
||||||
|
#details['usedSize'] = '0' |
||||||
|
#print 'is tag writable?' |
||||||
|
details['writable'] = ndefTag.isWritable() |
||||||
|
#print 'Data format' |
||||||
|
# Can be made readonly |
||||||
|
# get NDEF message details |
||||||
|
ndefMesg = ndefTag.getCachedNdefMessage() |
||||||
|
# get size of current records |
||||||
|
details['consumed'] = len(ndefMesg.toByteArray()) |
||||||
|
#print 'tag type' |
||||||
|
details['Type'] = ndefTag.getType() |
||||||
|
|
||||||
|
# check if tag is empty |
||||||
|
if not ndefMesg: |
||||||
|
details['Message'] = None |
||||||
|
return details |
||||||
|
|
||||||
|
ndefrecords = ndefMesg.getRecords() |
||||||
|
length = len(ndefrecords) |
||||||
|
#print 'length', length |
||||||
|
# will contain the NDEF record types |
||||||
|
recTypes = [] |
||||||
|
self. |
||||||
|
for record in ndefrecords: |
||||||
|
recTypes.append({ |
||||||
|
'type': ''.join(map(unichr, record.getType())), |
||||||
|
'payload': ''.join(map(unichr, record.getPayload())) |
||||||
|
}) |
||||||
|
|
||||||
|
details['recTypes'] = recTypes |
||||||
|
except Exception as err: |
||||||
|
print str(err) |
||||||
|
|
||||||
|
return details |
||||||
|
|
||||||
def on_new_intent(self, intent): |
def on_new_intent(self, intent): |
||||||
# print 'on_new_intent()', intent.getAction() |
''' This functions is called when the application receives a |
||||||
if intent.getAction() != NfcAdapter.ACTION_NDEF_DISCOVERED: |
new intent, for the ones the application has registered previously, |
||||||
# print 'unknow action, avoid.' |
either in the manifest or in the foreground dispatch setup in the |
||||||
|
nfc_init function above. |
||||||
|
''' |
||||||
|
|
||||||
|
action_list = (NfcAdapter.ACTION_NDEF_DISCOVERED,) |
||||||
|
# get TAG |
||||||
|
#tag = cast('android.nfc.Tag', intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)) |
||||||
|
|
||||||
|
#details = self.get_ndef_details(tag) |
||||||
|
|
||||||
|
if intent.getAction() not in action_list: |
||||||
|
print 'unknow action, avoid.' |
||||||
return |
return |
||||||
|
|
||||||
rawmsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES) |
rawmsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES) |
||||||
# print 'raw messages', rawmsgs |
|
||||||
if not rawmsgs: |
if not rawmsgs: |
||||||
return |
return |
||||||
|
|
||||||
for message in rawmsgs: |
for message in rawmsgs: |
||||||
message = cast(NdefMessage, message) |
message = cast(NdefMessage, message) |
||||||
# print 'got message', message |
|
||||||
payload = message.getRecords()[0].getPayload() |
payload = message.getRecords()[0].getPayload() |
||||||
self.payload = payload |
|
||||||
print 'payload: {}'.format(''.join(map(chr, payload))) |
print 'payload: {}'.format(''.join(map(chr, payload))) |
||||||
|
|
||||||
def nfc_disable(self): |
def nfc_disable(self): |
||||||
# print 'nfc_enable()' |
'''Disable app from handling tags. |
||||||
activity.bind(on_new_intent=self.on_new_intent) |
''' |
||||||
|
self.disable_foreground_dispatch() |
||||||
|
|
||||||
def nfc_enable(self): |
def nfc_enable(self): |
||||||
# print 'nfc_enable()' |
'''Enable app to handle tags when app in foreground. |
||||||
activity.bind(on_new_intent=self.on_new_intent) |
''' |
||||||
|
self.enable_foreground_dispatch() |
||||||
|
|
||||||
|
def create_AAR(self): |
||||||
|
'''Create the record responsible for linking our application to the tag. |
||||||
|
''' |
||||||
|
return NdefRecord.createApplicationRecord(JString("org.electrum.kivy")) |
||||||
|
|
||||||
|
def create_TNF_EXTERNAL(self, data): |
||||||
|
'''Create our actual payload record. |
||||||
|
''' |
||||||
|
if BUILDVERSION >= 14: |
||||||
|
domain = "org.electrum" |
||||||
|
stype = "externalType" |
||||||
|
extRecord = NdefRecord.createExternal(domain, stype, data) |
||||||
|
else: |
||||||
|
# Creating the NdefRecord manually: |
||||||
|
extRecord = NdefRecord( |
||||||
|
NdefRecord.TNF_EXTERNAL_TYPE, |
||||||
|
"org.electrum:externalType", |
||||||
|
'', |
||||||
|
data) |
||||||
|
return extRecord |
||||||
|
|
||||||
|
def create_ndef_message(self, *recs): |
||||||
|
''' Create the Ndef message that will written to tag |
||||||
|
''' |
||||||
|
records = [] |
||||||
|
for record in recs: |
||||||
|
if record: |
||||||
|
records.append(record) |
||||||
|
|
||||||
|
return NdefMessage(records) |
||||||
|
|
||||||
|
|
||||||
|
@run_on_ui_thread |
||||||
|
def disable_foreground_dispatch(self): |
||||||
|
'''Disable foreground dispatch when app is paused. |
||||||
|
''' |
||||||
|
self.nfc_adapter.disableForegroundDispatch(self.j_context) |
||||||
|
|
||||||
|
@run_on_ui_thread |
||||||
|
def enable_foreground_dispatch(self): |
||||||
|
'''Start listening for new tags |
||||||
|
''' |
||||||
|
self.nfc_adapter.enableForegroundDispatch(self.j_context, |
||||||
|
self.nfc_pending_intent, self.ndef_exchange_filters, self.ndef_tech_list) |
||||||
|
|
||||||
@run_on_ui_thread |
@run_on_ui_thread |
||||||
def _nfc_enable_ndef_exchange(self, data): |
def _nfc_enable_ndef_exchange(self, data): |
||||||
# print 'create record' |
# Enable p2p exchange |
||||||
|
# Create record |
||||||
ndef_record = NdefRecord( |
ndef_record = NdefRecord( |
||||||
NdefRecord.TNF_MIME_MEDIA, |
NdefRecord.TNF_MIME_MEDIA, |
||||||
'text/plain', '', data) |
'org.electrum.kivy', '', data) |
||||||
# print 'create message' |
|
||||||
|
# Create message |
||||||
ndef_message = NdefMessage([ndef_record]) |
ndef_message = NdefMessage([ndef_record]) |
||||||
|
|
||||||
# print 'enable ndef push' |
# Enable ndef push |
||||||
self.nfc_adapter.enableForegroundNdefPush(self.j_context, ndef_message) |
self.nfc_adapter.enableForegroundNdefPush(self.j_context, ndef_message) |
||||||
|
|
||||||
# print 'enable dispatch', self.j_context, self.nfc_pending_intent |
# Enable dispatch |
||||||
self.nfc_adapter.enableForegroundDispatch(self.j_context, |
self.nfc_adapter.enableForegroundDispatch(self.j_context, |
||||||
self.nfc_pending_intent, self.ndef_exchange_filters, []) |
self.nfc_pending_intent, self.ndef_exchange_filters, []) |
||||||
|
|
||||||
@run_on_ui_thread |
@run_on_ui_thread |
||||||
def _nfc_disable_ndef_exchange(self): |
def _nfc_disable_ndef_exchange(self): |
||||||
|
# Disable p2p exchange |
||||||
self.nfc_adapter.disableForegroundNdefPush(self.j_context) |
self.nfc_adapter.disableForegroundNdefPush(self.j_context) |
||||||
self.nfc_adapter.disableForegroundDispatch(self.j_context) |
self.nfc_adapter.disableForegroundDispatch(self.j_context) |
||||||
|
|
||||||
def nfc_enable_exchange(self, data): |
def nfc_enable_exchange(self, data): |
||||||
|
'''Enable Ndef exchange for p2p |
||||||
|
''' |
||||||
self._nfc_enable_ndef_exchange() |
self._nfc_enable_ndef_exchange() |
||||||
|
|
||||||
def nfc_disable_exchange(self): |
def nfc_disable_exchange(self): |
||||||
|
''' Disable Ndef exchange for p2p |
||||||
|
''' |
||||||
self._nfc_disable_ndef_exchange() |
self._nfc_disable_ndef_exchange() |
||||||
|
|||||||
Loading…
Reference in new issue