From bcbdc7539a3ad090f10012ba70736d31f9c6ee0f Mon Sep 17 00:00:00 2001 From: ThomasV Date: Wed, 4 Apr 2012 18:47:17 +0200 Subject: [PATCH 01/45] android client; interactive layout --- client/electrum4a.py | 275 ++++++++++++++++++++++++++++--------------- 1 file changed, 178 insertions(+), 97 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index 3e8d8244..614adb81 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -34,51 +34,8 @@ 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' - - 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'] - - rows += """ - - - - - """%(time_str, ' '+ label[0:10]+ '... ', format_satoshis(v)) - output = """ - - %s - -"""% rows - return output @@ -98,23 +55,26 @@ def show_addresses(): print response +title = """ + + +""" def main_layout(): return """ - - - + android:background="#ff000022"> %s @@ -123,21 +83,43 @@ def main_layout(): android:layout_height="wrap_content" android:text="" android:textAppearance="?android:attr/textAppearanceLarge" - android:gravity="left"> + android:gravity="left" + android:textColor="0xffffffff" + android:padding="10" + android:textSize="18" > - - - - - + + + + + %s + + + + + + + -"""%get_history_layout(10) +"""%(title, get_history_layout(15)) payto_layout=""" @@ -146,13 +128,9 @@ payto_layout=""" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="#ff000000"> + android:background="#ff000022"> - + %s android:layout_height="wrap_content" android:text="Contacts"> - - - android:layout_height="wrap_content" android:text="Cancel"> -""" +"""%title settings_layout = """ @@ -229,6 +204,8 @@ settings_layout = """ android:layout_height="match_parent" android:background="#ff000000"> + %s + -""" +"""%title +def get_history_values(n): + values = [] + h = wallet.get_tx_history() + for i in range(n): + 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' + + 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'] + values.append((conf, ' ' + time_str, ' ' + format_satoshis(v,True), ' ' + label )) + + return values + + +def get_history_layout(n): + rows = "" + i = 0 + values = get_history_values(n) + for v in values: + a,b,c,d = v + color = "0xff00ff00" if a == 'v' else "0xffff0000" + rows += """ + + + + + + """%(i,a,color,i,b,i,c,i,d) + i += 1 + + output = """ + + %s +"""% 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","0xff00ff00") + else: + droid.fullSetProperty("hl_%d_col1"%i, "textColor","0xffff0000") + + 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 + + +def update_layout(): + + 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)+']' + + droid.fullSetProperty("balanceTextView", "text", text) + + if wallet.was_updated and wallet.up_to_date: + wallet.was_updated = False + set_history_layout(15) + droid.vibrate() -def show_balance(): - c, u = wallet.get_balance() - droid.fullSetProperty("balanceTextView","text","Balance:"+format_satoshis(c)) def recipient_dialog(): @@ -334,7 +416,7 @@ if not wallet.file_exists: wallet.seed = str(seed) wallet.init_mpk( wallet.seed ) - droid.dialogCreateSpinnerProgress("Electrum", "recovering keys") + droid.dialogCreateSpinnerProgress("Electrum", "recovering wallet...") droid.dialogShow() WalletSynchronizer(wallet,True).start() wallet.update() @@ -355,13 +437,7 @@ if not wallet.file_exists: exit(1) else: - droid.dialogCreateSpinnerProgress("Electrum", "synchronizing") - droid.dialogShow() WalletSynchronizer(wallet,True).start() - wallet.update() - wallet.save() - droid.dialogDismiss() - droid.vibrate() def add_menu(): @@ -373,30 +449,33 @@ add_menu() def main_loop(): droid.fullShow(main_layout()) - show_balance() + update_layout() out = None while out is None: - event = droid.eventWait().result + event = droid.eventWait(1000).result # wait for 1 second + if not event: + update_layout() + continue + print "got event in main loop", event if event["name"]=="click": id=event["data"]["id"] - if id=="buttonQuit": - out = 'exit' - elif id=="buttonSend": + if id=="buttonSend": out = 'payto' elif id=="buttonReceive": show_addresses() - elif id=="buttonQuit": - out = 'quit' - elif event["name"]=="settings": out = 'settings' + elif event["name"]=="key": + if event["data"]["key"] == '4': + out = 'quit' + elif event["name"]=="quit": out = 'quit' @@ -456,6 +535,10 @@ def payto_loop(): 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": # out = 'main' @@ -464,7 +547,7 @@ def payto_loop(): def history_loop(): - layout = get_history_layout() + layout = get_history_layout(15) droid.fullShow(layout) out = None while out is None: @@ -475,8 +558,7 @@ def history_loop(): if event["data"]["text"] == "OK": out = 'main' - elif event["name"] == "key": - print repr(event["data"]["key"]) + elif event["name"]=="key": if event["data"]["key"] == '4': out = 'main' @@ -547,7 +629,6 @@ def settings_loop(): out = 'main' elif event["name"] == "key": - print repr(event["data"]["key"]) if event["data"]["key"] == '4': out = 'main' From f08172d0e8c7610f230bc20a1c9c26a198679e1c Mon Sep 17 00:00:00 2001 From: ThomasV Date: Wed, 4 Apr 2012 20:42:40 +0200 Subject: [PATCH 02/45] scrollview --- client/electrum4a.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index 614adb81..b1b71b83 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -69,10 +69,14 @@ title = """ def main_layout(): return """ - + + @@ -101,7 +105,7 @@ def main_layout(): %s @@ -119,6 +123,7 @@ def main_layout(): + """%(title, get_history_layout(15)) From 570b3413332dd24d31641b1c8821ef4034e0333a Mon Sep 17 00:00:00 2001 From: ThomasV Date: Wed, 4 Apr 2012 20:44:21 +0200 Subject: [PATCH 03/45] scrollview --- client/electrum4a.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/client/electrum4a.py b/client/electrum4a.py index b1b71b83..f37d3033 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -109,6 +109,11 @@ def main_layout(): android:layout_height="wrap_content" android:id="@+id/linearLayout1"> + + From e00c656ee6d5352f4046c798de31f9264d4cf500 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Wed, 4 Apr 2012 22:04:43 +0200 Subject: [PATCH 04/45] layouts --- client/electrum4a.py | 161 ++++++++++++++++++++++++++++++------------- 1 file changed, 112 insertions(+), 49 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index f37d3033..c980615e 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -36,10 +36,20 @@ wallet.read() +def select_from_contacts(): + title = 'Contacts:' + droid.dialogCreateAlert(title) + 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 show_addresses(): +def select_from_addresses(): droid.dialogCreateAlert("Addresses:") l = [] for i in range(len(wallet.addresses)): @@ -48,17 +58,19 @@ def show_addresses(): 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 title = """ - - - - -"""%title - + """) @@ -362,19 +387,6 @@ def update_layout(): -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): @@ -456,7 +468,8 @@ else: def add_menu(): - droid.addOptionsMenuItem("Settings","settings",None,"") + droid.addOptionsMenuItem("Network","settings",None,"") + droid.addOptionsMenuItem("New contact","newcontact",None,"") droid.addOptionsMenuItem("Quit","quit",None,"") add_menu() @@ -481,12 +494,45 @@ def main_loop(): if id=="buttonSend": out = 'payto' + if id=="buttonContacts": + global contact_addr + contact_addr = select_from_contacts() + if contact_addr: + out = 'contacts' + elif id=="buttonReceive": - show_addresses() + global receive_addr + receive_addr = select_from_addresses() + if receive_addr: + out = 'receive' elif event["name"]=="settings": out = 'settings' + elif event["name"]=="newcontact": + code = droid.scanBarcode() + r = code.result + if r: + address = r['extras']['SCAN_RESULT'] + if address: + if wallet.is_valid(address): + droid.dialogCreateAlert('Add to contacts?', address) + droid.dialogSetPositiveButtonText('OK') + droid.dialogSetNegativeButtonText('Cancel') + droid.dialogShow() + response = droid.dialogGetResponse().result + droid.dialogDismiss() + print response + if response.get('which') == 'positive': + wallet.addressbook.append(address) + wallet.save() + else: + droid.dialogCreateAlert('Invalid address', address) + droid.dialogSetPositiveButtonText('OK') + droid.dialogShow() + response = droid.dialogGetResponse().result + droid.dialogDismiss() + elif event["name"]=="key": if event["data"]["key"] == '4': out = 'quit' @@ -494,11 +540,6 @@ def main_loop(): elif event["name"]=="quit": out = 'quit' - # print droid.fullSetProperty("background","backgroundColor","0xff7f0000") - # elif event["name"]=="screen": - # if event["data"]=="destroy": - # out = 'exit' - return out def payto_loop(): @@ -530,7 +571,7 @@ def payto_loop(): out = 'main' elif id=="buttonContacts": - addr = recipient_dialog() + addr = select_from_contacts() droid.fullSetProperty("recipient","text",addr) elif id=="buttonQR": @@ -561,13 +602,17 @@ def payto_loop(): return out -def history_loop(): - layout = get_history_layout(15) - droid.fullShow(layout) +receive_addr = '' +contact_addr = '' + + +def receive_loop(): + droid.fullShow(receive_layout) + droid.fullSetProperty("receiveTextView","text", receive_addr) out = None while out is None: event = droid.eventWait().result - print "got event in history loop", event + print "got event", event if event["name"] == "click": if event["data"]["text"] == "OK": @@ -577,12 +622,27 @@ def history_loop(): if event["data"]["key"] == '4': out = 'main' - #elif event["name"]=="screen": - # if event["data"]=="destroy": - # out = 'main' + return out + +def contacts_loop(): + droid.fullShow(contacts_layout) + droid.fullSetProperty("contactTextView","text", contact_addr) + out = None + while out is None: + event = droid.eventWait().result + print "got event", event + if event["name"] == "click": + + if event["data"]["text"] == "OK": + out = 'main' + + elif event["name"]=="key": + if event["data"]["key"] == '4': + out = 'main' return out + def server_dialog(plist): droid.dialogCreateAlert("servers") droid.dialogSetItems( plist.keys() ) @@ -593,6 +653,7 @@ def server_dialog(plist): response = plist.keys()[i] return response + def protocol_dialog(plist): options=["TCP","HTTP","native"] droid.dialogCreateAlert("Protocol") @@ -661,10 +722,12 @@ while True: s = main_loop() elif s == 'payto': s = payto_loop() + elif s == 'receive': + s = receive_loop() + elif s == 'contacts': + s = contacts_loop() elif s == 'settings': s = settings_loop() - elif s == 'history': - s = history_loop() elif s == 'contacts': s = contacts_loop() else: From 95f6818ab165033afda2bb1ff556723d4dbc021d Mon Sep 17 00:00:00 2001 From: ThomasV Date: Wed, 4 Apr 2012 22:35:35 +0200 Subject: [PATCH 05/45] layouts, fixes --- client/electrum4a.py | 54 +++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index c980615e..2ac337e6 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -144,16 +144,20 @@ def main_layout(): """%(title, get_history_layout(15)) -payto_layout=""" - - +def make_layout(s): + return """ + %s + %s + """%(title,s) + +payto_layout = make_layout(""" android:layout_height="wrap_content" android:text="Send"> - - -"""%title + """) - -def make_layout(s): - return """ - - %s - %s - """%(title,s) - receive_layout = make_layout(""" + + + """) settings_layout = make_layout(""" @@ -683,10 +680,11 @@ def settings_loop(): plist[host] = z host = server_dialog(plist) - p = plist[host] - port = p['t'] - srv = host + ':' + port + ':t' - droid.fullSetProperty("server","text",srv) + if host: + p = plist[host] + port = p['t'] + srv = host + ':' + port + ':t' + droid.fullSetProperty("server","text",srv) elif id=="buttonSave": droid.fullQuery() From 4eb21961bcffef1ad4b227c6c55b519c038e7ab3 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Wed, 4 Apr 2012 23:16:51 +0200 Subject: [PATCH 06/45] do not vibrate on init --- client/electrum4a.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index 2ac337e6..846c99f8 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -361,6 +361,7 @@ def set_history_layout(n): i += 1 +first_time_update = True def update_layout(): @@ -373,14 +374,18 @@ def update_layout(): else: c, u = wallet.get_balance() text = "Balance:"+format_satoshis(c) - if u : text += '['+ format_satoshis(u,True)+']' + if u : text += ' [' + format_satoshis(u,True).strip() + ']' droid.fullSetProperty("balanceTextView", "text", text) if wallet.was_updated and wallet.up_to_date: + global first_time_update + if not first_time_update: + droid.vibrate() + else: + first_time_update = False wallet.was_updated = False set_history_layout(15) - droid.vibrate() From aa3ff22c1966fd4b60ae180b71c8e236f8c3422e Mon Sep 17 00:00:00 2001 From: ThomasV Date: Wed, 4 Apr 2012 23:30:08 +0200 Subject: [PATCH 07/45] strip --- client/gui.py | 2 +- client/gui_qt.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/gui.py b/client/gui.py index de919f5b..77acdd1c 100644 --- 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 index e249ec74..66f61cef 100644 --- a/client/gui_qt.py +++ b/client/gui_qt.py @@ -203,7 +203,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" From a4e2dc8ea61ac24c1334e9ce66d29897c7ab7814 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Thu, 5 Apr 2012 14:04:30 +0200 Subject: [PATCH 08/45] with qr codes in webviews --- client/electrum4a.py | 382 +++++++++++++++++++++++-------------------- 1 file changed, 208 insertions(+), 174 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index 846c99f8..022bfe2f 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -28,10 +28,6 @@ from decimal import Decimal import datetime -droid = android.Android() -wallet = Wallet() -wallet.set_path("/sdcard/electrum.dat") -wallet.read() @@ -39,11 +35,17 @@ wallet.read() def select_from_contacts(): title = 'Contacts:' droid.dialogCreateAlert(title) + droid.dialogSetPositiveButtonText('New contact') droid.dialogSetItems(wallet.addressbook) droid.dialogShow() - response = droid.dialogGetResponse() - result = response.result.get('item') + response = droid.dialogGetResponse().result droid.dialogDismiss() + + if response.get('which') == 'positive': + return 'newcontact' + + result = response.get('item') + print result if result is not None: addr = wallet.addressbook[result] return addr @@ -67,6 +69,38 @@ def select_from_addresses(): +def qr_code_layout(addr): + return """ + + QR code + + + + + + + + +
+
+ + +
+ +
+ +"""%addr + + title = """ """ -def main_layout(): +def make_layout(s): return """ - - - + %s + %s + """%(title,s) + + +def main_layout(): + return """ + + + + + %s @@ -116,46 +168,12 @@ def main_layout(): %s - - - - - - - - + """%(title, get_history_layout(15)) -def make_layout(s): - return """ - - %s - %s - """%(title,s) - payto_layout = make_layout(""" + android:layout_height="wrap_content" android:text="From QR code"> + android:layout_height="wrap_content" android:text="From Contacts"> @@ -215,38 +233,27 @@ payto_layout = make_layout(""" android:layout_height="wrap_content" android:id="@+id/linearLayout1"> - """) -receive_layout = make_layout(""" - - """) -contacts_layout = make_layout(""" - - - """) + android:tag="Tag Me" + android:inputType="numberDecimal"> + -settings_layout = make_layout(""" + android:tag="Tag Me" + android:inputType="textCapWords|textPhonetic|number"> - - - - """) + android:layout_height="wrap_content" + android:id="@+id/linearLayout1"> + + + + + + + + + +""") @@ -361,7 +382,7 @@ def set_history_layout(n): i += 1 -first_time_update = True + def update_layout(): @@ -420,8 +441,7 @@ def pay_to(recipient, amount, fee, label): - -if not wallet.file_exists: +def recover(): droid.dialogCreateAlert("wallet file not found") droid.dialogSetPositiveButtonText('OK') droid.dialogShow() @@ -463,22 +483,35 @@ if not wallet.file_exists: else: droid.dialogCreateSpinnerProgress("wallet not found") droid.dialogShow() - exit(1) - -else: - WalletSynchronizer(wallet,True).start() -def add_menu(): - droid.addOptionsMenuItem("Network","settings",None,"") - droid.addOptionsMenuItem("New contact","newcontact",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): + droid.dialogCreateAlert('Add to contacts?', address) + droid.dialogSetPositiveButtonText('OK') + droid.dialogSetNegativeButtonText('Cancel') + droid.dialogShow() + response = droid.dialogGetResponse().result + droid.dialogDismiss() + print response + if response.get('which') == 'positive': + wallet.addressbook.append(address) + wallet.save() + else: + droid.dialogCreateAlert('Invalid address', address) + droid.dialogSetPositiveButtonText('OK') + droid.dialogShow() + response = droid.dialogGetResponse().result + droid.dialogDismiss() def main_loop(): - droid.fullShow(main_layout()) update_layout() out = None while out is None: @@ -493,59 +526,35 @@ def main_loop(): if event["name"]=="click": id=event["data"]["id"] - if id=="buttonSend": - out = 'payto' - - if id=="buttonContacts": - global contact_addr - contact_addr = select_from_contacts() - if contact_addr: - out = 'contacts' - - elif id=="buttonReceive": - global receive_addr - receive_addr = select_from_addresses() - if receive_addr: - out = 'receive' - elif event["name"]=="settings": out = 'settings' - elif event["name"]=="newcontact": - code = droid.scanBarcode() - r = code.result - if r: - address = r['extras']['SCAN_RESULT'] - if address: - if wallet.is_valid(address): - droid.dialogCreateAlert('Add to contacts?', address) - droid.dialogSetPositiveButtonText('OK') - droid.dialogSetNegativeButtonText('Cancel') - droid.dialogShow() - response = droid.dialogGetResponse().result - droid.dialogDismiss() - print response - if response.get('which') == 'positive': - wallet.addressbook.append(address) - wallet.save() - else: - droid.dialogCreateAlert('Invalid address', address) - droid.dialogSetPositiveButtonText('OK') - droid.dialogShow() - response = droid.dialogGetResponse().result - droid.dialogDismiss() - elif event["name"]=="key": if event["data"]["key"] == '4': out = 'quit' - 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 + return out def payto_loop(): - droid.fullShow(payto_layout) out = None while out is None: event = droid.eventWait().result @@ -584,14 +593,8 @@ def payto_loop(): if addr: droid.fullSetProperty("recipient","text",addr) - elif id=="buttonCancelSend": - out = 'main' - - elif event["name"]=="settings": - out = 'settings' - - elif event["name"]=="quit": - out = 'quit' + elif event["name"] in menu_commands: + out = event["name"] elif event["name"]=="key": if event["data"]["key"] == '4': @@ -609,39 +612,19 @@ contact_addr = '' def receive_loop(): - droid.fullShow(receive_layout) - droid.fullSetProperty("receiveTextView","text", receive_addr) out = None while out is None: event = droid.eventWait().result print "got event", event - if event["name"] == "click": - - if event["data"]["text"] == "OK": - out = 'main' - - elif event["name"]=="key": - if event["data"]["key"] == '4': - out = 'main' - + out = 'main' return out def contacts_loop(): - droid.fullShow(contacts_layout) - droid.fullSetProperty("contactTextView","text", contact_addr) out = None while out is None: event = droid.eventWait().result print "got event", event - if event["name"] == "click": - - if event["data"]["text"] == "OK": - out = 'main' - - elif event["name"]=="key": - if event["data"]["key"] == '4': - out = 'main' - + out = 'main' return out @@ -664,12 +647,13 @@ def protocol_dialog(plist): def settings_loop(): - droid.fullShow(settings_layout) droid.fullSetProperty("server","text",wallet.server) out = None while out is None: event = droid.eventWait().result + print "got event", event + if event["name"] == "click": id = event["data"]["id"] @@ -711,30 +695,80 @@ def settings_loop(): if event["data"]["key"] == '4': out = 'main' - elif event["name"]=="quit": - out = 'quit' + elif event["name"] in menu_commands: + out = event["name"] return out +menu_commands = ["send", "receive", "settings", "contacts", "main"] + + +first_time_update = True +droid = android.Android() +wallet = Wallet() + +wallet.set_path("/sdcard/electrum.dat") +wallet.read() +if not wallet.file_exists: + recover() + exit(1) +else: + WalletSynchronizer(wallet,True).start() + s = 'main' -while True: + +def add_menu(s): + droid.clearOptionsMenu() if s == 'main': - s = main_loop() - elif s == 'payto': - s = payto_loop() - elif s == 'receive': - s = receive_loop() + droid.addOptionsMenuItem("Send","send",None,"") + droid.addOptionsMenuItem("Receive","receive",None,"") + droid.addOptionsMenuItem("Contacts","contacts",None,"") + droid.addOptionsMenuItem("Settings","settings",None,"") elif s == 'contacts': - s = contacts_loop() + droid.addOptionsMenuItem("Pay to","paytocontact",None,"") + droid.addOptionsMenuItem("Edit label","editcontact",None,"") + droid.addOptionsMenuItem("Delete","removecontact",None,"") elif s == 'settings': - s = settings_loop() + droid.addOptionsMenuItem("Save","save",None,"") + droid.addOptionsMenuItem("Cancel","cancel",None,"") + + + #droid.addOptionsMenuItem("Quit","quit",None,"") + + +while True: + add_menu(s) + if s == 'main': + droid.fullShow(main_layout()) + s = main_loop() + droid.fullDismiss() + elif s == 'send': + droid.fullShow(payto_layout) + s = payto_loop() + droid.fullDismiss() + + elif s == 'receive': + f = open('/sdcard/sl4a/scripts/recv.html',"w") + f.write(qr_code_layout(receive_addr)) + f.close() + droid.webViewShow("file:///sdcard/sl4a/scripts/recv.html?url=%s"%receive_addr,True) + s = receive_loop() + elif s == 'contacts': + f = open('/sdcard/sl4a/scripts/recv.html',"w") + f.write(qr_code_layout(contact_addr)) + f.close() + droid.webViewShow("file:///sdcard/sl4a/scripts/recv.html?url=%s"%contact_addr,True) s = contacts_loop() + + elif s == 'settings': + droid.fullShow(settings_layout) + s = settings_loop() + droid.fullDismiss() else: break -droid.fullDismiss() droid.makeToast("Bye!") From d46b37b3ea456acea6862ce7f4ba695c40d3bd2d Mon Sep 17 00:00:00 2001 From: ThomasV Date: Thu, 5 Apr 2012 14:11:26 +0200 Subject: [PATCH 09/45] fix url --- client/electrum4a.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index 022bfe2f..ab5baa40 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -73,10 +73,8 @@ def qr_code_layout(addr): return """ QR code - -
- +
"""%addr title = """ +""" + +def make_layout(s, scrollable): + content = """ -""" -def make_layout(s): + %s """%s + + if scrollable: + content = """ + + + + + %s + + + + """%content + + return """ - %s - %s - """%(title,s) + + %s + """%content + + def main_layout(): - return """ - - - - - - - %s - + return make_layout(""" - %s - - - - -"""%(title, get_history_layout(15)) + %s """%get_history_layout(15),True) payto_layout = make_layout(""" + + android:tag="Tag Me" android:inputType="text"> + android:tag="Tag Me" android:inputType="text"> - """) + """,False) @@ -264,7 +300,7 @@ settings_layout = make_layout(""" android:layout_width="match_parent" android:layout_height="wrap_content" android:tag="Tag Me" - android:inputType="textCapWords|textPhonetic|number"> + android:inputType="text"> -""") +""",False) @@ -419,17 +455,19 @@ def pay_to(recipient, amount, fee, label): droid.dialogCreateSpinnerProgress("Electrum", "signing transaction...") droid.dialogShow() - tx = wallet.mktx( recipient, amount, label, password, fee) + + try: + tx = wallet.mktx( recipient, amount, label, password, fee) + except: + droid.dialogDismiss() + return 'error' + 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 - droid.dialogDismiss() + modal_dialog('tx sent', h) return h else: return 'error' @@ -440,11 +478,8 @@ def pay_to(recipient, amount, fee, label): def recover(): - droid.dialogCreateAlert("wallet file not found") - droid.dialogSetPositiveButtonText('OK') - droid.dialogShow() - resp = droid.dialogGetResponse().result - print resp + if not modal_question("Wallet not found","restore from seed?"): + exit(1) code = droid.scanBarcode() r = code.result @@ -453,13 +488,8 @@ def recover(): else: exit(1) - droid.dialogCreateAlert('seed', seed) - droid.dialogSetPositiveButtonText('OK') - droid.dialogSetNegativeButtonText('Cancel') - droid.dialogShow() - response = droid.dialogGetResponse().result - droid.dialogDismiss() - print response + if not modal_question('Seed', seed): + exit(1) wallet.seed = str(seed) wallet.init_mpk( wallet.seed ) @@ -475,12 +505,10 @@ def recover(): # history and addressbook wallet.update_tx_history() wallet.fill_addressbook() - droid.dialogCreateAlert("recovery successful") - droid.dialogShow() wallet.save() + modal_dialog("recovery successful") else: - droid.dialogCreateSpinnerProgress("wallet not found") - droid.dialogShow() + modal_dialog("no transactions found for this seed") @@ -491,22 +519,11 @@ def make_new_contact(): address = r['extras']['SCAN_RESULT'] if address: if wallet.is_valid(address): - droid.dialogCreateAlert('Add to contacts?', address) - droid.dialogSetPositiveButtonText('OK') - droid.dialogSetNegativeButtonText('Cancel') - droid.dialogShow() - response = droid.dialogGetResponse().result - droid.dialogDismiss() - print response - if response.get('which') == 'positive': + if modal_question('Add to contacts?', address): wallet.addressbook.append(address) wallet.save() else: - droid.dialogCreateAlert('Invalid address', address) - droid.dialogSetPositiveButtonText('OK') - droid.dialogShow() - response = droid.dialogGetResponse().result - droid.dialogDismiss() + modal_dialog('Invalid address', address) def main_loop(): @@ -552,6 +569,7 @@ def main_loop(): return out + def payto_loop(): out = None while out is None: @@ -568,15 +586,15 @@ def payto_loop(): label = droid.fullQueryDetail("label").result.get('text') amount = droid.fullQueryDetail('amount').result.get('text') fee = '0.001' - amount = int( 100000000 * Decimal(amount) ) + try: + amount = int( 100000000 * Decimal(amount) ) + except: + modal_dialog('Error','invalid amount') + continue + 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() + modal_dialog('result',result) out = 'main' elif id=="buttonContacts": @@ -637,35 +655,31 @@ def server_dialog(plist): return response -def protocol_dialog(plist): - options=["TCP","HTTP","native"] - droid.dialogCreateAlert("Protocol") - droid.dialogSetSingleChoiceItems(options) - def settings_loop(): droid.fullSetProperty("server","text",wallet.server) + droid.fullSetProperty("fee","text", "%s"% str( Decimal( wallet.fee)/100000000 ) ) out = None while out is None: event = droid.eventWait().result print "got event", event - if event["name"] == "click": + 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"] == "click": id = event["data"]["id"] 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 - host = server_dialog(plist) if host: p = plist[host] @@ -673,18 +687,36 @@ def settings_loop(): srv = host + ':' + port + ':t' droid.fullSetProperty("server","text",srv) - elif id=="buttonSave": + elif id=="buttonProtocol": droid.fullQuery() srv = droid.fullQueryDetail("server").result.get('text') + host = srv.split(':')[0] + if host in plist: + server = protocol_dialog(host, plist[host]) + print server + droid.fullSetProperty("server","text",server) + + elif id=="buttonSave": + + droid.fullQuery() + srv = droid.fullQueryDetail("server").result.get('text') + fee = droid.fullQueryDetail("fee").result.get('text') try: wallet.set_server(srv) - out = 'main' except: - droid.dialogCreateAlert('error') - droid.dialogSetPositiveButtonText('OK') - droid.dialogShow() - droid.dialogGetResponse() - droid.dialogDismiss() + modal_dialog('error','invalid server') + + try: + fee = int( 100000000 * Decimal(fee) ) + if wallet.fee != fee: + wallet.fee = fee + wallet.save() + except: + modal_dialog('error','invalid fee value') + + out = 'main' + + elif id=="buttonCancel": out = 'main' @@ -725,17 +757,18 @@ def add_menu(s): droid.addOptionsMenuItem("Receive","receive",None,"") droid.addOptionsMenuItem("Contacts","contacts",None,"") droid.addOptionsMenuItem("Settings","settings",None,"") + elif s == 'receive': + droid.addOptionsMenuItem("Back","main",None,"") elif s == 'contacts': - droid.addOptionsMenuItem("Pay to","paytocontact",None,"") - droid.addOptionsMenuItem("Edit label","editcontact",None,"") - droid.addOptionsMenuItem("Delete","removecontact",None,"") + droid.addOptionsMenuItem("Back","main",None,"") + #droid.addOptionsMenuItem("Pay to","paytocontact",None,"") + #droid.addOptionsMenuItem("Edit label","editcontact",None,"") + #droid.addOptionsMenuItem("Delete","removecontact",None,"") elif s == 'settings': droid.addOptionsMenuItem("Save","save",None,"") droid.addOptionsMenuItem("Cancel","cancel",None,"") - #droid.addOptionsMenuItem("Quit","quit",None,"") - while True: add_menu(s) @@ -743,6 +776,7 @@ while True: droid.fullShow(main_layout()) s = main_loop() droid.fullDismiss() + elif s == 'send': droid.fullShow(payto_layout) s = payto_loop() @@ -752,7 +786,7 @@ while True: f = open('/sdcard/sl4a/scripts/recv.html',"w") f.write(qr_code_layout(receive_addr)) f.close() - droid.webViewShow("file:///sdcard/sl4a/scripts/recv.html") + wvs = droid.webViewShow("file:///sdcard/sl4a/scripts/recv.html") s = receive_loop() elif s == 'contacts': From e606ed0aa3756cf93f8b58e840048a6b6363802d Mon Sep 17 00:00:00 2001 From: ThomasV Date: Thu, 5 Apr 2012 18:44:09 +0200 Subject: [PATCH 11/45] fix labels --- client/electrum4a.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index 6ed1f3b4..87bc3cb9 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -347,8 +347,8 @@ def get_history_values(n): time_str = 'pending' conf = 'o' - label = line.get('label') - #if not label: label = line['tx_hash'] + 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 )) From c6c04f80fe0a35a1b75b729ac140eea69d5588a3 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Thu, 5 Apr 2012 18:49:30 +0200 Subject: [PATCH 12/45] fix: do not change the server if the user cancelled the action --- client/electrum4a.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index 87bc3cb9..9c756b61 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -693,8 +693,8 @@ def settings_loop(): host = srv.split(':')[0] if host in plist: server = protocol_dialog(host, plist[host]) - print server - droid.fullSetProperty("server","text",server) + if server: + droid.fullSetProperty("server","text",server) elif id=="buttonSave": From 9156375d1055b0caff3a8b54bc81295610fcb0e0 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Thu, 5 Apr 2012 21:28:38 +0200 Subject: [PATCH 13/45] passwords --- client/electrum4a.py | 107 ++++++++++++++++++++++++++++++------------- 1 file changed, 75 insertions(+), 32 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index 9c756b61..46f803c4 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -23,13 +23,13 @@ from interface import WalletSynchronizer from wallet import Wallet from wallet import format_satoshis from decimal import Decimal - +import mnemonic import datetime -def modal_dialog(title, msg): +def modal_dialog(title, msg = ''): droid.dialogCreateAlert(title,msg) droid.dialogSetPositiveButtonText('OK') droid.dialogShow() @@ -273,21 +273,6 @@ payto_layout = make_layout(""" settings_layout = make_layout(""" - - - - - - + + + + + @@ -488,11 +487,14 @@ def recover(): else: exit(1) - if not modal_question('Seed', seed): + if not modal_question('Seed', seed ): exit(1) wallet.seed = str(seed) wallet.init_mpk( wallet.seed ) + + change_password_dialog() + droid.dialogCreateSpinnerProgress("Electrum", "recovering wallet...") droid.dialogShow() WalletSynchronizer(wallet,True).start() @@ -505,10 +507,11 @@ def recover(): # history and addressbook wallet.update_tx_history() wallet.fill_addressbook() - wallet.save() modal_dialog("recovery successful") else: - modal_dialog("no transactions found for this seed") + if not modal_question("no transactions found for this seed","do you want to keep this wallet?"): + exit(1) + wallet.save() @@ -655,6 +658,43 @@ def server_dialog(plist): return response +def seed_dialog(): + if wallet.use_encryption: + password = droid.dialogGetPassword('Password').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('Current password').result + if not password: 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 + password2 = droid.dialogGetPassword('Confirm new password').result + if new_password != password2: + modal_dialog('error','passwords do not match') + return + + if new_password: + wallet.update_password(seed, new_password) def settings_loop(): @@ -696,8 +736,8 @@ def settings_loop(): if server: droid.fullSetProperty("server","text",server) - elif id=="buttonSave": + elif id=="buttonSave": droid.fullQuery() srv = droid.fullQueryDetail("server").result.get('text') fee = droid.fullQueryDetail("fee").result.get('text') @@ -714,20 +754,22 @@ def settings_loop(): except: modal_dialog('error','invalid fee value') - out = 'main' + elif event["name"] in menu_commands: + out = event["name"] - - - elif id=="buttonCancel": - out = 'main' + elif event["name"] == 'password': + change_password_dialog() + + elif event["name"] == 'seed': + seed_dialog() + + elif event["name"] == 'cancel': + out = 'main' elif event["name"] == "key": if event["data"]["key"] == '4': out = 'main' - elif event["name"] in menu_commands: - out = event["name"] - return out @@ -743,7 +785,6 @@ wallet.set_path("/sdcard/electrum.dat") wallet.read() if not wallet.file_exists: recover() - exit(1) else: WalletSynchronizer(wallet,True).start() @@ -765,11 +806,13 @@ def add_menu(s): #droid.addOptionsMenuItem("Edit label","editcontact",None,"") #droid.addOptionsMenuItem("Delete","removecontact",None,"") elif s == 'settings': - droid.addOptionsMenuItem("Save","save",None,"") - droid.addOptionsMenuItem("Cancel","cancel",None,"") + droid.addOptionsMenuItem("Password","password",None,"") + droid.addOptionsMenuItem("Seed","seed",None,"") + + while True: add_menu(s) if s == 'main': From 0cb7957ffb2c2d1f585858c27a6c070ab6283e5c Mon Sep 17 00:00:00 2001 From: ThomasV Date: Thu, 5 Apr 2012 21:38:25 +0200 Subject: [PATCH 14/45] password --- client/electrum4a.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index 46f803c4..440e3617 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -693,9 +693,12 @@ def change_password_dialog(): modal_dialog('error','passwords do not match') return + wallet.update_password(seed, new_password) if new_password: - wallet.update_password(seed, new_password) - + modal_dialog('Password updated','your wallet is encrypted') + else: + modal_dialog('Password removed','your wallet is not encrypted') + def settings_loop(): droid.fullSetProperty("server","text",wallet.server) From e6db36e9b418095ff9d301e9132f2ba4e10df52a Mon Sep 17 00:00:00 2001 From: ThomasV Date: Thu, 5 Apr 2012 22:32:48 +0200 Subject: [PATCH 15/45] fix: return on cancel --- client/electrum4a.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index 440e3617..130c9b66 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -448,7 +448,7 @@ 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 From df9699f79db8ae297e7ffd272f85d3014af994a6 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Thu, 5 Apr 2012 22:40:14 +0200 Subject: [PATCH 16/45] better exception handling --- client/electrum4a.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index 130c9b66..bc96173c 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -457,20 +457,19 @@ def pay_to(recipient, amount, fee, label): try: tx = wallet.mktx( recipient, amount, label, password, fee) - except: + except BaseException, e: + modal_dialog('error', e.message) droid.dialogDismiss() - return 'error' + return - print tx droid.dialogDismiss() - if tx: - r, h = wallet.sendtx( tx ) - modal_dialog('tx sent', h) - return h + r, h = wallet.sendtx( tx ) + if r: + modal_dialog('Payment sent', h) + return True else: - return 'error' - + modal_dialog('Error', h) @@ -597,8 +596,8 @@ def payto_loop(): fee = int( 100000000 * Decimal(fee) ) result = pay_to(recipient, amount, fee, label) - modal_dialog('result',result) - out = 'main' + if result: + out = 'main' elif id=="buttonContacts": addr = select_from_contacts() From 29700a60948b06cc00534902780b4c9df1d4b8a2 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Fri, 6 Apr 2012 13:55:33 +0200 Subject: [PATCH 17/45] do not use webview for qr codes --- client/electrum4a.py | 129 ++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 58 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index bc96173c..cedcbb13 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -18,6 +18,7 @@ + import android from interface import WalletSynchronizer from wallet import Wallet @@ -102,50 +103,15 @@ def protocol_dialog(host, z): return host + ':' + port + ':' + p -def qr_code_layout(addr): - return """ - - QR code - - - - - -
-
- - -
- -
- -"""%addr - - -title = """ -""" - -def make_layout(s, scrollable): +def make_layout(s, scrollable = False): content = """ - - + %s """%s @@ -208,6 +174,25 @@ def main_layout(): +def qr_layout(addr): + return make_layout(""" + + + + + """%addr) + payto_layout = make_layout(""" """% rows return output + def set_history_layout(n): values = get_history_values(n) i = 0 @@ -412,7 +398,6 @@ def set_history_layout(n): 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 @@ -634,7 +619,10 @@ def receive_loop(): while out is None: event = droid.eventWait().result print "got event", event - out = 'main' + if event["name"]=="key": + if event["data"]["key"] == '4': + out = 'main' + return out def contacts_loop(): @@ -642,7 +630,10 @@ def contacts_loop(): while out is None: event = droid.eventWait().result print "got event", event - out = 'main' + if event["name"]=="key": + if event["data"]["key"] == '4': + out = 'main' + return out @@ -811,40 +802,62 @@ def add_menu(s): droid.addOptionsMenuItem("Password","password",None,"") droid.addOptionsMenuItem("Seed","seed",None,"") +def make_bitmap(addr): + # fixme: this is highly innefficient + 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( 350, 350 ) + print len(bitmap.bitarray) + bitmap.bitarray = [] + assert k == 33 + + for r in range(35): + tmparray = [ 0 ] * 350 + + if 0 < r < 34: + for c in range(k): + if qr.isDark(r-1, c): + tmparray[ (1+c)*10:(1+c)*10 + 10] = [1]*10 + + for i in range(10): + 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() - droid.fullDismiss() + #droid.fullDismiss() elif s == 'send': droid.fullShow(payto_layout) s = payto_loop() - droid.fullDismiss() + #droid.fullDismiss() elif s == 'receive': - f = open('/sdcard/sl4a/scripts/recv.html',"w") - f.write(qr_code_layout(receive_addr)) - f.close() - wvs = droid.webViewShow("file:///sdcard/sl4a/scripts/recv.html") + make_bitmap(receive_addr) + droid.fullShow(qr_layout(receive_addr)) s = receive_loop() elif s == 'contacts': - f = open('/sdcard/sl4a/scripts/recv.html',"w") - f.write(qr_code_layout(contact_addr)) - f.close() - droid.webViewShow("file:///sdcard/sl4a/scripts/recv.html") + 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() + #droid.fullDismiss() else: break From 79e3d2039dd90644bf7b314ffc52748339f299d1 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Fri, 6 Apr 2012 14:53:00 +0200 Subject: [PATCH 18/45] request 2 taps in order to exit --- client/electrum4a.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index cedcbb13..f670ccb7 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -516,6 +516,7 @@ def make_new_contact(): def main_loop(): update_layout() out = None + quitting = False while out is None: event = droid.eventWait(1000).result # wait for 1 second @@ -525,16 +526,21 @@ def main_loop(): print "got event in main loop", event + # 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 + if event["name"]=="click": id=event["data"]["id"] elif event["name"]=="settings": out = 'settings' - elif event["name"]=="key": - if event["data"]["key"] == '4': - out = 'quit' - elif event["name"] in menu_commands: out = event["name"] From fab367cf41f0f03f81c6ca70ce4acd73b1d81dfc Mon Sep 17 00:00:00 2001 From: ThomasV Date: Fri, 6 Apr 2012 14:54:02 +0200 Subject: [PATCH 19/45] bitmaps --- client/bmp.py | 206 +++++++++++++++++++++++++++++++++++ client/electrum_text_320.png | Bin 0 -> 4882 bytes 2 files changed, 206 insertions(+) create mode 100644 client/bmp.py create mode 100644 client/electrum_text_320.png diff --git a/client/bmp.py b/client/bmp.py new file mode 100644 index 00000000..bfdd165b --- /dev/null +++ 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/electrum_text_320.png b/client/electrum_text_320.png new file mode 100644 index 0000000000000000000000000000000000000000..dc6f086440b839c2323ba8692ce8cb13e1f7e310 GIT binary patch literal 4882 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 A@&Et; literal 0 HcmV?d00001 From 3628fee5828f69a6e23c30996e80043ac13763f0 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Fri, 6 Apr 2012 15:03:09 +0200 Subject: [PATCH 20/45] android --- client/ANDROID | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 client/ANDROID diff --git a/client/ANDROID b/client/ANDROID new file mode 100644 index 00000000..e54c4bca --- /dev/null +++ b/client/ANDROID @@ -0,0 +1,30 @@ +INSTALLATION INSTRUCTIONS FOR ANDROID + + +1. Install sl4a and py4a : you need at least revision 5 + +http://code.google.com/p/android-scripting/downloads/detail?name=sl4a_r5.apk +http://code.google.com/p/python-for-android/downloads/detail?name=PythonForAndroid_r5.apk + +To install these APKs, just visit the above links with your phone and click on the qr code + + +2. copy the following files in /sdcard/sl4a/scripts: + +aes (directory) +ecdsa (directory) +bmp.py +electrum4a.py +interface.py +mnemonic.py +msqr.py +pyqrnative.py +ripemd.py +version.py +wallet.py + +3. copy the logo in /sdcard/sl4a: + +electrum_text_320.png + +4. to run the application, open sl4a and click on electrum4a.py From 515ca2ea582d70a0879f9c8e7c0fb7b3cdeda224 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Fri, 6 Apr 2012 17:33:20 +0200 Subject: [PATCH 21/45] more help --- client/ANDROID | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/client/ANDROID b/client/ANDROID index e54c4bca..d39cfd8c 100644 --- a/client/ANDROID +++ b/client/ANDROID @@ -3,16 +3,15 @@ INSTALLATION INSTRUCTIONS FOR ANDROID 1. Install sl4a and py4a : you need at least revision 5 +To install these APKs, just visit the links below with your phone and click on the qr code + http://code.google.com/p/android-scripting/downloads/detail?name=sl4a_r5.apk http://code.google.com/p/python-for-android/downloads/detail?name=PythonForAndroid_r5.apk -To install these APKs, just visit the above links with your phone and click on the qr code 2. copy the following files in /sdcard/sl4a/scripts: -aes (directory) -ecdsa (directory) bmp.py electrum4a.py interface.py @@ -22,6 +21,12 @@ 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 Electrum tar.gz 3. copy the logo in /sdcard/sl4a: From 5744131e186c8b3b580e424f5f71ede45e028dd4 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 7 Apr 2012 15:13:18 +0200 Subject: [PATCH 22/45] callbacks --- client/electrum4a.py | 72 +++++++++++++++++++++-------------- client/electrum_text_320.png | Bin 4882 -> 4634 bytes client/gui_qt.py | 14 +++++-- client/interface.py | 37 ++++++++++-------- client/wallet.py | 3 +- 5 files changed, 77 insertions(+), 49 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index f670ccb7..6ec8fe98 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -152,19 +152,18 @@ def main_layout(): return make_layout(""" + android:textSize="6pt" + android:gravity="center_vertical|center_horizontal|left"> - @@ -346,7 +345,7 @@ def get_history_layout(n): values = get_history_values(n) for v in values: a,b,c,d = v - color = "0xff00ff00" if a == 'v' else "0xffff0000" + color = "#ff00ff00" if a == 'v' else "#ffff0000" rows += """ fj~O^kg@wp<}hwP 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 literal 4882 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 A@&Et; diff --git a/client/gui_qt.py b/client/gui_qt.py index 66f61cef..9c2dc752 100644 --- a/client/gui_qt.py +++ b/client/gui_qt.py @@ -144,6 +144,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 +166,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 +191,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: @@ -215,8 +220,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() @@ -1059,4 +1063,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 index 56ad9e61..bdba222d 100644 --- 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) + 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 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 index d0418ebe..906b5367 100644 --- 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 From c55c665d628dd7d8cbcbc2971f5df9ae990a0f1f Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 7 Apr 2012 15:51:37 +0200 Subject: [PATCH 23/45] r5x09 --- client/ANDROID | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ANDROID b/client/ANDROID index d39cfd8c..9796a69f 100644 --- a/client/ANDROID +++ b/client/ANDROID @@ -3,10 +3,10 @@ INSTALLATION INSTRUCTIONS FOR ANDROID 1. Install sl4a and py4a : you need at least revision 5 -To install these APKs, just visit the links below with your phone and click on the qr code +To install these APKs, just visit the links below with your phone and click on the apk link, or scan the qr code -http://code.google.com/p/android-scripting/downloads/detail?name=sl4a_r5.apk -http://code.google.com/p/python-for-android/downloads/detail?name=PythonForAndroid_r5.apk +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 From a63c989bd4b61f090db6b913f1a742ae7c86ea25 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 7 Apr 2012 19:34:33 +0200 Subject: [PATCH 24/45] soorter lines --- client/ANDROID | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/client/ANDROID b/client/ANDROID index 9796a69f..f64f5779 100644 --- a/client/ANDROID +++ b/client/ANDROID @@ -3,7 +3,8 @@ INSTALLATION INSTRUCTIONS FOR ANDROID 1. Install sl4a and py4a : you need at least revision 5 -To install these APKs, just visit the links below with your phone and click on the apk link, or scan the qr code +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 @@ -24,9 +25,9 @@ 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 Electrum tar.gz +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. copy the logo in /sdcard/sl4a: From 6d71483d198f4a98cd84c750e2bce6942b02c93e Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 7 Apr 2012 19:43:52 +0200 Subject: [PATCH 25/45] smaller QR code; edit_label button --- client/electrum4a.py | 55 +++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index 6ec8fe98..1aca942d 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -46,6 +46,15 @@ def modal_question(q,msg): droid.dialogDismiss() return response.get('which') == 'positive' +def edit_label(addr): + droid.dialogCreateAlert('edit label') + droid.dialogSetPositiveButtonText('OK') + droid.dialogSetNegativeButtonText('Cancel') + droid.dialogShow() + response = droid.dialogGetResponse().result + droid.dialogDismiss() + + def select_from_contacts(): title = 'Contacts:' droid.dialogCreateAlert(title) @@ -175,21 +184,23 @@ def main_layout(): def qr_layout(addr): return make_layout(""" + + + + - - """%addr) payto_layout = make_layout(""" @@ -645,6 +656,9 @@ def receive_loop(): if event["data"]["key"] == '4': out = 'main' + elif event["name"]=="edit": + edit_label(receive_addr) + return out def contacts_loop(): @@ -656,6 +670,9 @@ def contacts_loop(): if event["data"]["key"] == '4': out = 'main' + elif event["name"]=="edit": + edit_label(contact_addr) + return out @@ -766,6 +783,7 @@ def settings_loop(): if wallet.fee != fee: wallet.fee = fee wallet.save() + out = 'main' except: modal_dialog('error','invalid fee value') @@ -812,18 +830,17 @@ def add_menu(s): droid.addOptionsMenuItem("Contacts","contacts",None,"") droid.addOptionsMenuItem("Settings","settings",None,"") elif s == 'receive': - droid.addOptionsMenuItem("Back","main",None,"") + droid.addOptionsMenuItem("Edit","edit",None,"") elif s == 'contacts': - droid.addOptionsMenuItem("Back","main",None,"") - #droid.addOptionsMenuItem("Pay to","paytocontact",None,"") - #droid.addOptionsMenuItem("Edit label","editcontact",None,"") - #droid.addOptionsMenuItem("Delete","removecontact",None,"") + droid.addOptionsMenuItem("Edit","edit",None,"") + droid.addOptionsMenuItem("Pay to","paytocontact",None,"") + droid.addOptionsMenuItem("Delete","removecontact",None,"") elif s == 'settings': droid.addOptionsMenuItem("Password","password",None,"") droid.addOptionsMenuItem("Seed","seed",None,"") def make_bitmap(addr): - # fixme: this is highly innefficient + # fixme: this is highly inefficient droid.dialogCreateSpinnerProgress("please wait") droid.dialogShow() import pyqrnative, bmp @@ -831,20 +848,20 @@ def make_bitmap(addr): qr.addData(addr) qr.make() k = qr.getModuleCount() - bitmap = bmp.BitMap( 350, 350 ) + bitmap = bmp.BitMap( 35*8, 35*8 ) print len(bitmap.bitarray) bitmap.bitarray = [] assert k == 33 for r in range(35): - tmparray = [ 0 ] * 350 + tmparray = [ 0 ] * 35*8 if 0 < r < 34: for c in range(k): if qr.isDark(r-1, c): - tmparray[ (1+c)*10:(1+c)*10 + 10] = [1]*10 + tmparray[ (1+c)*8:(2+c)*8] = [1]*8 - for i in range(10): + for i in range(8): bitmap.bitarray.append( tmparray[:] ) bitmap.saveFile( "/sdcard/sl4a/qrcode.bmp" ) From 40d46e951fa31861790bca8b55768984322efe3e Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 7 Apr 2012 20:23:08 +0200 Subject: [PATCH 26/45] edit labels --- client/electrum4a.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index 1aca942d..a5a32cd9 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -47,13 +47,17 @@ def modal_question(q,msg): return response.get('which') == 'positive' def edit_label(addr): - droid.dialogCreateAlert('edit label') + droid.dialogCreateInput('Edit label','',wallet.labels.get(addr)) droid.dialogSetPositiveButtonText('OK') droid.dialogSetNegativeButtonText('Cancel') droid.dialogShow() response = droid.dialogGetResponse().result droid.dialogDismiss() - + if response.get('which') == 'positive': + wallet.labels[addr] = response.get('value') + wallet.update_tx_history() + wallet.save() + droid.fullSetProperty("labelTextView", "text", wallet.labels.get(addr)) def select_from_contacts(): title = 'Contacts:' @@ -201,7 +205,15 @@ def qr_layout(addr): android:antialias="false" android:src="file:///sdcard/sl4a/qrcode.bmp" /> - """%addr) + + + + """%(addr,wallet.labels.get(addr,''))) payto_layout = make_layout(""" From 3b06118afee04d2007121562231a2f1353817243 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 7 Apr 2012 20:28:18 +0200 Subject: [PATCH 27/45] message should be None --- client/electrum4a.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index a5a32cd9..73bd2f78 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -47,7 +47,7 @@ def modal_question(q,msg): return response.get('which') == 'positive' def edit_label(addr): - droid.dialogCreateInput('Edit label','',wallet.labels.get(addr)) + droid.dialogCreateInput('Edit label',None,wallet.labels.get(addr)) droid.dialogSetPositiveButtonText('OK') droid.dialogSetNegativeButtonText('Cancel') droid.dialogShow() From 82073afa85f7fb29b7f60e1674af4627cf7bf918 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sun, 8 Apr 2012 22:51:24 +0200 Subject: [PATCH 28/45] make qr code layout scrollable --- client/electrum4a.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index 73bd2f78..3173fcb3 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -213,7 +213,7 @@ def qr_layout(addr): android:gravity="center_vertical|center_horizontal|center"> - """%(addr,wallet.labels.get(addr,''))) + """%(addr,wallet.labels.get(addr,'')), True) payto_layout = make_layout(""" From a2cd477c8e0b276deec151593a894b6fe04c79a9 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Mon, 9 Apr 2012 09:42:53 +0200 Subject: [PATCH 29/45] restore from mnemonic code --- client/electrum4a.py | 47 ++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index 3173fcb3..14078f61 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -37,24 +37,29 @@ def modal_dialog(title, msg = ''): droid.dialogGetResponse() droid.dialogDismiss() -def modal_question(q,msg): - droid.dialogCreateAlert(q, msg) - droid.dialogSetPositiveButtonText('OK') - droid.dialogSetNegativeButtonText('Cancel') - droid.dialogShow() - response = droid.dialogGetResponse().result - droid.dialogDismiss() - return response.get('which') == 'positive' - -def edit_label(addr): - droid.dialogCreateInput('Edit label',None,wallet.labels.get(addr)) +def modal_input(title, msg, value = None): + droid.dialogCreateInput(title, msg) droid.dialogSetPositiveButtonText('OK') droid.dialogSetNegativeButtonText('Cancel') droid.dialogShow() response = droid.dialogGetResponse().result droid.dialogDismiss() if response.get('which') == 'positive': - wallet.labels[addr] = response.get('value') + return response.get('value') + +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: + wallet.labels[addr] = v wallet.update_tx_history() wallet.save() droid.fullSetProperty("labelTextView", "text", wallet.labels.get(addr)) @@ -489,12 +494,20 @@ def recover(): if not modal_question("Wallet not found","restore from seed?"): exit(1) - code = droid.scanBarcode() - r = code.result - if r: - seed = r['extras']['SCAN_RESULT'] + 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: - exit(1) + 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) if not modal_question('Seed', seed ): exit(1) From cb42aedfb38a78ce7fdff4bd38e171798f798bf6 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Mon, 9 Apr 2012 09:46:05 +0200 Subject: [PATCH 30/45] missing value parameter --- client/electrum4a.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index 14078f61..c695317d 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -38,7 +38,7 @@ def modal_dialog(title, msg = ''): droid.dialogDismiss() def modal_input(title, msg, value = None): - droid.dialogCreateInput(title, msg) + droid.dialogCreateInput(title, msg, value) droid.dialogSetPositiveButtonText('OK') droid.dialogSetNegativeButtonText('Cancel') droid.dialogShow() From 51fb9625f8e1160c79c818948b9278e87ae00f1b Mon Sep 17 00:00:00 2001 From: ThomasV Date: Mon, 9 Apr 2012 10:31:43 +0200 Subject: [PATCH 31/45] copy, label and payto buttons --- client/electrum4a.py | 55 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index c695317d..4788933b 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -30,7 +30,7 @@ import datetime -def modal_dialog(title, msg = ''): +def modal_dialog(title, msg = None): droid.dialogCreateAlert(title,msg) droid.dialogSetPositiveButtonText('OK') droid.dialogShow() @@ -58,17 +58,26 @@ def modal_question(q, msg, pos_text = 'OK', neg_text = 'Cancel'): def edit_label(addr): v = modal_input('Edit label',None,wallet.labels.get(addr)) - if v: - wallet.labels[addr] = v + 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", wallet.labels.get(addr)) + 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.dialogSetItems(wallet.addressbook) droid.dialogShow() response = droid.dialogGetResponse().result droid.dialogDismiss() @@ -88,8 +97,8 @@ def select_from_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() @@ -618,6 +627,11 @@ def main_loop(): def payto_loop(): + global recipient + if recipient: + droid.fullSetProperty("recipient","text",recipient) + recipient = None + out = None while out is None: event = droid.eventWait().result @@ -670,7 +684,7 @@ def payto_loop(): receive_addr = '' contact_addr = '' - +recipient = '' def receive_loop(): out = None @@ -681,12 +695,17 @@ def receive_loop(): if event["data"]["key"] == '4': out = 'main' + 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 @@ -695,9 +714,21 @@ def contacts_loop(): if event["data"]["key"] == '4': 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 @@ -855,11 +886,13 @@ def add_menu(s): droid.addOptionsMenuItem("Contacts","contacts",None,"") droid.addOptionsMenuItem("Settings","settings",None,"") elif s == 'receive': - droid.addOptionsMenuItem("Edit","edit",None,"") + droid.addOptionsMenuItem("Copy","clipboard",None,"") + droid.addOptionsMenuItem("Label","edit",None,"") elif s == 'contacts': - droid.addOptionsMenuItem("Edit","edit",None,"") + droid.addOptionsMenuItem("Copy","clipboard",None,"") + droid.addOptionsMenuItem("Label","edit",None,"") droid.addOptionsMenuItem("Pay to","paytocontact",None,"") - droid.addOptionsMenuItem("Delete","removecontact",None,"") + #droid.addOptionsMenuItem("Delete","deletecontact",None,"") elif s == 'settings': droid.addOptionsMenuItem("Password","password",None,"") droid.addOptionsMenuItem("Seed","seed",None,"") From 290a06616973ecce8523cad10b0c839dc4ef91df Mon Sep 17 00:00:00 2001 From: ThomasV Date: Mon, 9 Apr 2012 11:38:21 +0200 Subject: [PATCH 32/45] create wallet in android, enhance password dialogs --- client/electrum4a.py | 105 ++++++++++++++++++++++++++----------------- client/wallet.py | 5 ++- 2 files changed, 66 insertions(+), 44 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index 4788933b..ce9842d0 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -351,7 +351,9 @@ settings_layout = make_layout(""" def get_history_values(n): values = [] h = wallet.get_tx_history() - for i in range(n): + + length = min(n, len(h)) + for i in range(length): line = h[-i-1] v = line['value'] try: @@ -500,48 +502,63 @@ def pay_to(recipient, amount, fee, label): def recover(): - if not modal_question("Wallet not found","restore from seed?"): - exit(1) - 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) - - if not modal_question('Seed', seed ): - exit(1) - - wallet.seed = str(seed) - wallet.init_mpk( wallet.seed ) - - change_password_dialog() - - droid.dialogCreateSpinnerProgress("Electrum", "recovering wallet...") + 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() + if response.get('which') == 'negative': + exit(1) + + 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() - modal_dialog("recovery successful") - else: - if not modal_question("no transactions found for this seed","do you want to keep this wallet?"): - 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) + + change_password_dialog() wallet.save() @@ -761,8 +778,8 @@ def seed_dialog(): def change_password_dialog(): if wallet.use_encryption: - password = droid.dialogGetPassword('Current password').result - if not password: return + password = droid.dialogGetPassword('Your wallet is encrypted').result + if password is None: return else: password = None @@ -773,16 +790,20 @@ def change_password_dialog(): return new_password = droid.dialogGetPassword('Choose a password').result - password2 = droid.dialogGetPassword('Confirm new password').result - if new_password != password2: - modal_dialog('error','passwords do not match') + 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('Password removed','your wallet is not encrypted') + modal_dialog('No password','your wallet is not encrypted') def settings_loop(): diff --git a/client/wallet.py b/client/wallet.py index 906b5367..d30bea71 100644 --- a/client/wallet.py +++ b/client/wallet.py @@ -340,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 ) @@ -850,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] From 9304cfbdd5d44e2648c805cd0ce47e330f2e1372 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Mon, 9 Apr 2012 18:53:56 +0200 Subject: [PATCH 33/45] unneeded --- client/gui_qt.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/gui_qt.py b/client/gui_qt.py index 9c2dc752..8012d86f 100644 --- 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() From 6329120847203984d45527ca486e04e20f5c79ca Mon Sep 17 00:00:00 2001 From: ThomasV Date: Mon, 9 Apr 2012 21:02:59 +0200 Subject: [PATCH 34/45] make showqrcode a static method --- client/gui_qt.py | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/client/gui_qt.py b/client/gui_qt.py index 8012d86f..2620ac0e 100644 --- a/client/gui_qt.py +++ b/client/gui_qt.py @@ -524,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) @@ -667,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 From 8b59bb85d4b0930151c1ce41f9bde545374fbb4d Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 10 Apr 2012 19:05:56 +0200 Subject: [PATCH 35/45] new settings layout using listview. do not use the logo to save space. --- client/electrum4a.py | 174 ++++++++++++++++++------------------------- 1 file changed, 73 insertions(+), 101 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index ce9842d0..df2c91cd 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -109,14 +109,17 @@ def select_from_addresses(): return addr +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, z): droid.dialogCreateAlert('Protocol',host) protocols = z.keys() l = [] for p in protocols: - if p == 't': l.append('TCP/stratum') - if p == 'h': l.append('HTTP/Stratum') - if p == 'n': l.append('TCP/native') + l.append(protocol_name(p)) droid.dialogSetSingleChoiceItems(l) droid.dialogSetPositiveButtonText('OK') droid.dialogSetNegativeButtonText('Cancel') @@ -131,14 +134,26 @@ def protocol_dialog(host, z): + def make_layout(s, scrollable = False): content = """ - + android:layout_height="wrap_content" + android:background="#ff222222"> + + +
%s """%s @@ -292,59 +307,10 @@ payto_layout = make_layout(""" -settings_layout = make_layout(""" - - - - - - - - - - - - - - - - - - - - - - -""",False) +settings_layout = make_layout(""" """) @@ -762,7 +728,7 @@ def server_dialog(plist): def seed_dialog(): if wallet.use_encryption: - password = droid.dialogGetPassword('Password').result + password = droid.dialogGetPassword('Seed').result if not password: return else: password = None @@ -807,13 +773,24 @@ def change_password_dialog(): def settings_loop(): - droid.fullSetProperty("server","text",wallet.server) - droid.fullSetProperty("fee","text", "%s"% str( Decimal( wallet.fee)/100000000 ) ) + + + 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, 'fee: '+fee, 'password: '+is_encrypted, 'seed']) + + 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: @@ -824,55 +801,53 @@ def settings_loop(): z[protocol] = port plist[host] = z + if event["name"] == "itemclick": + pos = event["data"]["position"] - if event["name"] == "click": - id = event["data"]["id"] - - if id=="buttonServer": + if pos == "0": #server host = server_dialog(plist) if host: p = plist[host] port = p['t'] srv = host + ':' + port + ':t' - droid.fullSetProperty("server","text",srv) + try: + wallet.set_server(srv) + except: + modal_dialog('error','invalid server') + set_listview() - elif id=="buttonProtocol": - droid.fullQuery() - srv = droid.fullQueryDetail("server").result.get('text') - host = srv.split(':')[0] + elif pos == "1": #protocol if host in plist: - server = protocol_dialog(host, plist[host]) - if server: - droid.fullSetProperty("server","text",server) + srv = protocol_dialog(host, plist[host]) + if srv: + try: + wallet.set_server(srv) + except: + modal_dialog('error','invalid server') + set_listview() - - elif id=="buttonSave": - droid.fullQuery() - srv = droid.fullQueryDetail("server").result.get('text') - fee = droid.fullQueryDetail("fee").result.get('text') - try: - wallet.set_server(srv) - except: - modal_dialog('error','invalid server') - - try: - fee = int( 100000000 * Decimal(fee) ) + elif pos == "2": #fee + fee = modal_input('fee', 'miners fee', str( Decimal( wallet.fee)/100000000 )) + if fee: + try: + fee = int( 100000000 * Decimal(fee) ) + except: + modal_dialog('error','invalid fee value') if wallet.fee != fee: wallet.fee = fee wallet.save() - out = 'main' - except: - modal_dialog('error','invalid fee value') + set_listview() + + elif pos == "3": + change_password_dialog() + + elif pos == "4": + seed_dialog() + elif event["name"] in menu_commands: out = event["name"] - elif event["name"] == 'password': - change_password_dialog() - - elif event["name"] == 'seed': - seed_dialog() - elif event["name"] == 'cancel': out = 'main' @@ -914,9 +889,6 @@ def add_menu(s): droid.addOptionsMenuItem("Label","edit",None,"") droid.addOptionsMenuItem("Pay to","paytocontact",None,"") #droid.addOptionsMenuItem("Delete","deletecontact",None,"") - elif s == 'settings': - droid.addOptionsMenuItem("Password","password",None,"") - droid.addOptionsMenuItem("Seed","seed",None,"") def make_bitmap(addr): # fixme: this is highly inefficient @@ -971,7 +943,7 @@ while True: s = contacts_loop() elif s == 'settings': - droid.fullShow(settings_layout) + #droid.fullShow(settings_layout) s = settings_loop() #droid.fullDismiss() else: From 0eccf42bf3ec1413b43e454a614e4dc96a24fb74 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 10 Apr 2012 19:08:10 +0200 Subject: [PATCH 36/45] capitals --- client/electrum4a.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index df2c91cd..dbc932ff 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -781,7 +781,7 @@ def settings_loop(): is_encrypted = 'yes' if wallet.use_encryption else 'no' protocol = protocol_name(p) droid.fullShow(settings_layout) - droid.fullSetList("myListView",['server: ' + server, 'protocol: '+ protocol, 'fee: '+fee, 'password: '+is_encrypted, 'seed']) + droid.fullSetList("myListView",['Server: ' + server, 'Protocol: '+ protocol, 'Fee: '+fee, 'Password: '+is_encrypted, 'Seed']) set_listview() From d73e9eb8c4d6c7ca9721ec660e7c70cc91e4cd76 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 10 Apr 2012 19:14:44 +0200 Subject: [PATCH 37/45] textsize --- client/electrum4a.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index dbc932ff..3888a310 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -194,12 +194,11 @@ def main_layout(): return make_layout(""" From f034a5ed4acf296db38e478e05aba1eb41d51088 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 10 Apr 2012 19:30:33 +0200 Subject: [PATCH 38/45] add protocol to settings --- client/electrum4a.py | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index 3888a310..7a698ad8 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -37,8 +37,8 @@ def modal_dialog(title, msg = None): droid.dialogGetResponse() droid.dialogDismiss() -def modal_input(title, msg, value = None): - droid.dialogCreateInput(title, msg, value) +def modal_input(title, msg, value = None, etype=None): + droid.dialogCreateInput(title, msg, value, etype) droid.dialogSetPositiveButtonText('OK') droid.dialogSetNegativeButtonText('Cancel') droid.dialogShow() @@ -114,17 +114,19 @@ def protocol_name(p): if p == 'h': return 'HTTP/Stratum' if p == 'n': return 'TCP/native' -def protocol_dialog(host, z): +def protocol_dialog(host, protocol, z): droid.dialogCreateAlert('Protocol',host) protocols = z.keys() l = [] + current = protocols.index(protocol) for p in protocols: l.append(protocol_name(p)) - droid.dialogSetSingleChoiceItems(l) + 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() @@ -780,7 +782,7 @@ def settings_loop(): is_encrypted = 'yes' if wallet.use_encryption else 'no' protocol = protocol_name(p) droid.fullShow(settings_layout) - droid.fullSetList("myListView",['Server: ' + server, 'Protocol: '+ protocol, 'Fee: '+fee, 'Password: '+is_encrypted, 'Seed']) + droid.fullSetList("myListView",['Server: ' + server, 'Port: '+port, 'Protocol: '+ protocol, 'Fee: '+fee, 'Password: '+is_encrypted, 'Seed']) set_listview() @@ -802,6 +804,7 @@ def settings_loop(): if event["name"] == "itemclick": pos = event["data"]["position"] + host, port, protocol = wallet.server.split(':') if pos == "0": #server host = server_dialog(plist) @@ -815,9 +818,20 @@ def settings_loop(): modal_dialog('error','invalid server') set_listview() - elif pos == "1": #protocol + elif pos == "1": #port + a_port = modal_input('Port', 'port number', port, "number") + if a_port: + if a_port != port: + srv = host + ':' + a_port + ':t' + try: + wallet.set_server(srv) + except: + modal_dialog('error','invalid port number') + set_listview() + + elif pos == "2": #protocol if host in plist: - srv = protocol_dialog(host, plist[host]) + srv = protocol_dialog(host, protocol, plist[host]) if srv: try: wallet.set_server(srv) @@ -825,8 +839,8 @@ def settings_loop(): modal_dialog('error','invalid server') set_listview() - elif pos == "2": #fee - fee = modal_input('fee', 'miners fee', str( Decimal( wallet.fee)/100000000 )) + elif pos == "3": #fee + fee = modal_input('fee', 'miners fee', str( Decimal( wallet.fee)/100000000 ), "number") if fee: try: fee = int( 100000000 * Decimal(fee) ) @@ -837,10 +851,10 @@ def settings_loop(): wallet.save() set_listview() - elif pos == "3": + elif pos == "4": change_password_dialog() - elif pos == "4": + elif pos == "5": seed_dialog() From 7feada05db0ecdc595223290b6cc12235591dbf9 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 10 Apr 2012 19:39:57 +0200 Subject: [PATCH 39/45] fee is Decimal --- client/electrum4a.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index 7a698ad8..774fe8ba 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -840,7 +840,7 @@ def settings_loop(): set_listview() elif pos == "3": #fee - fee = modal_input('fee', 'miners fee', str( Decimal( wallet.fee)/100000000 ), "number") + fee = modal_input('fee', 'miners fee', str( Decimal( wallet.fee)/100000000 ), "numberDecimal") if fee: try: fee = int( 100000000 * Decimal(fee) ) From bbf82c54f4366a608bc11e14b156e49730ac5b2b Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 10 Apr 2012 19:47:23 +0200 Subject: [PATCH 40/45] dialog for private servers --- client/electrum4a.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index 774fe8ba..772042b1 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -717,11 +717,17 @@ def contacts_loop(): 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 From 7405a758e87e5f8792bebc305b5d42d09f23c785 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 10 Apr 2012 19:57:39 +0200 Subject: [PATCH 41/45] protocols --- client/electrum4a.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index 772042b1..65b0436b 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -116,7 +116,10 @@ def protocol_name(p): def protocol_dialog(host, protocol, z): droid.dialogCreateAlert('Protocol',host) - protocols = z.keys() + if z: + protocols = z.keys() + else: + protocols = ['t','h','n'] l = [] current = protocols.index(protocol) for p in protocols: @@ -788,7 +791,7 @@ def settings_loop(): is_encrypted = 'yes' if wallet.use_encryption else 'no' protocol = protocol_name(p) droid.fullShow(settings_layout) - droid.fullSetList("myListView",['Server: ' + server, 'Port: '+port, 'Protocol: '+ protocol, 'Fee: '+fee, 'Password: '+is_encrypted, 'Seed']) + droid.fullSetList("myListView",['Server: ' + server, 'Protocol: '+ protocol, 'Port: '+port, 'Fee: '+fee, 'Password: '+is_encrypted, 'Seed']) set_listview() @@ -824,18 +827,7 @@ def settings_loop(): modal_dialog('error','invalid server') set_listview() - elif pos == "1": #port - a_port = modal_input('Port', 'port number', port, "number") - if a_port: - if a_port != port: - srv = host + ':' + a_port + ':t' - try: - wallet.set_server(srv) - except: - modal_dialog('error','invalid port number') - set_listview() - - elif pos == "2": #protocol + elif pos == "1": #protocol if host in plist: srv = protocol_dialog(host, protocol, plist[host]) if srv: @@ -845,6 +837,17 @@ def settings_loop(): 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('fee', 'miners fee', str( Decimal( wallet.fee)/100000000 ), "numberDecimal") if fee: From aa7135265556887274470c8123fe8a170a159d01 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 10 Apr 2012 20:08:25 +0200 Subject: [PATCH 42/45] check address before asking for password --- client/electrum4a.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index 65b0436b..e296cb2b 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -633,10 +633,15 @@ def payto_loop(): recipient = droid.fullQueryDetail("recipient").result.get('text') label = droid.fullQueryDetail("label").result.get('text') amount = droid.fullQueryDetail('amount').result.get('text') + + 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') + modal_dialog('Error','Invalid amount') continue result = pay_to(recipient, amount, wallet.fee, label) From 6fd27de85507f23e2db458c321dae2a66b13789a Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 10 Apr 2012 20:20:49 +0200 Subject: [PATCH 43/45] updated help --- client/ANDROID | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/client/ANDROID b/client/ANDROID index f64f5779..0f20289d 100644 --- a/client/ANDROID +++ b/client/ANDROID @@ -1,7 +1,7 @@ INSTALLATION INSTRUCTIONS FOR ANDROID -1. Install sl4a and py4a : you need at least revision 5 +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 @@ -29,8 +29,5 @@ 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. copy the logo in /sdcard/sl4a: -electrum_text_320.png - -4. to run the application, open sl4a and click on electrum4a.py +3. to run the application, open sl4a and click on electrum4a.py From f23e6c66b7747cb09a41c817e187b4fed704b904 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 10 Apr 2012 20:36:13 +0200 Subject: [PATCH 44/45] better help text --- client/electrum4a.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index e296cb2b..aac3c4ec 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -796,7 +796,7 @@ def settings_loop(): 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, 'Fee: '+fee, 'Password: '+is_encrypted, 'Seed']) + droid.fullSetList("myListView",['Server: ' + server, 'Protocol: '+ protocol, 'Port: '+port, 'Transaction fee: '+fee, 'Password: '+is_encrypted, 'Seed']) set_listview() @@ -854,7 +854,7 @@ def settings_loop(): set_listview() elif pos == "3": #fee - fee = modal_input('fee', 'miners fee', str( Decimal( wallet.fee)/100000000 ), "numberDecimal") + 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) ) From 4edb11c181e3647ea6e7eda6330f30392fe61b3b Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 10 Apr 2012 20:37:34 +0200 Subject: [PATCH 45/45] password update --- client/electrum4a.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/electrum4a.py b/client/electrum4a.py index aac3c4ec..f2477644 100755 --- a/client/electrum4a.py +++ b/client/electrum4a.py @@ -785,7 +785,8 @@ def change_password_dialog(): modal_dialog('Password updated','your wallet is encrypted') else: modal_dialog('No password','your wallet is not encrypted') - + return True + def settings_loop(): @@ -866,7 +867,8 @@ def settings_loop(): set_listview() elif pos == "4": - change_password_dialog() + if change_password_dialog(): + set_listview() elif pos == "5": seed_dialog()