electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

scanner_android.py (8410B)


      1 '''This is the Android implementation of NFC Scanning using the
      2 built in NFC adapter of some android phones.
      3 '''
      4 
      5 from kivy.app import App
      6 from kivy.clock import Clock
      7 #Detect which platform we are on
      8 from kivy.utils import platform
      9 if platform != 'android':
     10     raise ImportError
     11 import threading
     12 
     13 from . import NFCBase
     14 from jnius import autoclass, cast
     15 from android.runnable import run_on_ui_thread
     16 from android import activity
     17 
     18 BUILDVERSION = autoclass('android.os.Build$VERSION').SDK_INT
     19 NfcAdapter = autoclass('android.nfc.NfcAdapter')
     20 PythonActivity = autoclass('org.kivy.android.PythonActivity')
     21 JString = autoclass('java.lang.String')
     22 Charset = autoclass('java.nio.charset.Charset')
     23 locale = autoclass('java.util.Locale')
     24 Intent = autoclass('android.content.Intent')
     25 IntentFilter = autoclass('android.content.IntentFilter')
     26 PendingIntent = autoclass('android.app.PendingIntent')
     27 Ndef = autoclass('android.nfc.tech.Ndef')
     28 NdefRecord = autoclass('android.nfc.NdefRecord')
     29 NdefMessage = autoclass('android.nfc.NdefMessage')
     30 
     31 app = None
     32 
     33 
     34 
     35 class ScannerAndroid(NFCBase):
     36     ''' This is the class responsible for handling the interface with the
     37     Android NFC adapter. See Module Documentation for details.
     38     '''
     39 
     40     name = 'NFCAndroid'
     41 
     42     def nfc_init(self):
     43         ''' This is where we initialize NFC adapter.
     44         '''
     45         # Initialize NFC
     46         global app
     47         app = App.get_running_app()
     48 
     49         # Make sure we are listening to new intent 
     50         activity.bind(on_new_intent=self.on_new_intent)
     51 
     52         # Configure nfc
     53         self.j_context = context = PythonActivity.mActivity
     54         self.nfc_adapter = NfcAdapter.getDefaultAdapter(context)
     55         # Check if adapter exists
     56         if not self.nfc_adapter:
     57             return False
     58         
     59         # specify that we want our activity to remain on top when a new intent
     60         # is fired
     61         self.nfc_pending_intent = PendingIntent.getActivity(context, 0,
     62             Intent(context, context.getClass()).addFlags(
     63                 Intent.FLAG_ACTIVITY_SINGLE_TOP), 0)
     64 
     65         # Filter for different types of action, by default we enable all.
     66         # These are only for handling different NFC technologies when app is in foreground
     67         self.ndef_detected = IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED)
     68         #self.tech_detected = IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED)
     69         #self.tag_detected = IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED)
     70 
     71         # setup tag discovery for ourt tag type
     72         try:
     73             self.ndef_detected.addCategory(Intent.CATEGORY_DEFAULT)
     74             # setup the foreground dispatch to detect all mime types
     75             self.ndef_detected.addDataType('*/*')
     76 
     77             self.ndef_exchange_filters = [self.ndef_detected]
     78         except Exception as err:
     79             raise Exception(repr(err))
     80         return True
     81 
     82     def get_ndef_details(self, tag):
     83         ''' Get all the details from the tag.
     84         '''
     85         details = {}
     86 
     87         try:
     88             #print 'id'
     89             details['uid'] = ':'.join(['{:02x}'.format(bt & 0xff) for bt in tag.getId()])
     90             #print 'technologies'
     91             details['Technologies'] = tech_list = [tech.split('.')[-1] for tech in tag.getTechList()]
     92             #print 'get NDEF tag details'
     93             ndefTag = cast('android.nfc.tech.Ndef', Ndef.get(tag))
     94             #print 'tag size'
     95             details['MaxSize'] = ndefTag.getMaxSize()
     96             #details['usedSize'] = '0'
     97             #print 'is tag writable?'
     98             details['writable'] = ndefTag.isWritable()
     99             #print 'Data format'
    100             # Can be made readonly
    101             # get NDEF message details
    102             ndefMesg = ndefTag.getCachedNdefMessage()
    103             # get size of current records
    104             details['consumed'] = len(ndefMesg.toByteArray())
    105             #print 'tag type'
    106             details['Type'] = ndefTag.getType()
    107 
    108             # check if tag is empty
    109             if not ndefMesg:
    110                 details['Message'] = None
    111                 return details
    112 
    113             ndefrecords =  ndefMesg.getRecords()
    114             length = len(ndefrecords)
    115             #print 'length', length
    116             # will contain the NDEF record types
    117             recTypes = []
    118             for record in ndefrecords:
    119                 recTypes.append({
    120                     'type': ''.join(map(chr, record.getType())),
    121                     'payload': ''.join(map(chr, record.getPayload()))
    122                     })
    123 
    124             details['recTypes'] = recTypes
    125         except Exception as err:
    126             print(str(err))
    127 
    128         return details
    129 
    130     def on_new_intent(self, intent):
    131         ''' This function is called when the application receives a
    132         new intent, for the ones the application has registered previously,
    133         either in the manifest or in the foreground dispatch setup in the
    134         nfc_init function above. 
    135         '''
    136 
    137         action_list = (NfcAdapter.ACTION_NDEF_DISCOVERED,)
    138         # get TAG
    139         #tag = cast('android.nfc.Tag', intent.getParcelableExtra(NfcAdapter.EXTRA_TAG))
    140 
    141         #details = self.get_ndef_details(tag)
    142 
    143         if intent.getAction() not in action_list:
    144             print('unknow action, avoid.')
    145             return
    146 
    147         rawmsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
    148         if not rawmsgs:
    149             return
    150         for message in rawmsgs:
    151             message = cast(NdefMessage, message)
    152             payload = message.getRecords()[0].getPayload()
    153             print('payload: {}'.format(''.join(map(chr, payload))))
    154 
    155     def nfc_disable(self):
    156         '''Disable app from handling tags.
    157         '''
    158         self.disable_foreground_dispatch()
    159 
    160     def nfc_enable(self):
    161         '''Enable app to handle tags when app in foreground.
    162         '''
    163         self.enable_foreground_dispatch()
    164 
    165     def create_AAR(self):
    166         '''Create the record responsible for linking our application to the tag.
    167         '''
    168         return NdefRecord.createApplicationRecord(JString("org.electrum.kivy"))
    169 
    170     def create_TNF_EXTERNAL(self, data):
    171         '''Create our actual payload record.
    172         '''
    173         if BUILDVERSION >= 14:
    174             domain = "org.electrum"
    175             stype = "externalType"
    176             extRecord = NdefRecord.createExternal(domain, stype, data)
    177         else:
    178             # Creating the NdefRecord manually:
    179             extRecord = NdefRecord(
    180                 NdefRecord.TNF_EXTERNAL_TYPE,
    181                 "org.electrum:externalType",
    182                 '',
    183                 data)
    184         return extRecord
    185 
    186     def create_ndef_message(self, *recs):
    187         ''' Create the Ndef message that will be written to tag
    188         '''
    189         records = []
    190         for record in recs:
    191             if record:
    192                 records.append(record)
    193 
    194         return NdefMessage(records)
    195 
    196 
    197     @run_on_ui_thread
    198     def disable_foreground_dispatch(self):
    199         '''Disable foreground dispatch when app is paused.
    200         '''
    201         self.nfc_adapter.disableForegroundDispatch(self.j_context)
    202 
    203     @run_on_ui_thread
    204     def enable_foreground_dispatch(self):
    205         '''Start listening for new tags
    206         '''
    207         self.nfc_adapter.enableForegroundDispatch(self.j_context,
    208                 self.nfc_pending_intent, self.ndef_exchange_filters, self.ndef_tech_list)
    209 
    210     @run_on_ui_thread
    211     def _nfc_enable_ndef_exchange(self, data):
    212         # Enable p2p exchange
    213         # Create record
    214         ndef_record = NdefRecord(
    215                 NdefRecord.TNF_MIME_MEDIA,
    216                 'org.electrum.kivy', '', data)
    217         
    218         # Create message
    219         ndef_message = NdefMessage([ndef_record])
    220 
    221         # Enable ndef push
    222         self.nfc_adapter.enableForegroundNdefPush(self.j_context, ndef_message)
    223 
    224         # Enable dispatch
    225         self.nfc_adapter.enableForegroundDispatch(self.j_context,
    226                 self.nfc_pending_intent, self.ndef_exchange_filters, [])
    227 
    228     @run_on_ui_thread
    229     def _nfc_disable_ndef_exchange(self):
    230         # Disable p2p exchange
    231         self.nfc_adapter.disableForegroundNdefPush(self.j_context)
    232         self.nfc_adapter.disableForegroundDispatch(self.j_context)
    233 
    234     def nfc_enable_exchange(self, data):
    235         '''Enable Ndef exchange for p2p
    236         '''
    237         self._nfc_enable_ndef_exchange()
    238 
    239     def nfc_disable_exchange(self):
    240         ''' Disable Ndef exchange for p2p
    241         '''
    242         self._nfc_disable_ndef_exchange()