Browse Source

include NFC changes required for transferring data

283
akshayaurora 11 years ago
committed by ThomasV
parent
commit
cd4f8a074c
  1. 38
      gui/kivy/main_window.py
  2. 23
      gui/kivy/nfc_scanner/__init__.py
  3. 199
      gui/kivy/nfc_scanner/scanner_android.py
  4. 15
      gui/kivy/nfc_scanner/scanner_dummy.py
  5. 8
      gui/kivy/tools/blacklist.txt
  6. 6
      gui/kivy/tools/buildozer.spec
  7. 1
      gui/kivy/uix/dialogs/new_contact.py
  8. 1
      gui/kivy/uix/ui_screens/mainscreen.kv

38
gui/kivy/main_window.py

@ -4,6 +4,11 @@ import datetime
from electrum import WalletStorage, Wallet from electrum import WalletStorage, Wallet
from electrum.i18n import _, set_language 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.app import App
from kivy.core.window import Window from kivy.core.window import Window
from kivy.logger import Logger from kivy.logger import Logger
@ -30,9 +35,9 @@ inch = None
util = False util = False
re = None 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 # the data
Cache.register('electrum_widgets', timeout=240) Cache.register('electrum_widgets', timeout=0)
class ElectrumWindow(App): class ElectrumWindow(App):
@ -375,19 +380,23 @@ class ElectrumWindow(App):
module='electrum_gui.kivy.uix.screens') module='electrum_gui.kivy.uix.screens')
Factory.register('ScreenDashboard', Factory.register('ScreenDashboard',
module='electrum_gui.kivy.uix.screens') module='electrum_gui.kivy.uix.screens')
Factory.register('EffectWidget', #Factory.register('EffectWidget',
module='electrum_gui.kivy.uix.effectwidget') # module='electrum_gui.kivy.uix.effectwidget')
# load and focus the ui
#Load mainscreen
Factory.register('QRCodeWidget', Factory.register('QRCodeWidget',
module='electrum_gui.kivy.uix.qrcodewidget') module='electrum_gui.kivy.uix.qrcodewidget')
Factory.register('MainScreen', Factory.register('MainScreen',
module='electrum_gui.kivy.uix.screens') module='electrum_gui.kivy.uix.screens')
Factory.register('CSpinner', Factory.register('CSpinner',
module='electrum_gui.kivy.uix.screens') 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') dr = Builder.load_file('gui/kivy/uix/ui_screens/mainscreen.kv')
self.root.add_widget(dr) self.root.add_widget(dr)
self.root.manager = manager = dr.ids.manager self.root.manager = manager = dr.ids.manager
@ -1032,7 +1041,7 @@ class ElectrumWindow(App):
# populate # populate
def set_address(*l): def set_address(*l):
content = screen_send.content.ids content = screen_send.ids
content.payto_e.text = m_addr content.payto_e.text = m_addr
content.message_e.text = message content.message_e.text = message
if amount: if amount:
@ -1051,7 +1060,7 @@ class ElectrumWindow(App):
# switch_to the send screen # switch_to the send screen
tabs.ids.panel.switch_to(tabs.ids.tab_send) tabs.ids.panel.switch_to(tabs.ids.tab_send)
content = screen_send.content.ids content = screen_send.ids
if content: if content:
self.set_frozen(content, False) self.set_frozen(content, False)
screen_send.screen_label.text = _("please wait...") screen_send.screen_label.text = _("please wait...")
@ -1064,12 +1073,11 @@ class ElectrumWindow(App):
# switch_to the send screen # switch_to the send screen
tabs.ids.panel.switch_to(tabs.ids.tab_send) tabs.ids.panel.switch_to(tabs.ids.tab_send)
content = screen_send.content
self.set_frozen(content, True) self.set_frozen(content, True)
content.ids.payto_e.text = self.gui_object.payment_request.domain screen_send.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()) screen_send.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.message_e.text = self.gui_object.payment_request.memo
# wait for screen to load # wait for screen to load
Clock.schedule_once(set_address, .5) Clock.schedule_once(set_address, .5)
@ -1275,4 +1283,4 @@ class ElectrumWindow(App):
info_bubble.message = text info_bubble.message = text
if not pos: if not pos:
pos = (win.center[0], win.center[1] - (info_bubble.height/2)) pos = (win.center[0], win.center[1] - (info_bubble.height/2))
info_bubble.show(pos, duration, width, modal=modal, exit=exit) info_bubble.show(pos, duration, width, modal=modal, exit=exit)

23
gui/kivy/nfc_scanner/__init__.py

@ -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') __all__ = ('NFCBase', 'NFCScanner')
class NFCBase(Widget): 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) payload = ObjectProperty(None)
'''This is the data gotten from the tag.
'''
def nfc_init(self): def nfc_init(self):
''' Initialize the adapter ''' Initialize the adapter.
''' '''
pass pass
@ -27,17 +27,18 @@ class NFCBase(Widget):
pass pass
def nfc_enable_exchange(self, data): def nfc_enable_exchange(self, data):
''' Start sending data ''' Enable P2P Ndef exchange
''' '''
pass pass
def nfc_disable_exchange(self): def nfc_disable_exchange(self):
''' Disable/Stop ndef exchange ''' Disable/Stop P2P Ndef exchange
''' '''
pass pass
# load NFCScanner implementation # 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'), ('android', 'scanner_android', 'ScannerAndroid'),
('dummy', 'scanner_dummy', 'ScannerDummy')), True, 'electrum_gui.kivy') ('dummy', 'scanner_dummy', 'ScannerDummy')), True, 'electrum_gui.kivy')

199
gui/kivy/nfc_scanner/scanner_android.py

@ -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 from kivy.utils import platform
if platform != 'android': if platform != 'android':
raise ImportError raise ImportError
import threading
from electrum_gui.kivy.nfc_scanner import NFCBase from electrum_gui.kivy.nfc_scanner import NFCBase
from jnius import autoclass, cast from jnius import autoclass, cast
from android.runnable import run_on_ui_thread from android.runnable import run_on_ui_thread
from android import activity from android import activity
BUILDVERSION = autoclass('android.os.Build$VERSION').SDK_INT
NfcAdapter = autoclass('android.nfc.NfcAdapter') NfcAdapter = autoclass('android.nfc.NfcAdapter')
PythonActivity = autoclass('org.renpy.android.PythonActivity') 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') Intent = autoclass('android.content.Intent')
IntentFilter = autoclass('android.content.IntentFilter') IntentFilter = autoclass('android.content.IntentFilter')
PendingIntent = autoclass('android.app.PendingIntent') PendingIntent = autoclass('android.app.PendingIntent')
Ndef = autoclass('android.nfc.tech.Ndef')
NdefRecord = autoclass('android.nfc.NdefRecord') NdefRecord = autoclass('android.nfc.NdefRecord')
NdefMessage = autoclass('android.nfc.NdefMessage') NdefMessage = autoclass('android.nfc.NdefMessage')
app = None
class ScannerAndroid(NFCBase): 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): 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.j_context = context = PythonActivity.mActivity
self.nfc_adapter = NfcAdapter.getDefaultAdapter(context) 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, self.nfc_pending_intent = PendingIntent.getActivity(context, 0,
Intent(context, context.getClass()).addFlags( Intent(context, context.getClass()).addFlags(
Intent.FLAG_ACTIVITY_SINGLE_TOP), 0) 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 = IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED)
self.ndef_detected.addDataType('text/plain') #self.tech_detected = IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED)
self.ndef_exchange_filters = [self.ndef_detected] #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): def on_new_intent(self, intent):
# print 'on_new_intent()', intent.getAction() ''' This functions is called when the application receives a
if intent.getAction() != NfcAdapter.ACTION_NDEF_DISCOVERED: new intent, for the ones the application has registered previously,
# print 'unknow action, avoid.' 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 return
rawmsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES) rawmsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
# print 'raw messages', rawmsgs
if not rawmsgs: if not rawmsgs:
return return
for message in rawmsgs: for message in rawmsgs:
message = cast(NdefMessage, message) message = cast(NdefMessage, message)
# print 'got message', message
payload = message.getRecords()[0].getPayload() payload = message.getRecords()[0].getPayload()
self.payload = payload
print 'payload: {}'.format(''.join(map(chr, payload))) print 'payload: {}'.format(''.join(map(chr, payload)))
def nfc_disable(self): def nfc_disable(self):
# print 'nfc_enable()' '''Disable app from handling tags.
activity.bind(on_new_intent=self.on_new_intent) '''
self.disable_foreground_dispatch()
def nfc_enable(self): def nfc_enable(self):
# print 'nfc_enable()' '''Enable app to handle tags when app in foreground.
activity.bind(on_new_intent=self.on_new_intent) '''
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 @run_on_ui_thread
def _nfc_enable_ndef_exchange(self, data): def _nfc_enable_ndef_exchange(self, data):
# print 'create record' # Enable p2p exchange
# Create record
ndef_record = NdefRecord( ndef_record = NdefRecord(
NdefRecord.TNF_MIME_MEDIA, NdefRecord.TNF_MIME_MEDIA,
'text/plain', '', data) 'org.electrum.kivy', '', data)
# print 'create message'
# Create message
ndef_message = NdefMessage([ndef_record]) ndef_message = NdefMessage([ndef_record])
# print 'enable ndef push' # Enable ndef push
self.nfc_adapter.enableForegroundNdefPush(self.j_context, ndef_message) 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_adapter.enableForegroundDispatch(self.j_context,
self.nfc_pending_intent, self.ndef_exchange_filters, []) self.nfc_pending_intent, self.ndef_exchange_filters, [])
@run_on_ui_thread @run_on_ui_thread
def _nfc_disable_ndef_exchange(self): def _nfc_disable_ndef_exchange(self):
# Disable p2p exchange
self.nfc_adapter.disableForegroundNdefPush(self.j_context) self.nfc_adapter.disableForegroundNdefPush(self.j_context)
self.nfc_adapter.disableForegroundDispatch(self.j_context) self.nfc_adapter.disableForegroundDispatch(self.j_context)
def nfc_enable_exchange(self, data): def nfc_enable_exchange(self, data):
'''Enable Ndef exchange for p2p
'''
self._nfc_enable_ndef_exchange() self._nfc_enable_ndef_exchange()
def nfc_disable_exchange(self): def nfc_disable_exchange(self):
''' Disable Ndef exchange for p2p
'''
self._nfc_disable_ndef_exchange() self._nfc_disable_ndef_exchange()

15
gui/kivy/nfc_scanner/scanner_dummy.py

@ -5,16 +5,31 @@ from kivy.clock import Clock
from kivy.logger import Logger from kivy.logger import Logger
class ScannerDummy(NFCBase): class ScannerDummy(NFCBase):
'''This is the dummy interface that gets selected in case any other
hardware interface to NFC is not available.
'''
_initialised = False _initialised = False
name = 'NFCDummy'
def nfc_init(self): def nfc_init(self):
# print 'nfc_init()' # print 'nfc_init()'
Logger.debug('NFC: configure nfc') Logger.debug('NFC: configure nfc')
self._initialised = True self._initialised = True
self.nfc_enable()
return True
def on_new_intent(self, dt): 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') Logger.debug('NFC: got new dummy tag')
def nfc_enable(self): def nfc_enable(self):

8
gui/kivy/tools/blacklist.txt

@ -60,14 +60,14 @@ wsgiref/*
hotshot/* hotshot/*
pydoc_data/* pydoc_data/*
tty.pyo tty.pyo
anydbm.pyo #anydbm.pyo
nturl2path.pyo nturl2path.pyo
LICENCE.txt LICENCE.txt
macurl2path.pyo macurl2path.pyo
dummy_threading.pyo dummy_threading.pyo
audiodev.pyo audiodev.pyo
antigravity.pyo antigravity.pyo
dumbdbm.pyo #dumbdbm.pyo
sndhdr.pyo sndhdr.pyo
__phello__.foo.pyo __phello__.foo.pyo
sunaudio.pyo sunaudio.pyo
@ -93,7 +93,7 @@ plat-linux3/regen
#>sqlite3 #>sqlite3
# conditionnal include depending if some recipes are included or not. # conditionnal include depending if some recipes are included or not.
sqlite3/* #sqlite3/*
lib-dynload/_sqlite3.so #lib-dynload/_sqlite3.so
#<sqlite3 #<sqlite3

6
gui/kivy/tools/buildozer.spec

@ -32,7 +32,7 @@ source.exclude_exts = spec
version = 1.9.8 version = 1.9.8
# (list) Application requirements # (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 # (str) Presplash of the application
presplash.filename = %(source.dir)s/gui/kivy/theming/splash.png presplash.filename = %(source.dir)s/gui/kivy/theming/splash.png
@ -52,7 +52,7 @@ fullscreen = False
# #
# (list) Permissions # (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 # (int) Android API to use
#android.api = 14 #android.api = 14
@ -100,7 +100,7 @@ android.branch = master
#android.ouya.icon.filename = %(source.dir)s/data/ouya_icon.png #android.ouya.icon.filename = %(source.dir)s/data/ouya_icon.png
# (str) XML file to include as an intent filters in <activity> tag # (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 # (list) Android additionnal libraries to copy into libs/armeabi
android.add_libs_armeabi = lib/android/*.so android.add_libs_armeabi = lib/android/*.so

1
gui/kivy/uix/dialogs/new_contact.py

@ -1,7 +1,6 @@
from kivy.app import App from kivy.app import App
from kivy.factory import Factory from kivy.factory import Factory
from kivy.properties import ObjectProperty from kivy.properties import ObjectProperty
from kivy.cache import Cache
Factory.register('QrScannerDialog', module='electrum_gui.kivy.uix.dialogs.qr_scanner') Factory.register('QrScannerDialog', module='electrum_gui.kivy.uix.dialogs.qr_scanner')

1
gui/kivy/uix/ui_screens/mainscreen.kv

@ -489,6 +489,7 @@
<ScreenReceive> <ScreenReceive>
mode: 'qr' mode: 'qr'
name: 'receive' name: 'receive'
on_mode: if args[1] == 'nfc': from electrum_gui.kivy.nfc_scanner import NFCScanner
action_view: Factory.ReceiveActionView() action_view: Factory.ReceiveActionView()
on_activate: on_activate:
self.ids.toggle_qr.state = 'down' self.ids.toggle_qr.state = 'down'

Loading…
Cancel
Save