commit 5669e2887b7c719310f337071e35a468db78ca5b
parent 350a4629010b5c56852e457d38f1b04c71e4c11b
Author: ThomasV <thomasv@gitorious>
Date: Tue, 3 Apr 2012 17:32:08 +0200
Electrum for Android, first commit
Diffstat:
A | client/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!")