electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 5669e2887b7c719310f337071e35a468db78ca5b
parent 350a4629010b5c56852e457d38f1b04c71e4c11b
Author: ThomasV <thomasv@gitorious>
Date:   Tue,  3 Apr 2012 17:32:08 +0200

Electrum for Android, first commit

Diffstat:
Aclient/electrum4a.py | 511+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 511 insertions(+), 0 deletions(-)

diff --git a/client/electrum4a.py b/client/electrum4a.py @@ -0,0 +1,511 @@ +#!/usr/bin/env python +# +# Electrum - lightweight Bitcoin client +# Copyright (C) 2011 thomasv@gitorious +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + + +import android +from interface import WalletSynchronizer +from wallet import Wallet +from wallet import format_satoshis +from decimal import Decimal + +droid = android.Android() +wallet = Wallet() +wallet.set_path("/sdcard/electrum.dat") +wallet.read() + + +def get_history_layout(n): + lines = wallet.get_tx_history()[-n:] + rows = "" + for line in lines: + import datetime + v = line['value'] + if line.has_key('timestamp'): + dt = datetime.datetime.fromtimestamp( line['timestamp'] ) + if dt.date() == dt.today().date(): + time_str = str( dt.time() ) + else: + time_str = str( dt.date() ) + else: + time_str = 'pending' + + label = line.get('label') + if not label: label = line['tx_hash'] + + rows += """ + <TableRow> + <TextView + android:text="%s" + android:padding="2px" /> + <TextView + android:text="%s" + android:padding="2px" /> + <TextView + android:text="%s" + android:gravity="right" + android:padding="2px" /> + </TableRow>"""%(time_str, ' '+ label[0:10]+ '... ', format_satoshis(v)) + + + output = """ +<TableLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:stretchColumns="*"> + %s +</TableLayout> +"""% rows + return output + + + +def show_addresses(): + droid.dialogCreateAlert("Addresses:") + l = [] + for i in range(len(wallet.addresses)): + addr = wallet.addresses[i] + l.append( wallet.labels.get(addr,'') + ' ' + addr) + + droid.dialogSetItems(l) + droid.dialogShow() + response = droid.dialogGetResponse().result + droid.dialogDismiss() + + # show qr code + print response + + + +main_layout = """<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/background" + android:orientation="vertical" android:layout_width="match_parent" + android:layout_height="match_parent" android:background="#ff000000"> + + <TextView android:id="@+id/historyTextView" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="History" + android:textAppearance="?android:attr/textAppearanceLarge" + android:gravity="center_vertical|center_horizontal|center"> + </TextView> + + %s + + <TextView android:id="@+id/balanceTextView" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="TextView" + android:textAppearance="?android:attr/textAppearanceLarge" + android:gravity="center_vertical|center_horizontal|center"> + </TextView> + + <LinearLayout android:layout_width="match_parent" + android:layout_height="wrap_content" android:id="@+id/linearLayout1"> + <Button android:id="@+id/buttonSend" android:layout_width="wrap_content" + android:layout_height="wrap_content" android:text="Send"></Button> + <Button android:id="@+id/buttonReceive" android:layout_width="wrap_content" + android:layout_height="wrap_content" android:text="Receive"></Button> + <Button android:id="@+id/buttonQuit" android:layout_width="wrap_content" + android:layout_height="wrap_content" android:text="Quit"></Button> + </LinearLayout> + +</LinearLayout> +"""%get_history_layout(10) + + +payto_layout="""<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/background" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="#ff000000"> + + <TextView android:id="@+id/recipientTextView" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Pay to:" + android:textAppearance="?android:attr/textAppearanceLarge" + android:gravity="left"> + </TextView> + + <EditText android:id="@+id/recipient" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:tag="Tag Me" android:inputType="textCapWords|textPhonetic|number"> + </EditText> + + <TextView android:id="@+id/labelTextView" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Description:" + android:textAppearance="?android:attr/textAppearanceLarge" + android:gravity="left"> + </TextView> + + <EditText android:id="@+id/label" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:tag="Tag Me" android:inputType="textCapWords|textPhonetic|number"> + </EditText> + + <TextView android:id="@+id/amountLabelTextView" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Amount:" + android:textAppearance="?android:attr/textAppearanceLarge" + android:gravity="left"> + </TextView> + + <EditText android:id="@+id/amount" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:tag="Tag Me" android:inputType="textCapWords|number"> + </EditText> + + <LinearLayout android:layout_width="match_parent" + android:layout_height="wrap_content" android:id="@+id/linearLayout1"> + <Button android:id="@+id/buttonContacts" android:layout_width="wrap_content" + android:layout_height="wrap_content" android:text="Contacts"></Button> + <Button android:id="@+id/buttonPay" android:layout_width="wrap_content" + android:layout_height="wrap_content" android:text="Send"></Button> + <Button android:id="@+id/buttonCancelSend" android:layout_width="wrap_content" + android:layout_height="wrap_content" android:text="Cancel"></Button> + </LinearLayout> +</LinearLayout> +""" + + +settings_layout = """<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/background" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="#ff000000"> + + <TextView android:id="@+id/serverTextView" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Server:" + android:textAppearance="?android:attr/textAppearanceLarge" + android:gravity="left"> + </TextView> + + <EditText android:id="@+id/server" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:tag="Tag Me" android:inputType="*"> + </EditText> + + <LinearLayout android:layout_width="match_parent" + android:layout_height="wrap_content" android:id="@+id/linearLayout1"> + <Button android:id="@+id/buttonServer" android:layout_width="wrap_content" + android:layout_height="wrap_content" android:text="Server List"></Button> + <Button android:id="@+id/buttonSave" android:layout_width="wrap_content" + android:layout_height="wrap_content" android:text="Save"></Button> + <Button android:id="@+id/buttonCancel" android:layout_width="wrap_content" + android:layout_height="wrap_content" android:text="Cancel"></Button> + </LinearLayout> + +</LinearLayout> +""" + + + +droid.addOptionsMenuItem("Settings","settings",None,"") +droid.addOptionsMenuItem("Quit","quit",None,"") + + +def show_balance(): + c, u = wallet.get_balance() + droid.fullSetProperty("balanceTextView","text","Balance:"+format_satoshis(c)) + + +def recipient_dialog(): + title = 'Pay to:' + message = ('Select recipient') + droid.dialogCreateAlert(title, message) + droid.dialogSetItems(wallet.addressbook) + droid.dialogShow() + response = droid.dialogGetResponse() + result = response.result.get('item') + droid.dialogDismiss() + if result is not None: + addr = wallet.addressbook[result] + return addr + + +def pay_to(recipient, amount, fee, label): + + if wallet.use_encryption: + password = droid.dialogGetPassword('Password').result + print "password", password + else: + password = None + + droid.dialogCreateSpinnerProgress("Electrum", "signing transaction...") + droid.dialogShow() + tx = wallet.mktx( recipient, amount, label, password, fee) + print tx + droid.dialogDismiss() + + if tx: + r, h = wallet.sendtx( tx ) + droid.dialogCreateAlert('tx sent', h) + droid.dialogSetPositiveButtonText('OK') + droid.dialogShow() + response = droid.dialogGetResponse().result + droid.dialogDismiss() + return h + else: + return 'error' + + + + + + + +if not wallet.file_exists: + droid.dialogCreateAlert("wallet file not found") + droid.dialogSetPositiveButtonText('OK') + droid.dialogShow() + resp = droid.dialogGetResponse().result + exit(1) + + + + +droid.dialogCreateSpinnerProgress("Electrum", "synchronizing") +droid.dialogShow() +WalletSynchronizer(wallet,True).start() +wallet.update() +wallet.save() +droid.dialogDismiss() +droid.vibrate() + + + +def main_loop(): + + droid.fullShow(main_layout) + show_balance() + + out = None + while out is None: + + event = droid.eventWait().result + print "got event in main loop", event + + if event["name"]=="click": + id=event["data"]["id"] + if id=="buttonQuit": + out = 'exit' + + elif id=="buttonSend": + out = 'payto' + + elif id=="buttonReceive": + show_addresses() + + elif id=="buttonQuit": + out = 'quit' + + elif event["name"]=="settings": + out = 'settings' + + elif event["name"]=="quit": + out = 'quit' + + #print droid.fullSetProperty("background","backgroundColor","0xff7f0000") + #elif event["name"]=="screen": + # if event["data"]=="destroy": + # out = 'exit' + + #droid.fullDismiss() + return out + +def payto_loop(): + droid.fullShow(payto_layout) + out = None + while out is None: + event = droid.eventWait().result + print "got event in payto loop", event + + if event["name"] == "click": + id = event["data"]["id"] + + if id=="buttonPay": + + droid.fullQuery() + recipient = droid.fullQueryDetail("recipient").result.get('text') + label = droid.fullQueryDetail("label").result.get('text') + amount = droid.fullQueryDetail('amount').result.get('text') + fee = '0.001' + amount = int( 100000000 * Decimal(amount) ) + fee = int( 100000000 * Decimal(fee) ) + result = pay_to(recipient, amount, fee, label) + + droid.dialogCreateAlert('result', result) + droid.dialogSetPositiveButtonText('OK') + droid.dialogShow() + droid.dialogGetResponse() + droid.dialogDismiss() + out = 'main' + + + elif id=="buttonContacts": + addr = recipient_dialog() + 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"]=="screen": + # if event["data"]=="destroy": + # out = 'main' + + #droid.fullDismiss() + return out + + +def history_loop(): + layout = get_history_layout() + droid.fullShow(layout) + out = None + while out is None: + event = droid.eventWait().result + print "got event in history loop", event + if event["name"] == "click": + + if event["data"]["text"] == "OK": + out = 'main' + + elif event["name"] == "key": + print repr(event["data"]["key"]) + if event["data"]["key"] == '4': + out = 'main' + + #elif event["name"]=="screen": + # if event["data"]=="destroy": + # out = 'main' + + #droid.fullQuery() + #print "Data entered =",droid.fullQueryDetail("editText1").result + #droid.fullDismiss() + return out + +def server_dialog(plist): + droid.dialogCreateAlert("servers") + droid.dialogSetItems( plist.keys() ) + droid.dialogShow() + i = droid.dialogGetResponse().result.get('item') + droid.dialogDismiss() + if i is not None: + response = plist.keys()[i] + return response + +def protocol_dialog(plist): + options=["TCP","HTTP","native"] + droid.dialogCreateAlert("Protocol") + droid.dialogSetSingleChoiceItems(options) + + + +def settings_loop(): + droid.fullShow(settings_layout) + droid.fullSetProperty("server","text",wallet.server) + + out = None + while out is None: + event = droid.eventWait().result + if event["name"] == "click": + + id = event["data"]["id"] + + 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) + p = plist[host] + port = p['t'] + srv = host + ':' + port + ':t' + droid.fullSetProperty("server","text",srv) + + elif id=="buttonSave": + droid.fullQuery() + srv = droid.fullQueryDetail("server").result.get('text') + try: + wallet.set_server(srv) + out = 'main' + except: + droid.dialogCreateAlert('error') + droid.dialogSetPositiveButtonText('OK') + droid.dialogShow() + droid.dialogGetResponse() + droid.dialogDismiss() + + elif id=="buttonCancel": + out = 'main' + + elif event["name"] == "key": + print repr(event["data"]["key"]) + if event["data"]["key"] == '4': + out = 'main' + + elif event["name"]=="quit": + out = 'quit' + + return out + + + + +s = 'main' +while True: + if s == 'main': + s = main_loop() + elif s == 'payto': + s = payto_loop() + elif s == 'settings': + s = settings_loop() + elif s == 'history': + s = history_loop() + elif s == 'contacts': + s = contacts_loop() + else: + break + +droid.fullDismiss() +droid.makeToast("Bye!")