tinclude NFC changes required for transferring data - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit cd4f8a074c7afd0380b0bc6ed92ed477bd10477d DIR parent f2fc18fe327c5c8f0fe2bdb2fe8f7f57f623ccfa HTML Author: akshayaurora <akshayaurora@gmail.com> Date: Fri, 1 Aug 2014 21:39:34 +0530 include NFC changes required for transferring data Diffstat: M gui/kivy/main_window.py | 39 ++++++++++++++++++------------- M gui/kivy/nfc_scanner/__init__.py | 23 ++++++++++++----------- M gui/kivy/nfc_scanner/scanner_andro… | 199 +++++++++++++++++++++++++++---- M gui/kivy/nfc_scanner/scanner_dummy… | 15 +++++++++++++++ M gui/kivy/tools/blacklist.txt | 8 ++++---- M gui/kivy/tools/buildozer.spec | 6 +++--- M gui/kivy/uix/dialogs/new_contact.py | 1 - M gui/kivy/uix/ui_screens/mainscreen… | 1 + 8 files changed, 236 insertions(+), 56 deletions(-) --- DIR diff --git a/gui/kivy/main_window.py b/gui/kivy/main_window.py t@@ -4,6 +4,11 @@ import datetime from electrum import WalletStorage, Wallet from electrum.i18n import _, set_language +from kivy.config import Config +Config.set('modules', 'screen', 'droid2') +Config.set('graphics', 'width', '480') +Config.set('graphics', 'height', '840') + from kivy.app import App from kivy.core.window import Window from kivy.logger import Logger t@@ -30,9 +35,9 @@ inch = None util = False re = None -# register widget cache for keeping memory down timeout to 4 minutes to cache +# register widget cache for keeping memory down timeout to forever to cache # the data -Cache.register('electrum_widgets', timeout=240) +Cache.register('electrum_widgets', timeout=0) class ElectrumWindow(App): t@@ -375,19 +380,23 @@ class ElectrumWindow(App): module='electrum_gui.kivy.uix.screens') Factory.register('ScreenDashboard', module='electrum_gui.kivy.uix.screens') - Factory.register('EffectWidget', - module='electrum_gui.kivy.uix.effectwidget') - - # load and focus the ui - #Load mainscreen - + #Factory.register('EffectWidget', + # module='electrum_gui.kivy.uix.effectwidget') Factory.register('QRCodeWidget', module='electrum_gui.kivy.uix.qrcodewidget') Factory.register('MainScreen', module='electrum_gui.kivy.uix.screens') Factory.register('CSpinner', module='electrum_gui.kivy.uix.screens') + # preload widgets. Remove this if you want to load the widgets on demand + Cache.append('electrum_widgets', 'AnimatedPopup', Factory.AnimatedPopup()) + Cache.append('electrum_widgets', 'TabbedCarousel', Factory.TabbedCarousel()) + Cache.append('electrum_widgets', 'QRCodeWidget', Factory.QRCodeWidget()) + Cache.append('electrum_widgets', 'CSpinner', Factory.CSpinner()) + + # load and focus the ui + #Load mainscreen dr = Builder.load_file('gui/kivy/uix/ui_screens/mainscreen.kv') self.root.add_widget(dr) self.root.manager = manager = dr.ids.manager t@@ -1032,7 +1041,7 @@ class ElectrumWindow(App): # populate def set_address(*l): - content = screen_send.content.ids + content = screen_send.ids content.payto_e.text = m_addr content.message_e.text = message if amount: t@@ -1051,7 +1060,7 @@ class ElectrumWindow(App): # switch_to the send screen tabs.ids.panel.switch_to(tabs.ids.tab_send) - content = screen_send.content.ids + content = screen_send.ids if content: self.set_frozen(content, False) screen_send.screen_label.text = _("please wait...") t@@ -1064,12 +1073,11 @@ class ElectrumWindow(App): # switch_to the send screen tabs.ids.panel.switch_to(tabs.ids.tab_send) - content = screen_send.content self.set_frozen(content, True) - content.ids.payto_e.text = self.gui_object.payment_request.domain - content.ids.amount_e.text = self.format_amount(self.gui_object.payment_request.get_amount()) - content.ids.message_e.text = self.gui_object.payment_request.memo + screen_send.ids.payto_e.text = self.gui_object.payment_request.domain + screen_send.ids.amount_e.text = self.format_amount(self.gui_object.payment_request.get_amount()) + screen_send.ids.message_e.text = self.gui_object.payment_request.memo # wait for screen to load Clock.schedule_once(set_address, .5) t@@ -1275,4 +1283,4 @@ class ElectrumWindow(App): info_bubble.message = text if not pos: pos = (win.center[0], win.center[1] - (info_bubble.height/2)) - info_bubble.show(pos, duration, width, modal=modal, exit=exit) -\ No newline at end of file + info_bubble.show(pos, duration, width, modal=modal, exit=exit) DIR diff --git a/gui/kivy/nfc_scanner/__init__.py b/gui/kivy/nfc_scanner/__init__.py t@@ -1,18 +1,18 @@ -''' -''' -from kivy.core import core_select_lib -from kivy.uix.widget import Widget -from kivy.properties import ObjectProperty -from kivy.factory import Factory - __all__ = ('NFCBase', 'NFCScanner') class NFCBase(Widget): + ''' This is the base Abstract definition class that the actual hardware dependent + implementations would be based on. If you want to define a feature that is + accissible and implemented by every platform implementation then define that + method in this class. + ''' payload = ObjectProperty(None) + '''This is the data gotten from the tag. + ''' def nfc_init(self): - ''' Initialize the adapter + ''' Initialize the adapter. ''' pass t@@ -27,17 +27,18 @@ class NFCBase(Widget): pass def nfc_enable_exchange(self, data): - ''' Start sending data + ''' Enable P2P Ndef exchange ''' pass def nfc_disable_exchange(self): - ''' Disable/Stop ndef exchange + ''' Disable/Stop P2P Ndef exchange ''' pass # load NFCScanner implementation -NFCScanner = core_select_lib('nfc_scanner', ( +NFCScanner = core_select_lib('nfc_manager', ( + # keep the dummy implementtation as the last one to make it the fallback provider.NFCScanner = core_select_lib('nfc_scanner', ( ('android', 'scanner_android', 'ScannerAndroid'), ('dummy', 'scanner_dummy', 'ScannerDummy')), True, 'electrum_gui.kivy') DIR diff --git a/gui/kivy/nfc_scanner/scanner_android.py b/gui/kivy/nfc_scanner/scanner_android.py t@@ -1,86 +1,243 @@ +'''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): - # print 'nfc_init()' + ''' This is where we initialize NFC adapter. + ''' + # Initialize NFC + global app + app = App.get_running_app() - # print 'configure nfc' + # 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) - # print 'p2p filter' + # 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.ndef_detected.addDataType('text/plain') - self.ndef_exchange_filters = [self.ndef_detected] + #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): - # print 'on_new_intent()', intent.getAction() - if intent.getAction() != NfcAdapter.ACTION_NDEF_DISCOVERED: - # print 'unknow action, avoid.' + ''' 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) - # print 'raw messages', rawmsgs if not rawmsgs: return - for message in rawmsgs: message = cast(NdefMessage, message) - # print 'got message', message payload = message.getRecords()[0].getPayload() - self.payload = payload print 'payload: {}'.format(''.join(map(chr, payload))) def nfc_disable(self): - # print 'nfc_enable()' - activity.bind(on_new_intent=self.on_new_intent) + '''Disable app from handling tags. + ''' + self.disable_foreground_dispatch() def nfc_enable(self): - # print 'nfc_enable()' - activity.bind(on_new_intent=self.on_new_intent) + '''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): - # print 'create record' + # Enable p2p exchange + # Create record ndef_record = NdefRecord( NdefRecord.TNF_MIME_MEDIA, - 'text/plain', '', data) - # print 'create message' + 'org.electrum.kivy', '', data) + + # Create message ndef_message = NdefMessage([ndef_record]) - # print 'enable ndef push' + # Enable ndef push self.nfc_adapter.enableForegroundNdefPush(self.j_context, ndef_message) - # print 'enable dispatch', self.j_context, self.nfc_pending_intent + # 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() DIR diff --git a/gui/kivy/nfc_scanner/scanner_dummy.py b/gui/kivy/nfc_scanner/scanner_dummy.py t@@ -5,16 +5,31 @@ from kivy.clock import Clock from kivy.logger import Logger class ScannerDummy(NFCBase): + '''This is the dummy interface that gets selected in case any other + hardware interface to NFC is not available. + ''' _initialised = False + name = 'NFCDummy' + def nfc_init(self): # print 'nfc_init()' Logger.debug('NFC: configure nfc') self._initialised = True + self.nfc_enable() + return True def on_new_intent(self, dt): + tag_info = {'type': 'dymmy', + 'message': 'dummy', + 'extra details': None} + + # let Main app know that a tag has been detected + app = App.get_running_app() + app.tag_discovered(tag_info) + app.show_info('New tag detected.', duration=2) Logger.debug('NFC: got new dummy tag') def nfc_enable(self): DIR diff --git a/gui/kivy/tools/blacklist.txt b/gui/kivy/tools/blacklist.txt t@@ -60,14 +60,14 @@ wsgiref/* hotshot/* pydoc_data/* tty.pyo -anydbm.pyo +#anydbm.pyo nturl2path.pyo LICENCE.txt macurl2path.pyo dummy_threading.pyo audiodev.pyo antigravity.pyo -dumbdbm.pyo +#dumbdbm.pyo sndhdr.pyo __phello__.foo.pyo sunaudio.pyo t@@ -93,7 +93,7 @@ plat-linux3/regen #>sqlite3 # conditionnal include depending if some recipes are included or not. -sqlite3/* -lib-dynload/_sqlite3.so +#sqlite3/* +#lib-dynload/_sqlite3.so #<sqlite3 DIR diff --git a/gui/kivy/tools/buildozer.spec b/gui/kivy/tools/buildozer.spec t@@ -32,7 +32,7 @@ source.exclude_exts = spec version = 1.9.8 # (list) Application requirements -requirements = pil, qrcode, ecdsa, pbkdf2, openssl, pyopenssl, pyasn, pyasn-modules, plyer==master, kivy==master +requirements = tlslite, openssl, pyopenssl, pil, qrcode, ecdsa, pbkdf2, pyasn1, pyasn1-modules, plyer==master, kivy==master # (str) Presplash of the application presplash.filename = %(source.dir)s/gui/kivy/theming/splash.png t@@ -52,7 +52,7 @@ fullscreen = False # # (list) Permissions -android.permissions = INTERNET, WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE , CAMERA, NFC +android.permissions = INTERNET, WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE, CAMERA, NFC # (int) Android API to use #android.api = 14 t@@ -100,7 +100,7 @@ android.branch = master #android.ouya.icon.filename = %(source.dir)s/data/ouya_icon.png # (str) XML file to include as an intent filters in <activity> tag -#android.manifest.intent_filters = +#android.manifest.intent_filters = nfc_filter.xml # (list) Android additionnal libraries to copy into libs/armeabi android.add_libs_armeabi = lib/android/*.so DIR diff --git a/gui/kivy/uix/dialogs/new_contact.py b/gui/kivy/uix/dialogs/new_contact.py t@@ -1,7 +1,6 @@ from kivy.app import App from kivy.factory import Factory from kivy.properties import ObjectProperty -from kivy.cache import Cache Factory.register('QrScannerDialog', module='electrum_gui.kivy.uix.dialogs.qr_scanner') DIR diff --git a/gui/kivy/uix/ui_screens/mainscreen.kv b/gui/kivy/uix/ui_screens/mainscreen.kv t@@ -489,6 +489,7 @@ <ScreenReceive> mode: 'qr' name: 'receive' + on_mode: if args[1] == 'nfc': from electrum_gui.kivy.nfc_scanner import NFCScanner action_view: Factory.ReceiveActionView() on_activate: self.ids.toggle_qr.state = 'down'