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