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()