commit cd4f8a074c7afd0380b0bc6ed92ed477bd10477d
parent f2fc18fe327c5c8f0fe2bdb2fe8f7f57f623ccfa
Author: akshayaurora <akshayaurora@gmail.com>
Date: Fri, 1 Aug 2014 21:39:34 +0530
include NFC changes required for transferring data
Diffstat:
8 files changed, 236 insertions(+), 56 deletions(-)
diff --git a/gui/kivy/main_window.py b/gui/kivy/main_window.py
@@ -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
@@ -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):
@@ -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
@@ -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:
@@ -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...")
@@ -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)
@@ -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)
diff --git a/gui/kivy/nfc_scanner/__init__.py b/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')
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
@@ -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')
diff --git a/gui/kivy/nfc_scanner/scanner_android.py b/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
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()
diff --git a/gui/kivy/nfc_scanner/scanner_dummy.py b/gui/kivy/nfc_scanner/scanner_dummy.py
@@ -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):
diff --git a/gui/kivy/tools/blacklist.txt b/gui/kivy/tools/blacklist.txt
@@ -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
@@ -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
diff --git a/gui/kivy/tools/buildozer.spec b/gui/kivy/tools/buildozer.spec
@@ -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
@@ -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
@@ -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
diff --git a/gui/kivy/uix/dialogs/new_contact.py b/gui/kivy/uix/dialogs/new_contact.py
@@ -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')
diff --git a/gui/kivy/uix/ui_screens/mainscreen.kv b/gui/kivy/uix/ui_screens/mainscreen.kv
@@ -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'