commit e42cd36318c5924409adf4189bab5c06662705c7
parent e4cefa8bc5d30b5df12e1cf851380ccad0145a87
Author: ThomasV <thomasv@gitorious>
Date: Mon, 16 Apr 2012 18:38:27 +0400
Merge branch 'master' of gitorious.org:electrum/electrum
Diffstat:
8 files changed, 1000 insertions(+), 354 deletions(-)
diff --git a/client/ANDROID b/client/ANDROID
@@ -0,0 +1,33 @@
+INSTALLATION INSTRUCTIONS FOR ANDROID
+
+
+1. Install sl4a and py4a : you will need at least revision r5x12 of sl4a
+
+To install these APKs, just visit the links below with your phone and
+click on the apk link, or scan the qr code
+
+sl4a: http://code.google.com/p/android-scripting/wiki/Unofficial
+py4a: http://code.google.com/p/python-for-android/downloads/detail?name=PythonForAndroid_r5.apk
+
+
+
+2. copy the following files in /sdcard/sl4a/scripts:
+
+bmp.py
+electrum4a.py
+interface.py
+mnemonic.py
+msqr.py
+pyqrnative.py
+ripemd.py
+version.py
+wallet.py
+aes (directory)
+ecdsa (directory)
+
+Note: The aes and ecdsa directories are not included in the git
+repository. You will have to find them on your system, or you can
+find them in the distributed version, Electrum tar.gz or Electrum.zip
+
+
+3. to run the application, open sl4a and click on electrum4a.py
diff --git a/client/bmp.py b/client/bmp.py
@@ -0,0 +1,206 @@
+# -*- coding: utf-8 -*-
+"""
+bmp.py - module for constructing simple BMP graphics files
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+"""
+__version__ = "0.3"
+__about = "bmp module, version %s, written by Paul McGuire, October, 2003, updated by Margus Laak, September, 2009" % __version__
+
+from math import ceil, hypot
+
+
+def shortToString(i):
+ hi = (i & 0xff00) >> 8
+ lo = i & 0x00ff
+ return chr(lo) + chr(hi)
+
+def longToString(i):
+ hi = (long(i) & 0x7fff0000) >> 16
+ lo = long(i) & 0x0000ffff
+ return shortToString(lo) + shortToString(hi)
+
+def long24ToString(i):
+ return chr(i & 0xff) + chr(i >> 8 & 0xff) + chr(i >> 16 & 0xff)
+
+def stringToLong(input_string, offset):
+ return ord(input_string[offset+3]) << 24 | ord(input_string[offset+2]) << 16 | ord(input_string[offset+1]) << 8 | ord(input_string[offset])
+
+def stringToLong24(input_string, offset):
+ return ord(input_string[offset+2]) << 16 | ord(input_string[offset+1]) << 8 | ord(input_string[offset])
+
+class Color(object):
+ """class for specifying colors while drawing BitMap elements"""
+ __slots__ = [ 'red', 'grn', 'blu' ]
+ __shade = 32
+
+ def __init__( self, r=0, g=0, b=0 ):
+ self.red = r
+ self.grn = g
+ self.blu = b
+
+ def __setattr__(self, name, value):
+ if hasattr(self, name):
+ raise AttributeError, "Color is immutable"
+ else:
+ object.__setattr__(self, name, value)
+
+ def __str__( self ):
+ return "R:%d G:%d B:%d" % (self.red, self.grn, self.blu )
+
+ def __hash__( self ):
+ return ( ( long(self.blu) ) +
+ ( long(self.grn) << 8 ) +
+ ( long(self.red) << 16 ) )
+
+ def __eq__( self, other ):
+ return (self is other) or (self.toLong == other.toLong)
+
+ def lighten( self ):
+ return Color(
+ min( self.red + Color.__shade, 255),
+ min( self.grn + Color.__shade, 255),
+ min( self.blu + Color.__shade, 255)
+ )
+
+ def darken( self ):
+ return Color(
+ max( self.red - Color.__shade, 0),
+ max( self.grn - Color.__shade, 0),
+ max( self.blu - Color.__shade, 0)
+ )
+
+ def toLong( self ):
+ return self.__hash__()
+
+ def fromLong( l ):
+ b = l & 0xff
+ l = l >> 8
+ g = l & 0xff
+ l = l >> 8
+ r = l & 0xff
+ return Color( r, g, b )
+ fromLong = staticmethod(fromLong)
+
+# define class constants for common colors
+Color.BLACK = Color( 0, 0, 0 )
+Color.RED = Color( 255, 0, 0 )
+Color.GREEN = Color( 0, 255, 0 )
+Color.BLUE = Color( 0, 0, 255 )
+Color.CYAN = Color( 0, 255, 255 )
+Color.MAGENTA = Color( 255, 0, 255 )
+Color.YELLOW = Color( 255, 255, 0 )
+Color.WHITE = Color( 255, 255, 255 )
+Color.DKRED = Color( 128, 0, 0 )
+Color.DKGREEN = Color( 0, 128, 0 )
+Color.DKBLUE = Color( 0, 0, 128 )
+Color.TEAL = Color( 0, 128, 128 )
+Color.PURPLE = Color( 128, 0, 128 )
+Color.BROWN = Color( 128, 128, 0 )
+Color.GRAY = Color( 128, 128, 128 )
+
+
+class BitMap(object):
+ """class for drawing and saving simple Windows bitmap files"""
+
+ LINE_SOLID = 0
+ LINE_DASHED = 1
+ LINE_DOTTED = 2
+ LINE_DOT_DASH=3
+ _DASH_LEN = 12.0
+ _DOT_LEN = 6.0
+ _DOT_DASH_LEN = _DOT_LEN + _DASH_LEN
+
+ def __init__( self, width, height,
+ bkgd = Color.WHITE, frgd = Color.BLACK ):
+ self.wd = int( ceil(width) )
+ self.ht = int( ceil(height) )
+ self.bgcolor = 0
+ self.fgcolor = 1
+ self.palette = []
+ self.palette.append( bkgd.toLong() )
+ self.palette.append( frgd.toLong() )
+ self.currentPen = self.fgcolor
+
+ tmparray = [ self.bgcolor ] * self.wd
+ self.bitarray = [ tmparray[:] for i in range( self.ht ) ]
+ self.currentPen = 1
+
+
+ def plotPoint( self, x, y ):
+ if ( 0 <= x < self.wd and 0 <= y < self.ht ):
+ x = int(x)
+ y = int(y)
+ self.bitarray[y][x] = self.currentPen
+
+
+ def _saveBitMapNoCompression( self ):
+ line_padding = (4 - (self.wd % 4)) % 4
+
+ # write bitmap header
+ _bitmap = "BM"
+ _bitmap += longToString( 54 + self.ht*(self.wd*3 + line_padding) ) # DWORD size in bytes of the file
+ _bitmap += longToString( 0 ) # DWORD 0
+ _bitmap += longToString( 54 )
+ _bitmap += longToString( 40 ) # DWORD header size = 40
+ _bitmap += longToString( self.wd ) # DWORD image width
+ _bitmap += longToString( self.ht ) # DWORD image height
+ _bitmap += shortToString( 1 ) # WORD planes = 1
+ _bitmap += shortToString( 24 ) # WORD bits per pixel = 8
+ _bitmap += longToString( 0 ) # DWORD compression = 0
+ _bitmap += longToString( self.ht * (self.wd * 3 + line_padding) ) # DWORD sizeimage = size in bytes of the bitmap = width * height
+ _bitmap += longToString( 0 ) # DWORD horiz pixels per meter (?)
+ _bitmap += longToString( 0 ) # DWORD ver pixels per meter (?)
+ _bitmap += longToString( 0 ) # DWORD number of colors used = 256
+ _bitmap += longToString( 0 ) # DWORD number of "import colors = len( self.palette )
+
+ # write pixels
+ self.bitarray.reverse()
+ for row in self.bitarray:
+ for pixel in row:
+ c = self.palette[pixel]
+ _bitmap += long24ToString(c)
+ for i in range(line_padding):
+ _bitmap += chr( 0 )
+
+ return _bitmap
+
+
+
+ def saveFile( self, filename):
+ _b = self._saveBitMapNoCompression( )
+
+ f = file(filename, 'wb')
+ f.write(_b)
+ f.close()
+
+
+
+
+
+
+if __name__ == "__main__":
+
+ bmp = BitMap( 10, 10 )
+ bmp.plotPoint( 5, 5 )
+ bmp.plotPoint( 0, 0 )
+ bmp.saveFile( "test.bmp" )
+
diff --git a/client/electrum4a.py b/client/electrum4a.py
@@ -18,141 +18,237 @@
+
import android
from interface import WalletSynchronizer
from wallet import Wallet
from wallet import format_satoshis
from decimal import Decimal
-
+import mnemonic
import datetime
-droid = android.Android()
-wallet = Wallet()
-wallet.set_path("/sdcard/electrum.dat")
-wallet.read()
-
-def get_history_layout(n):
- rows = ""
- for line in wallet.get_tx_history()[-n:]:
- v = line['value']
- try:
- dt = datetime.datetime.fromtimestamp( line['timestamp'] )
- if dt.date() == dt.today().date():
- time_str = str( dt.time() )
- else:
- time_str = str( dt.date() )
- except:
- print line['timestamp']
- time_str = 'pending'
+def modal_dialog(title, msg = None):
+ droid.dialogCreateAlert(title,msg)
+ droid.dialogSetPositiveButtonText('OK')
+ droid.dialogShow()
+ droid.dialogGetResponse()
+ droid.dialogDismiss()
- label = line.get('label')
- #if not label: label = line['tx_hash']
- is_default_label = (label == '') or (label is None)
- if is_default_label: label = line['default_label']
+def modal_input(title, msg, value = None, etype=None):
+ droid.dialogCreateInput(title, msg, value, etype)
+ droid.dialogSetPositiveButtonText('OK')
+ droid.dialogSetNegativeButtonText('Cancel')
+ droid.dialogShow()
+ response = droid.dialogGetResponse().result
+ droid.dialogDismiss()
+ if response.get('which') == 'positive':
+ return response.get('value')
- rows += """
- <TableRow
- android:layout_width="fill_parent">
- <TextView
- android:layout_column="1"
- android:text="%s"
- android:padding="2px" />
- <TextView
- android:text="%s"
- android:padding="2px" />
- <TextView
- android:text="%s"
- android:gravity="right"
- android:padding="2px" />
- </TableRow>"""%(time_str, ' '+ label[0:10]+ '... ', format_satoshis(v))
+def modal_question(q, msg, pos_text = 'OK', neg_text = 'Cancel'):
+ droid.dialogCreateAlert(q, msg)
+ droid.dialogSetPositiveButtonText(pos_text)
+ droid.dialogSetNegativeButtonText(neg_text)
+ droid.dialogShow()
+ response = droid.dialogGetResponse().result
+ droid.dialogDismiss()
+ return response.get('which') == 'positive'
+
+def edit_label(addr):
+ v = modal_input('Edit label',None,wallet.labels.get(addr))
+ if v is not None:
+ if v:
+ wallet.labels[addr] = v
+ else:
+ if addr in wallet.labels.keys():
+ wallet.labels.pop(addr)
+ wallet.update_tx_history()
+ wallet.save()
+ droid.fullSetProperty("labelTextView", "text", v)
+def select_from_contacts():
+ title = 'Contacts:'
+ droid.dialogCreateAlert(title)
+ l = []
+ for i in range(len(wallet.addressbook)):
+ addr = wallet.addressbook[i]
+ label = wallet.labels.get(addr,addr)
+ l.append( label )
+ droid.dialogSetItems(l)
+ droid.dialogSetPositiveButtonText('New contact')
+ droid.dialogShow()
+ response = droid.dialogGetResponse().result
+ droid.dialogDismiss()
- output = """
-<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:stretchColumns="1">
- %s
-</TableLayout>
-"""% rows
- return output
+ if response.get('which') == 'positive':
+ return 'newcontact'
+ result = response.get('item')
+ print result
+ if result is not None:
+ addr = wallet.addressbook[result]
+ return addr
-def show_addresses():
+def select_from_addresses():
droid.dialogCreateAlert("Addresses:")
l = []
for i in range(len(wallet.addresses)):
addr = wallet.addresses[i]
- l.append( wallet.labels.get(addr,'') + ' ' + addr)
-
+ label = wallet.labels.get(addr,addr)
+ l.append( label )
droid.dialogSetItems(l)
droid.dialogShow()
- response = droid.dialogGetResponse().result
+ response = droid.dialogGetResponse()
+ result = response.result.get('item')
droid.dialogDismiss()
+ if result is not None:
+ addr = wallet.addresses[result]
+ return addr
- # show qr code
- print response
+def protocol_name(p):
+ if p == 't': return 'TCP/stratum'
+ if p == 'h': return 'HTTP/Stratum'
+ if p == 'n': return 'TCP/native'
+
+def protocol_dialog(host, protocol, z):
+ droid.dialogCreateAlert('Protocol',host)
+ if z:
+ protocols = z.keys()
+ else:
+ protocols = ['t','h','n']
+ l = []
+ current = protocols.index(protocol)
+ for p in protocols:
+ l.append(protocol_name(p))
+ droid.dialogSetSingleChoiceItems(l, current)
+ droid.dialogSetPositiveButtonText('OK')
+ droid.dialogSetNegativeButtonText('Cancel')
+ droid.dialogShow()
+ response = droid.dialogGetResponse().result
+ if not response: return
+ if response.get('which') == 'positive':
+ response = droid.dialogGetSelectedItems().result[0]
+ droid.dialogDismiss()
+ p = protocols[response]
+ port = z[p]
+ return host + ':' + port + ':' + p
+
+
+
+
+def make_layout(s, scrollable = False):
+ content = """
+
+ <LinearLayout
+ android:id="@+id/zz"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="#ff222222">
+
+ <TextView
+ android:id="@+id/textElectrum"
+ android:text="Electrum"
+ android:textSize="7pt"
+ android:textColor="#ff4444ff"
+ android:gravity="left"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ />
+ </LinearLayout>
+
+ %s """%s
+
+ if scrollable:
+ content = """
+ <ScrollView
+ android:id="@+id/scrollview"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" >
+
+ %s
+
+ </LinearLayout>
+ </ScrollView>
+ """%content
-def main_layout():
return """<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/background"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="#ff000000">
+ android:background="#ff000022">
+
+ %s
+ </LinearLayout>"""%content
+
+
+
+
+def main_layout():
+ return make_layout("""
+ <TextView android:id="@+id/balanceTextView"
+ android:layout_width="match_parent"
+ android:text=""
+ android:textColor="#ffffffff"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:padding="7dip"
+ android:textSize="8pt"
+ android:gravity="center_vertical|center_horizontal|left">
+ </TextView>
<TextView android:id="@+id/historyTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="Electrum"
+ android:text="Recent transactions"
android:textAppearance="?android:attr/textAppearanceLarge"
android:gravity="center_vertical|center_horizontal|center">
</TextView>
- %s
+ %s """%get_history_layout(15),True)
- <TextView android:id="@+id/balanceTextView"
+
+
+def qr_layout(addr):
+ return make_layout("""
+
+ <TextView android:id="@+id/addrTextView"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text=""
+ android:layout_height="50"
+ android:text="%s"
android:textAppearance="?android:attr/textAppearanceLarge"
- android:gravity="left">
- </TextView>
-
- <LinearLayout android:layout_width="match_parent"
- android:layout_height="wrap_content" android:id="@+id/linearLayout1">
- <Button android:id="@+id/buttonSend" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="Send"></Button>
- <Button android:id="@+id/buttonReceive" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="Receive"></Button>
- <Button android:id="@+id/buttonQuit" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="Quit"></Button>
- </LinearLayout>
+ android:gravity="center_vertical|center_horizontal|center">
+ </TextView>
-</LinearLayout>
-"""%get_history_layout(10)
+ <ImageView
+ android:id="@+id/qrView"
+ android:gravity="center"
+ android:layout_width="match_parent"
+ android:layout_height="350"
+ android:antialias="false"
+ android:src="file:///sdcard/sl4a/qrcode.bmp" />
+ <TextView android:id="@+id/labelTextView"
+ android:layout_width="match_parent"
+ android:layout_height="50"
+ android:text="%s"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:gravity="center_vertical|center_horizontal|center">
+ </TextView>
-payto_layout="""<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/background"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#ff000000">
+ """%(addr,wallet.labels.get(addr,'')), True)
- <LinearLayout android:id="@+id/linearLayout0"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="#44ffffff">
+payto_layout = make_layout("""
<TextView android:id="@+id/recipientTextView"
android:layout_width="match_parent"
@@ -166,21 +262,18 @@ payto_layout="""<?xml version="1.0" encoding="utf-8"?>
<EditText android:id="@+id/recipient"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:tag="Tag Me" android:inputType="textCapWords|textPhonetic|number">
+ android:tag="Tag Me" android:inputType="text">
</EditText>
<LinearLayout android:id="@+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button android:id="@+id/buttonQR" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="Scan QR"></Button>
+ android:layout_height="wrap_content" android:text="From QR code"></Button>
<Button android:id="@+id/buttonContacts" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="Contacts"></Button>
+ android:layout_height="wrap_content" android:text="From Contacts"></Button>
</LinearLayout>
- </LinearLayout>
-
-
<TextView android:id="@+id/labelTextView"
android:layout_width="match_parent"
@@ -193,7 +286,7 @@ payto_layout="""<?xml version="1.0" encoding="utf-8"?>
<EditText android:id="@+id/label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:tag="Tag Me" android:inputType="textCapWords|textPhonetic|number">
+ android:tag="Tag Me" android:inputType="text">
</EditText>
<TextView android:id="@+id/amountLabelTextView"
@@ -214,201 +307,318 @@ payto_layout="""<?xml version="1.0" encoding="utf-8"?>
android:layout_height="wrap_content" android:id="@+id/linearLayout1">
<Button android:id="@+id/buttonPay" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="Send"></Button>
- <Button android:id="@+id/buttonCancelSend" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="Cancel"></Button>
- </LinearLayout>
-</LinearLayout>
-"""
+ </LinearLayout>""",False)
-settings_layout = """<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/background"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#ff000000">
- <TextView android:id="@+id/serverTextView"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="Server:"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:gravity="left">
- </TextView>
+settings_layout = make_layout(""" <ListView
+ android:id="@+id/myListView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />""")
- <EditText android:id="@+id/server"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:tag="Tag Me" android:inputType="*">
- </EditText>
- <LinearLayout android:layout_width="match_parent"
- android:layout_height="wrap_content" android:id="@+id/linearLayout1">
- <Button android:id="@+id/buttonServer" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="Server List"></Button>
- <Button android:id="@+id/buttonSave" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="Save"></Button>
- <Button android:id="@+id/buttonCancel" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="Cancel"></Button>
- </LinearLayout>
-</LinearLayout>
-"""
+def get_history_values(n):
+ values = []
+ h = wallet.get_tx_history()
+ length = min(n, len(h))
+ for i in range(length):
+ line = h[-i-1]
+ v = line['value']
+ try:
+ dt = datetime.datetime.fromtimestamp( line['timestamp'] )
+ if dt.date() == dt.today().date():
+ time_str = str( dt.time() )
+ else:
+ time_str = str( dt.date() )
+ conf = 'v'
+ except:
+ print line['timestamp']
+ time_str = 'pending'
+ conf = 'o'
+ tx_hash = line['tx_hash']
+ label = wallet.labels.get(tx_hash)
+ is_default_label = (label == '') or (label is None)
+ if is_default_label: label = line['default_label']
+ values.append((conf, ' ' + time_str, ' ' + format_satoshis(v,True), ' ' + label ))
+ return values
-def show_balance():
- c, u = wallet.get_balance()
- droid.fullSetProperty("balanceTextView","text","Balance:"+format_satoshis(c))
+
+def get_history_layout(n):
+ rows = ""
+ i = 0
+ values = get_history_values(n)
+ for v in values:
+ a,b,c,d = v
+ color = "#ff00ff00" if a == 'v' else "#ffff0000"
+ rows += """
+ <TableRow>
+ <TextView
+ android:id="@+id/hl_%d_col1"
+ android:layout_column="0"
+ android:text="%s"
+ android:textColor="%s"
+ android:padding="3" />
+ <TextView
+ android:id="@+id/hl_%d_col2"
+ android:layout_column="1"
+ android:text="%s"
+ android:padding="3" />
+ <TextView
+ android:id="@+id/hl_%d_col3"
+ android:layout_column="2"
+ android:text="%s"
+ android:padding="3" />
+ <TextView
+ android:id="@+id/hl_%d_col4"
+ android:layout_column="3"
+ android:text="%s"
+ android:padding="4" />
+ </TableRow>"""%(i,a,color,i,b,i,c,i,d)
+ i += 1
+
+ output = """
+<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:stretchColumns="0,1,2,3">
+ %s
+</TableLayout>"""% rows
+ return output
+
+
+def set_history_layout(n):
+ values = get_history_values(n)
+ i = 0
+ for v in values:
+ a,b,c,d = v
+ droid.fullSetProperty("hl_%d_col1"%i,"text", a)
+
+ if a == 'v':
+ droid.fullSetProperty("hl_%d_col1"%i, "textColor","#ff00ff00")
+ else:
+ droid.fullSetProperty("hl_%d_col1"%i, "textColor","#ffff0000")
+
+ droid.fullSetProperty("hl_%d_col2"%i,"text", b)
+ droid.fullSetProperty("hl_%d_col3"%i,"text", c)
+ droid.fullSetProperty("hl_%d_col4"%i,"text", d)
+ i += 1
+
+
+
+
+status_text = ''
+def update_layout():
+ global status_text
+ if not wallet.interface.is_connected:
+ text = "Not connected..."
+ elif wallet.blocks == 0:
+ text = "Server not ready"
+ elif not wallet.up_to_date:
+ text = "Synchronizing..."
+ else:
+ c, u = wallet.get_balance()
+ text = "Balance:"+format_satoshis(c)
+ if u : text += ' [' + format_satoshis(u,True).strip() + ']'
+
+
+ # vibrate if status changed
+ if text != status_text:
+ if status_text and wallet.interface.is_connected and wallet.up_to_date:
+ droid.vibrate()
+ status_text = text
+
+ droid.fullSetProperty("balanceTextView", "text", status_text)
+
+ if wallet.up_to_date:
+ set_history_layout(15)
-def recipient_dialog():
- title = 'Pay to:'
- message = ('Select recipient')
- droid.dialogCreateAlert(title, message)
- droid.dialogSetItems(wallet.addressbook)
- droid.dialogShow()
- response = droid.dialogGetResponse()
- result = response.result.get('item')
- droid.dialogDismiss()
- if result is not None:
- addr = wallet.addressbook[result]
- return addr
def pay_to(recipient, amount, fee, label):
if wallet.use_encryption:
password = droid.dialogGetPassword('Password').result
- print "password", password
+ if not password: return
else:
password = None
droid.dialogCreateSpinnerProgress("Electrum", "signing transaction...")
droid.dialogShow()
- tx = wallet.mktx( recipient, amount, label, password, fee)
- print tx
- droid.dialogDismiss()
- if tx:
- r, h = wallet.sendtx( tx )
- droid.dialogCreateAlert('tx sent', h)
- droid.dialogSetPositiveButtonText('OK')
- droid.dialogShow()
- response = droid.dialogGetResponse().result
+ try:
+ tx = wallet.mktx( recipient, amount, label, password, fee)
+ except BaseException, e:
+ modal_dialog('error', e.message)
droid.dialogDismiss()
- return h
- else:
- return 'error'
-
+ return
+ droid.dialogDismiss()
+ r, h = wallet.sendtx( tx )
+ if r:
+ modal_dialog('Payment sent', h)
+ return True
+ else:
+ modal_dialog('Error', h)
-if not wallet.file_exists:
- droid.dialogCreateAlert("wallet file not found")
- droid.dialogSetPositiveButtonText('OK')
- droid.dialogShow()
- resp = droid.dialogGetResponse().result
- print resp
- code = droid.scanBarcode()
- r = code.result
- if r:
- seed = r['extras']['SCAN_RESULT']
- else:
- exit(1)
+def recover():
- droid.dialogCreateAlert('seed', seed)
- droid.dialogSetPositiveButtonText('OK')
+ droid.dialogCreateAlert("Wallet not found","Do you want to create a new wallet, or restore an existing one?")
+ droid.dialogSetPositiveButtonText('Create')
+ droid.dialogSetNeutralButtonText('Restore')
droid.dialogSetNegativeButtonText('Cancel')
droid.dialogShow()
response = droid.dialogGetResponse().result
droid.dialogDismiss()
- print response
+ if response.get('which') == 'negative':
+ exit(1)
- wallet.seed = str(seed)
- wallet.init_mpk( wallet.seed )
- droid.dialogCreateSpinnerProgress("Electrum", "recovering keys")
+ is_recovery = response.get('which') == 'neutral'
+
+ if not is_recovery:
+ wallet.new_seed(None)
+ else:
+ if modal_question("Input method",None,'QR Code', 'mnemonic'):
+ code = droid.scanBarcode()
+ r = code.result
+ if r:
+ seed = r['extras']['SCAN_RESULT']
+ else:
+ exit(1)
+ else:
+ m = modal_input('Mnemonic','please enter your code')
+ try:
+ seed = mnemonic.mn_decode(m.split(' '))
+ except:
+ modal_dialog('error: could not decode this seed')
+ exit(1)
+
+ wallet.seed = str(seed)
+
+ modal_dialog('Your seed is:', wallet.seed)
+ modal_dialog('Mnemonic code:', ' '.join(mnemonic.mn_encode(wallet.seed)) )
+
+ msg = "recovering wallet..." if is_recovery else "creating wallet..."
+ droid.dialogCreateSpinnerProgress("Electrum", msg)
droid.dialogShow()
+
+ wallet.init_mpk( wallet.seed )
WalletSynchronizer(wallet,True).start()
wallet.update()
- wallet.save()
+
droid.dialogDismiss()
droid.vibrate()
- if wallet.is_found():
- # history and addressbook
- wallet.update_tx_history()
- wallet.fill_addressbook()
- droid.dialogCreateAlert("recovery successful")
- droid.dialogShow()
- wallet.save()
- else:
- droid.dialogCreateSpinnerProgress("wallet not found")
- droid.dialogShow()
- exit(1)
+ if is_recovery:
+ if wallet.is_found():
+ wallet.update_tx_history()
+ wallet.fill_addressbook()
+ modal_dialog("recovery successful")
+ else:
+ if not modal_question("no transactions found for this seed","do you want to keep this wallet?"):
+ exit(1)
-else:
- droid.dialogCreateSpinnerProgress("Electrum", "synchronizing")
- droid.dialogShow()
- WalletSynchronizer(wallet,True).start()
- wallet.update()
+ change_password_dialog()
wallet.save()
- droid.dialogDismiss()
- droid.vibrate()
-def add_menu():
- droid.addOptionsMenuItem("Settings","settings",None,"")
- droid.addOptionsMenuItem("Quit","quit",None,"")
-add_menu()
+def make_new_contact():
+ code = droid.scanBarcode()
+ r = code.result
+ if r:
+ address = r['extras']['SCAN_RESULT']
+ if address:
+ if wallet.is_valid(address):
+ if modal_question('Add to contacts?', address):
+ wallet.addressbook.append(address)
+ wallet.save()
+ else:
+ modal_dialog('Invalid address', address)
+
+
+do_refresh = False
+def update_callback():
+ global do_refresh
+ print "gui callback", wallet.interface.is_connected, wallet.up_to_date
+ do_refresh = True
+ droid.eventPost("refresh",'z')
def main_loop():
- droid.fullShow(main_layout())
- show_balance()
+ global do_refresh
+
+ update_layout()
out = None
+ quitting = False
while out is None:
- event = droid.eventWait().result
- print "got event in main loop", event
+ event = droid.eventWait(1000).result
+ if event is None:
+ if do_refresh:
+ update_layout()
+ do_refresh = False
+ continue
- if event["name"]=="click":
- id=event["data"]["id"]
- if id=="buttonQuit":
- out = 'exit'
+ print "got event in main loop", repr(event)
+ if event == 'OK': continue
+ if event is None: continue
+ #if event["name"]=="refresh":
- elif id=="buttonSend":
- out = 'payto'
- elif id=="buttonReceive":
- show_addresses()
+ # request 2 taps before we exit
+ if event["name"]=="key":
+ if event["data"]["key"] == '4':
+ if quitting:
+ out = 'quit'
+ else:
+ quitting = True
+ else: quitting = False
- elif id=="buttonQuit":
- out = 'quit'
+ if event["name"]=="click":
+ id=event["data"]["id"]
elif event["name"]=="settings":
out = 'settings'
- elif event["name"]=="quit":
- out = 'quit'
+ elif event["name"] in menu_commands:
+ out = event["name"]
+
+ if out == 'contacts':
+ global contact_addr
+ contact_addr = select_from_contacts()
+ if contact_addr == 'newcontact':
+ make_new_contact()
+ contact_addr = None
+ if not contact_addr:
+ out = None
+
+ elif out == "receive":
+ global receive_addr
+ receive_addr = select_from_addresses()
+ if not receive_addr:
+ out = None
- # print droid.fullSetProperty("background","backgroundColor","0xff7f0000")
- # elif event["name"]=="screen":
- # if event["data"]=="destroy":
- # out = 'exit'
return out
+
def payto_loop():
- droid.fullShow(payto_layout)
+ global recipient
+ if recipient:
+ droid.fullSetProperty("recipient","text",recipient)
+ recipient = None
+
out = None
while out is None:
event = droid.eventWait().result
@@ -423,20 +633,23 @@ def payto_loop():
recipient = droid.fullQueryDetail("recipient").result.get('text')
label = droid.fullQueryDetail("label").result.get('text')
amount = droid.fullQueryDetail('amount').result.get('text')
- fee = '0.001'
- amount = int( 100000000 * Decimal(amount) )
- fee = int( 100000000 * Decimal(fee) )
- result = pay_to(recipient, amount, fee, label)
-
- droid.dialogCreateAlert('result', result)
- droid.dialogSetPositiveButtonText('OK')
- droid.dialogShow()
- droid.dialogGetResponse()
- droid.dialogDismiss()
- out = 'main'
+
+ if not wallet.is_valid(recipient):
+ modal_dialog('Error','Invalid Bitcoin address')
+ continue
+
+ try:
+ amount = int( 100000000 * Decimal(amount) )
+ except:
+ modal_dialog('Error','Invalid amount')
+ continue
+
+ result = pay_to(recipient, amount, wallet.fee, label)
+ if result:
+ out = 'main'
elif id=="buttonContacts":
- addr = recipient_dialog()
+ addr = select_from_contacts()
droid.fullSetProperty("recipient","text",addr)
elif id=="buttonQR":
@@ -447,14 +660,12 @@ def payto_loop():
if addr:
droid.fullSetProperty("recipient","text",addr)
- elif id=="buttonCancelSend":
- out = 'main'
+ elif event["name"] in menu_commands:
+ out = event["name"]
- elif event["name"]=="settings":
- out = 'settings'
-
- elif event["name"]=="quit":
- out = 'quit'
+ elif event["name"]=="key":
+ if event["data"]["key"] == '4':
+ out = 'main'
#elif event["name"]=="screen":
# if event["data"]=="destroy":
@@ -463,116 +674,308 @@ def payto_loop():
return out
-def history_loop():
- layout = get_history_layout()
- droid.fullShow(layout)
+receive_addr = ''
+contact_addr = ''
+recipient = ''
+
+def receive_loop():
out = None
while out is None:
event = droid.eventWait().result
- print "got event in history loop", event
- if event["name"] == "click":
-
- if event["data"]["text"] == "OK":
+ print "got event", event
+ if event["name"]=="key":
+ if event["data"]["key"] == '4':
out = 'main'
- elif event["name"] == "key":
- print repr(event["data"]["key"])
+ elif event["name"]=="clipboard":
+ droid.setClipboard(receive_addr)
+ modal_dialog('Address copied to clipboard',receive_addr)
+
+ elif event["name"]=="edit":
+ edit_label(receive_addr)
+
+ return out
+
+def contacts_loop():
+ global recipient
+ out = None
+ while out is None:
+ event = droid.eventWait().result
+ print "got event", event
+ if event["name"]=="key":
if event["data"]["key"] == '4':
out = 'main'
- #elif event["name"]=="screen":
- # if event["data"]=="destroy":
- # out = 'main'
+ elif event["name"]=="clipboard":
+ droid.setClipboard(contact_addr)
+ modal_dialog('Address copied to clipboard',contact_addr)
+
+ elif event["name"]=="edit":
+ edit_label(contact_addr)
+
+ elif event["name"]=="paytocontact":
+ recipient = contact_addr
+ out = 'send'
+
+ elif event["name"]=="deletecontact":
+ if modal_question('delete contact', contact_addr):
+ out = 'main'
return out
+
def server_dialog(plist):
- droid.dialogCreateAlert("servers")
+ droid.dialogCreateAlert("Public servers")
droid.dialogSetItems( plist.keys() )
+ droid.dialogSetPositiveButtonText('Private server')
droid.dialogShow()
- i = droid.dialogGetResponse().result.get('item')
+ response = droid.dialogGetResponse().result
droid.dialogDismiss()
+
+ if response.get('which') == 'positive':
+ return modal_input('Private server', None)
+
+ i = response.get('item')
if i is not None:
response = plist.keys()[i]
return response
-def protocol_dialog(plist):
- options=["TCP","HTTP","native"]
- droid.dialogCreateAlert("Protocol")
- droid.dialogSetSingleChoiceItems(options)
+def seed_dialog():
+ if wallet.use_encryption:
+ password = droid.dialogGetPassword('Seed').result
+ if not password: return
+ else:
+ password = None
+
+ try:
+ seed = wallet.pw_decode( wallet.seed, password)
+ except:
+ modal_dialog('error','incorrect password')
+ return
+
+ modal_dialog('Your seed is',seed)
+ modal_dialog('Mnemonic code:', ' '.join(mnemonic.mn_encode(seed)) )
+
+def change_password_dialog():
+ if wallet.use_encryption:
+ password = droid.dialogGetPassword('Your wallet is encrypted').result
+ if password is None: return
+ else:
+ password = None
+
+ try:
+ seed = wallet.pw_decode( wallet.seed, password)
+ except:
+ modal_dialog('error','incorrect password')
+ return
+
+ new_password = droid.dialogGetPassword('Choose a password').result
+ if new_password == None:
+ return
+
+ if new_password != '':
+ password2 = droid.dialogGetPassword('Confirm new password').result
+ if new_password != password2:
+ modal_dialog('error','passwords do not match')
+ return
+
+ wallet.update_password(seed, new_password)
+ if new_password:
+ modal_dialog('Password updated','your wallet is encrypted')
+ else:
+ modal_dialog('No password','your wallet is not encrypted')
+ return True
def settings_loop():
- droid.fullShow(settings_layout)
- droid.fullSetProperty("server","text",wallet.server)
- out = None
- while out is None:
- event = droid.eventWait().result
- if event["name"] == "click":
- id = event["data"]["id"]
+ def set_listview():
+ server, port, p = wallet.server.split(':')
+ fee = str( Decimal( wallet.fee)/100000000 )
+ is_encrypted = 'yes' if wallet.use_encryption else 'no'
+ protocol = protocol_name(p)
+ droid.fullShow(settings_layout)
+ droid.fullSetList("myListView",['Server: ' + server, 'Protocol: '+ protocol, 'Port: '+port, 'Transaction fee: '+fee, 'Password: '+is_encrypted, 'Seed'])
- if id=="buttonServer":
- plist = {}
- for item in wallet.interface.servers:
- host, pp = item
- z = {}
- for item2 in pp:
- protocol, port = item2
- z[protocol] = port
- plist[host] = z
+ set_listview()
+ out = None
+ while out is None:
+ event = droid.eventWait().result
+ print "got event", event
+ if event == 'OK': continue
+ if not event: continue
+
+ plist = {}
+ for item in wallet.interface.servers:
+ host, pp = item
+ z = {}
+ for item2 in pp:
+ protocol, port = item2
+ z[protocol] = port
+ plist[host] = z
+
+ if event["name"] == "itemclick":
+ pos = event["data"]["position"]
+ host, port, protocol = wallet.server.split(':')
+
+ if pos == "0": #server
host = server_dialog(plist)
- p = plist[host]
- port = p['t']
- srv = host + ':' + port + ':t'
- droid.fullSetProperty("server","text",srv)
-
- elif id=="buttonSave":
- droid.fullQuery()
- srv = droid.fullQueryDetail("server").result.get('text')
- try:
- wallet.set_server(srv)
- out = 'main'
- except:
- droid.dialogCreateAlert('error')
- droid.dialogSetPositiveButtonText('OK')
- droid.dialogShow()
- droid.dialogGetResponse()
- droid.dialogDismiss()
-
- elif id=="buttonCancel":
- out = 'main'
+ if host:
+ p = plist[host]
+ port = p['t']
+ srv = host + ':' + port + ':t'
+ try:
+ wallet.set_server(srv)
+ except:
+ modal_dialog('error','invalid server')
+ set_listview()
+
+ elif pos == "1": #protocol
+ if host in plist:
+ srv = protocol_dialog(host, protocol, plist[host])
+ if srv:
+ try:
+ wallet.set_server(srv)
+ except:
+ modal_dialog('error','invalid server')
+ set_listview()
+
+ elif pos == "2": #port
+ a_port = modal_input('Port number', 'If you use a public server, this field is set automatically when you set the protocol', port, "number")
+ if a_port:
+ if a_port != port:
+ srv = host + ':' + a_port + ':'+ protocol
+ try:
+ wallet.set_server(srv)
+ except:
+ modal_dialog('error','invalid port number')
+ set_listview()
+
+ elif pos == "3": #fee
+ fee = modal_input('Transaction fee', 'The fee will be this amount multiplied by the number of inputs in your transaction. ', str( Decimal( wallet.fee)/100000000 ), "numberDecimal")
+ if fee:
+ try:
+ fee = int( 100000000 * Decimal(fee) )
+ except:
+ modal_dialog('error','invalid fee value')
+ if wallet.fee != fee:
+ wallet.fee = fee
+ wallet.save()
+ set_listview()
+
+ elif pos == "4":
+ if change_password_dialog():
+ set_listview()
+
+ elif pos == "5":
+ seed_dialog()
+
+
+ elif event["name"] in menu_commands:
+ out = event["name"]
+
+ elif event["name"] == 'cancel':
+ out = 'main'
elif event["name"] == "key":
- print repr(event["data"]["key"])
if event["data"]["key"] == '4':
out = 'main'
- elif event["name"]=="quit":
- out = 'quit'
-
return out
-
+
+
+
+menu_commands = ["send", "receive", "settings", "contacts", "main"]
+droid = android.Android()
+wallet = Wallet(update_callback)
+
+wallet.set_path("/sdcard/electrum.dat")
+wallet.read()
+if not wallet.file_exists:
+ recover()
+else:
+ WalletSynchronizer(wallet,True).start()
s = 'main'
+
+def add_menu(s):
+ droid.clearOptionsMenu()
+ if s == 'main':
+ droid.addOptionsMenuItem("Send","send",None,"")
+ droid.addOptionsMenuItem("Receive","receive",None,"")
+ droid.addOptionsMenuItem("Contacts","contacts",None,"")
+ droid.addOptionsMenuItem("Settings","settings",None,"")
+ elif s == 'receive':
+ droid.addOptionsMenuItem("Copy","clipboard",None,"")
+ droid.addOptionsMenuItem("Label","edit",None,"")
+ elif s == 'contacts':
+ droid.addOptionsMenuItem("Copy","clipboard",None,"")
+ droid.addOptionsMenuItem("Label","edit",None,"")
+ droid.addOptionsMenuItem("Pay to","paytocontact",None,"")
+ #droid.addOptionsMenuItem("Delete","deletecontact",None,"")
+
+def make_bitmap(addr):
+ # fixme: this is highly inefficient
+ droid.dialogCreateSpinnerProgress("please wait")
+ droid.dialogShow()
+ import pyqrnative, bmp
+ qr = pyqrnative.QRCode(4, pyqrnative.QRErrorCorrectLevel.H)
+ qr.addData(addr)
+ qr.make()
+ k = qr.getModuleCount()
+ bitmap = bmp.BitMap( 35*8, 35*8 )
+ print len(bitmap.bitarray)
+ bitmap.bitarray = []
+ assert k == 33
+
+ for r in range(35):
+ tmparray = [ 0 ] * 35*8
+
+ if 0 < r < 34:
+ for c in range(k):
+ if qr.isDark(r-1, c):
+ tmparray[ (1+c)*8:(2+c)*8] = [1]*8
+
+ for i in range(8):
+ bitmap.bitarray.append( tmparray[:] )
+
+ bitmap.saveFile( "/sdcard/sl4a/qrcode.bmp" )
+ droid.dialogDismiss()
+
+
+
while True:
+ add_menu(s)
if s == 'main':
+ droid.fullShow(main_layout())
s = main_loop()
- elif s == 'payto':
+ #droid.fullDismiss()
+
+ elif s == 'send':
+ droid.fullShow(payto_layout)
s = payto_loop()
- elif s == 'settings':
- s = settings_loop()
- elif s == 'history':
- s = history_loop()
+ #droid.fullDismiss()
+
+ elif s == 'receive':
+ make_bitmap(receive_addr)
+ droid.fullShow(qr_layout(receive_addr))
+ s = receive_loop()
+
elif s == 'contacts':
+ make_bitmap(contact_addr)
+ droid.fullShow(qr_layout(contact_addr))
s = contacts_loop()
+
+ elif s == 'settings':
+ #droid.fullShow(settings_layout)
+ s = settings_loop()
+ #droid.fullDismiss()
else:
break
-droid.fullDismiss()
droid.makeToast("Bye!")
diff --git a/client/electrum_text_320.png b/client/electrum_text_320.png
Binary files differ.
diff --git a/client/gui.py b/client/gui.py
@@ -1111,7 +1111,7 @@ class ElectrumWindow:
self.network_button.set_tooltip_text("Connected to %s:%d.\n%d blocks\nresponse time: %f"%(interface.host, interface.port, self.wallet.blocks, interface.rtime))
c, u = self.wallet.get_balance()
text = "Balance: %s "%( format_satoshis(c) )
- if u: text += "[%s unconfirmed]"%( format_satoshis(u,True) )
+ if u: text += "[%s unconfirmed]"%( format_satoshis(u,True).strip() )
else:
self.status_image.set_from_stock(gtk.STOCK_NO, gtk.ICON_SIZE_MENU)
self.network_button.set_tooltip_text("Trying to contact %s.\n%d blocks"%(interface.host, self.wallet.blocks))
diff --git a/client/gui_qt.py b/client/gui_qt.py
@@ -100,8 +100,6 @@ class QRCodeWidget(QWidget):
super(QRCodeWidget, self).__init__()
self.addr = addr
self.setGeometry(300, 300, 350, 350)
- self.setWindowTitle('Colors')
- self.show()
self.qr = pyqrnative.QRCode(4, pyqrnative.QRErrorCorrectLevel.H)
self.qr.addData(addr)
self.qr.make()
@@ -144,6 +142,8 @@ class ElectrumWindow(QMainWindow):
def __init__(self, wallet):
QMainWindow.__init__(self)
self.wallet = wallet
+ self.wallet.gui_callback = self.update_callback
+
self.funds_error = False
self.tabs = tabs = QTabWidget(self)
@@ -164,11 +164,11 @@ class ElectrumWindow(QMainWindow):
QShortcut(QKeySequence("Ctrl+Q"), self, self.close)
QShortcut(QKeySequence("Ctrl+PgUp"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() - 1 )%tabs.count() ))
QShortcut(QKeySequence("Ctrl+PgDown"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() + 1 )%tabs.count() ))
-
+
+ self.connect(self, QtCore.SIGNAL('updatesignal'), self.update_wallet)
def connect_slots(self, sender):
- self.connect(sender, QtCore.SIGNAL('timersignal'), self.update_wallet)
self.connect(sender, QtCore.SIGNAL('timersignal'), self.check_recipient)
self.previous_payto_e=''
@@ -189,6 +189,9 @@ class ElectrumWindow(QMainWindow):
self.payto_e.setText(s)
+ def update_callback(self):
+ self.emit(QtCore.SIGNAL('updatesignal'))
+
def update_wallet(self):
if self.wallet.interface.is_connected:
if self.wallet.blocks == -1:
@@ -203,7 +206,7 @@ class ElectrumWindow(QMainWindow):
else:
c, u = self.wallet.get_balance()
text = "Balance: %s "%( format_satoshis(c) )
- if u: text += "[%s unconfirmed]"%( format_satoshis(u,True) )
+ if u: text += "[%s unconfirmed]"%( format_satoshis(u,True).strip() )
icon = QIcon(":icons/status_connected.png")
else:
text = "Not connected"
@@ -215,8 +218,7 @@ class ElectrumWindow(QMainWindow):
self.statusBar().showMessage(text)
self.status_button.setIcon( icon )
- if self.wallet.was_updated and self.wallet.up_to_date:
- self.wallet.was_updated = False
+ if self.wallet.up_to_date:
self.textbox.setText( self.wallet.banner )
self.update_history_tab()
self.update_receive_tab()
@@ -522,17 +524,7 @@ class ElectrumWindow(QMainWindow):
addr = unicode( i.text(0) )
return addr
- def showqrcode(address):
- if not address: return
- d = QDialog(self)
- d.setModal(1)
- d.setMinimumSize(270, 300)
- vbox = QVBoxLayout()
- vbox.addWidget(QRCodeWidget(address))
- vbox.addLayout(ok_cancel_buttons(d))
- d.setLayout(vbox)
- d.exec_()
- qrButton = EnterButton("QR",lambda: showqrcode(get_addr(l)))
+ qrButton = EnterButton("QR",lambda: ElectrumWindow.showqrcode(get_addr(l)))
def copy2clipboard(addr):
self.app.clipboard().setText(addr)
@@ -665,19 +657,20 @@ class ElectrumWindow(QMainWindow):
+ ' '.join(mnemonic.mn_encode(seed)) + "\""
QMessageBox.information(parent, 'Seed', msg, 'OK')
+ ElectrumWindow.showqrcode(seed)
- def showqrcode(address):
- if not address: return
- d = QDialog(None)
- d.setModal(1)
- d.setMinimumSize(270, 300)
- vbox = QVBoxLayout()
- vbox.addWidget(QRCodeWidget(address))
- vbox.addLayout(ok_cancel_buttons(d))
- d.setLayout(vbox)
- d.exec_()
- showqrcode(seed)
-
+ @staticmethod
+ def showqrcode(address):
+ if not address: return
+ d = QDialog(None)
+ d.setModal(1)
+ d.setWindowTitle(address)
+ d.setMinimumSize(270, 300)
+ vbox = QVBoxLayout()
+ vbox.addWidget(QRCodeWidget(address))
+ vbox.addLayout(ok_cancel_buttons(d))
+ d.setLayout(vbox)
+ d.exec_()
def question(self, msg):
return QMessageBox.question(self, 'Message', msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes
@@ -1059,4 +1052,6 @@ class ElectrumGui():
if url: w.set_url(url)
w.app = self.app
w.connect_slots(s)
+ w.update_wallet()
+
self.app.exec_()
diff --git a/client/interface.py b/client/interface.py
@@ -316,6 +316,7 @@ class TcpStratumInterface(Interface):
traceback.print_exc(file=sys.stdout)
self.is_connected = False
+ print "poking"
self.poke()
def send(self, messages):
@@ -448,27 +449,33 @@ class WalletSynchronizer(threading.Thread):
def run(self):
import socket, time
while True:
- try:
- while self.interface.is_connected:
- new_addresses = self.wallet.synchronize()
- if new_addresses:
- self.interface.subscribe(new_addresses)
- for addr in new_addresses:
- with self.wallet.lock:
- self.wallet.addresses_waiting_for_status.append(addr)
-
- if self.wallet.is_up_to_date():
+ while self.interface.is_connected:
+ new_addresses = self.wallet.synchronize()
+ if new_addresses:
+ self.interface.subscribe(new_addresses)
+ for addr in new_addresses:
+ with self.wallet.lock:
+ self.wallet.addresses_waiting_for_status.append(addr)
+
+ if self.wallet.is_up_to_date():
+ if not self.wallet.up_to_date:
self.wallet.up_to_date = True
+ self.wallet.was_updated = True
self.wallet.up_to_date_event.set()
- else:
+ else:
+ if self.wallet.up_to_date:
self.wallet.up_to_date = False
+ self.wallet.was_updated = True
- response = self.interface.responses.get()#True,100000000000) # workaround so that it can be keyboard interrupted
- self.handle_response(response)
- except socket.error:
- print "socket error"
- wallet.interface.is_connected = False
+ if self.wallet.was_updated:
+ self.wallet.gui_callback()
+ self.wallet.was_updated = False
+
+ response = self.interface.responses.get()
+ self.handle_response(response)
+ print "disconnected, gui callback"
+ self.wallet.gui_callback()
if self.loop:
time.sleep(5)
self.start_interface()
diff --git a/client/wallet.py b/client/wallet.py
@@ -242,10 +242,11 @@ from interface import DEFAULT_SERVERS
class Wallet:
- def __init__(self):
+ def __init__(self, gui_callback = lambda: None):
self.electrum_version = ELECTRUM_VERSION
self.seed_version = SEED_VERSION
+ self.gui_callback = gui_callback
self.gap_limit = 5 # configuration
self.fee = 100000
@@ -339,7 +340,7 @@ class Wallet:
def new_seed(self, password):
seed = "%032x"%ecdsa.util.randrange( pow(2,128) )
- self.init_mpk(seed)
+ #self.init_mpk(seed)
# encrypt
self.seed = self.pw_encode( seed, password )
@@ -849,7 +850,8 @@ class Wallet:
return target, signing_addr, auth_name
def update_password(self, seed, new_password):
- self.use_encryption = (new_password != '')
+ if new_password == '': new_password = None
+ self.use_encryption = (new_password != None)
self.seed = self.pw_encode( seed, new_password)
for k in self.imported_keys.keys():
a = self.imported_keys[k]