You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
243 lines
8.3 KiB
243 lines
8.3 KiB
'''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
|
|
if platform != 'android':
|
|
raise ImportError
|
|
import threading
|
|
|
|
from electrum_gui.kivy.nfc_scanner import NFCBase
|
|
from jnius import autoclass, cast
|
|
from android.runnable import run_on_ui_thread
|
|
from android import activity
|
|
|
|
BUILDVERSION = autoclass('android.os.Build$VERSION').SDK_INT
|
|
NfcAdapter = autoclass('android.nfc.NfcAdapter')
|
|
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')
|
|
IntentFilter = autoclass('android.content.IntentFilter')
|
|
PendingIntent = autoclass('android.app.PendingIntent')
|
|
Ndef = autoclass('android.nfc.tech.Ndef')
|
|
NdefRecord = autoclass('android.nfc.NdefRecord')
|
|
NdefMessage = autoclass('android.nfc.NdefMessage')
|
|
|
|
app = None
|
|
|
|
|
|
|
|
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):
|
|
''' This is where we initialize NFC adapter.
|
|
'''
|
|
# Initialize NFC
|
|
global app
|
|
app = App.get_running_app()
|
|
|
|
# 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.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,
|
|
Intent(context, context.getClass()).addFlags(
|
|
Intent.FLAG_ACTIVITY_SINGLE_TOP), 0)
|
|
|
|
# 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.tech_detected = IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED)
|
|
#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):
|
|
''' This functions is called when the application receives a
|
|
new intent, for the ones the application has registered previously,
|
|
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
|
|
|
|
rawmsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
|
|
if not rawmsgs:
|
|
return
|
|
for message in rawmsgs:
|
|
message = cast(NdefMessage, message)
|
|
payload = message.getRecords()[0].getPayload()
|
|
print 'payload: {}'.format(''.join(map(chr, payload)))
|
|
|
|
def nfc_disable(self):
|
|
'''Disable app from handling tags.
|
|
'''
|
|
self.disable_foreground_dispatch()
|
|
|
|
def nfc_enable(self):
|
|
'''Enable app to handle tags when app in foreground.
|
|
'''
|
|
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
|
|
def _nfc_enable_ndef_exchange(self, data):
|
|
# Enable p2p exchange
|
|
# Create record
|
|
ndef_record = NdefRecord(
|
|
NdefRecord.TNF_MIME_MEDIA,
|
|
'org.electrum.kivy', '', data)
|
|
|
|
# Create message
|
|
ndef_message = NdefMessage([ndef_record])
|
|
|
|
# Enable ndef push
|
|
self.nfc_adapter.enableForegroundNdefPush(self.j_context, ndef_message)
|
|
|
|
# Enable dispatch
|
|
self.nfc_adapter.enableForegroundDispatch(self.j_context,
|
|
self.nfc_pending_intent, self.ndef_exchange_filters, [])
|
|
|
|
@run_on_ui_thread
|
|
def _nfc_disable_ndef_exchange(self):
|
|
# Disable p2p exchange
|
|
self.nfc_adapter.disableForegroundNdefPush(self.j_context)
|
|
self.nfc_adapter.disableForegroundDispatch(self.j_context)
|
|
|
|
def nfc_enable_exchange(self, data):
|
|
'''Enable Ndef exchange for p2p
|
|
'''
|
|
self._nfc_enable_ndef_exchange()
|
|
|
|
def nfc_disable_exchange(self):
|
|
''' Disable Ndef exchange for p2p
|
|
'''
|
|
self._nfc_disable_ndef_exchange()
|
|
|