URI: 
       tscanner_android.py - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
       tscanner_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()