URI: 
       tkivy: use plugins - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit a5e94ef0e8d6a92096711218179489e24ad53f0d
   DIR parent c803a8ecab1c85b88fd741d5ec420e1ee3370ad5
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Tue, 13 Oct 2015 12:12:49 +0200
       
       kivy: use plugins
       
       Diffstat:
         M electrum                            |       8 +++-----
         M gui/kivy/__init__.py                |       4 +++-
         M gui/kivy/main.kv                    |      10 +++++++---
         M gui/kivy/main_window.py             |      89 ++++++++++---------------------
         D gui/kivy/plugins/__init__.py        |       1 -
         D gui/kivy/plugins/exchange_rate.py   |     376 -------------------------------
         M gui/kivy/uix/screens.py             |      19 +++++++++++++------
         M gui/kivy/uix/ui_screens/network.kv  |       1 +
         A gui/kivy/uix/ui_screens/plugins.kv  |      16 ++++++++++++++++
         M gui/kivy/uix/ui_screens/settings.kv |      33 +++++++++++++++----------------
         M plugins/__init__.py                 |       6 +++---
         M plugins/exchange_rate.py            |       1 +
       
       12 files changed, 92 insertions(+), 472 deletions(-)
       ---
   DIR diff --git a/electrum b/electrum
       t@@ -444,7 +444,7 @@ if __name__ == '__main__':
                    'verbose': True,
                    'cmd': 'gui',
                    'gui': 'kivy' if is_kivy else 'android',
       -            'auto_connect': True,
       +            #'auto_connect': True,
                }
            else:
                config_options = args.__dict__
       t@@ -474,10 +474,8 @@ if __name__ == '__main__':
            cmd_name = config.get('cmd')
        
            # initialize plugins.
       -    plugins = None
       -    if not is_android:
       -        gui_name = config.get('gui', 'qt') if cmd_name == 'gui' else 'cmdline'
       -        plugins = Plugins(config, is_bundle or is_local or is_android, gui_name)
       +    gui_name = config.get('gui', 'qt') if cmd_name == 'gui' else 'cmdline'
       +    plugins = Plugins(config, is_bundle or is_local or is_android, gui_name)
        
            # get password if needed
            if cmd_name not in ['gui', 'daemon']:
   DIR diff --git a/gui/kivy/__init__.py b/gui/kivy/__init__.py
       t@@ -61,10 +61,11 @@ from main_window import ElectrumWindow
        
        class ElectrumGui:
        
       -    def __init__(self, config, network, app=None):
       +    def __init__(self, config, network, plugins, app=None):
                Logger.debug('ElectrumGUI: initialising')
                self.network = network
                self.config = config
       +        self.plugins = plugins
        
                #:TODO
                # implement kivy plugin mechanism that needs to be more extensible
       t@@ -85,5 +86,6 @@ class ElectrumGui:
        
                self.main_window = w = ElectrumWindow(config=self.config,
                                                      network=self.network,
       +                                              plugins = self.plugins,
                                                      gui_object=self)
                w.run()
   DIR diff --git a/gui/kivy/main.kv b/gui/kivy/main.kv
       t@@ -451,13 +451,17 @@ BoxLayout:
                            on_press: ao._dropdown.dismiss()
                            on_release: app.popup_dialog('network')
                        ActionButton:
       -                    text: _('Wallet')
       +                    text: _('Settings')
       +                    on_press: ao._dropdown.dismiss()
       +                    on_release: app.popup_dialog('settings')
       +                ActionButton:
       +                    text: _('Wallets')
                            on_press: ao._dropdown.dismiss()
                            on_release: app.popup_dialog('wallet')
                        ActionButton:
       -                    text: _('Preferences')
       +                    text: _('Plugins')
                            on_press: ao._dropdown.dismiss()
       -                    on_release: app.popup_dialog('settings')
       +                    on_release: app.popup_dialog('plugins')
                        
            ScreenManager:
                id: manager
   DIR diff --git a/gui/kivy/main_window.py b/gui/kivy/main_window.py
       t@@ -1,12 +1,15 @@
       +import re
        import sys
        import time
        import datetime
        import traceback
       +from decimal import Decimal
        
        from electrum import WalletStorage, Wallet
        from electrum.i18n import _, set_language
        from electrum.contacts import Contacts
        from electrum.util import profiler
       +from electrum.plugins import run_hook
        
        from kivy.app import App
        from kivy.core.window import Window
       t@@ -18,6 +21,7 @@ from kivy.cache import Cache
        from kivy.clock import Clock
        from kivy.factory import Factory
        from kivy.metrics import inch, metrics
       +from kivy.lang import Builder
        
        # lazy imports for factory so that widgets can be used in kv
        Factory.register('InstallWizard',
       t@@ -27,11 +31,9 @@ Factory.register('ELTextInput', module='electrum_gui.kivy.uix.screens')
        
        
        # delayed imports: for startup speed on android
       -notification = app = ref = format_satoshis = Builder = None
       +notification = app = ref = format_satoshis = None
        util = False
        
       -from decimal import Decimal
       -import re
        
        # register widget cache for keeping memory down timeout to forever to cache
        # the data
       t@@ -39,7 +41,8 @@ Cache.register('electrum_widgets', timeout=0)
        
        from kivy.uix.screenmanager import Screen
        from kivy.uix.tabbedpanel import TabbedPanel
       -
       +from kivy.uix.label import Label
       +from kivy.uix.checkbox import CheckBox
        
        Factory.register('TabbedCarousel', module='electrum_gui.kivy.uix.screens')
        
       t@@ -176,7 +179,6 @@ class ElectrumWindow(App):
            def __init__(self, **kwargs):
                # initialize variables
                self._clipboard = None
       -        self.exchanger = None
                self.info_bubble = None
                self.qrscanner = None
                self.nfcscanner = None
       t@@ -185,8 +187,10 @@ class ElectrumWindow(App):
                super(ElectrumWindow, self).__init__(**kwargs)
        
                title = _('Electrum App')
       -        self.network = network = kwargs.get('network', None)
                self.electrum_config = config = kwargs.get('config', None)
       +        self.network = network = kwargs.get('network', None)
       +        self.plugins = kwargs.get('plugins', [])
       +
                self.gui_object = kwargs.get('gui_object', None)
        
                #self.config = self.gui_object.config
       t@@ -206,6 +210,8 @@ class ElectrumWindow(App):
                self._trigger_notify_transactions = \
                    Clock.create_trigger(self.notify_transactions, 5)
        
       +
       +
            def set_url(self, instance, url):
                self.gui_object.set_url(url)
        
       t@@ -226,12 +232,23 @@ class ElectrumWindow(App):
                activity.bind(on_activity_result=on_qr_result)
                PythonActivity.mActivity.startActivityForResult(intent, 0)
        
       -    def build(self):
       -        global Builder
       -        if not Builder:
       -            from kivy.lang import Builder
       -
       +    def show_plugins(self, plugins_list):
       +        def on_checkbox_active(cb, value):
       +            self.plugins.toggle_enabled(self.electrum_config, cb.name)
       +        for item in self.plugins.descriptions:
       +            if 'kivy' not in item.get('available_for', []):
       +                continue
       +            name = item.get('name')
       +            label = Label(text=item.get('fullname'))
       +            plugins_list.add_widget(label)
       +            cb = CheckBox()
       +            cb.name = name
       +            p = self.plugins.get(name)
       +            cb.active = (p is not None) and p.is_enabled()
       +            cb.bind(active=on_checkbox_active)
       +            plugins_list.add_widget(cb)
        
       +    def build(self):
                return Builder.load_file('gui/kivy/main.kv')
        
            def _pause(self):
       t@@ -403,52 +420,11 @@ class ElectrumWindow(App):
                self.wallet = None
        
        
       -    def create_quote_text(self, btc_balance, mode='normal'):
       -        '''
       -        '''
       -        if not self.exchanger:
       -            return
       -        quote_currency = self.exchanger.currency
       -        quote_balance = self.exchanger.exchange(btc_balance, quote_currency)
       -
       -        if quote_currency and mode == 'symbol':
       -            quote_currency = self.exchanger.symbols.get(quote_currency,
       -                                                        quote_currency)
       -
       -        if quote_balance is None:
       -            quote_text = u"..."
       -        else:
       -            quote_text = u"%s%.2f" % (quote_currency,
       -                                     quote_balance)
       -        return quote_text
        
            def set_currencies(self, quote_currencies):
                self.currencies = sorted(quote_currencies.keys())
                self._trigger_update_status()
        
       -    def get_history_rate(self, item, btc_balance, mintime):
       -        '''Historical rates: currently only using coindesk by default.
       -        '''
       -        maxtime = datetime.datetime.now().strftime('%Y-%m-%d')
       -        rate = self.exchanger.get_history_rate(item, btc_balance, mintime,
       -                                                maxtime)
       -
       -        return self.set_history_rate(item, rate)
       -
       -
       -    def set_history_rate(self, item, rate):
       -        '''
       -        '''
       -        #TODO: fix me allow other currencies to be used for history rates
       -        quote_currency = self.exchanger.symbols.get('USD', 'USD')
       -        if rate is None:
       -            quote_text = "..."
       -        else:
       -            quote_text = "{0}{1:.3}".format(quote_currency, rate)
       -        item = item()
       -        if item:
       -            item.quote_text = quote_text
       -        return quote_text
        
        
            @profiler
       t@@ -485,7 +461,7 @@ class ElectrumWindow(App):
                            unconfirmed =  " [%s unconfirmed]" %( self.format_amount(u, True).strip())
                        if x:
                            unmatured =  " [%s unmatured]"%(self.format_amount(x, True).strip())
       -                quote_text = self.create_quote_text(Decimal(c+u+x)/100000000, mode='symbol') or ''
       +                #quote_text = self.create_quote_text(Decimal(c+u+x)/100000000, mode='symbol') or ''
                        self.status = text.strip() + ' ' + self.base_unit
                else:
                    self.status = _("Not connected")
       t@@ -510,13 +486,6 @@ class ElectrumWindow(App):
        
            @profiler
            def update_wallet(self, *dt):
       -        '''
       -        '''
       -        if not self.exchanger:
       -            from electrum_gui.kivy.plugins.exchange_rate import Exchanger
       -            self.exchanger = Exchanger(self)
       -            self.exchanger.start()
       -            return
                self._trigger_update_status()
                if self.wallet.up_to_date or not self.network or not self.network.is_connected():
                    self.update_history_tab()
   DIR diff --git a/gui/kivy/plugins/__init__.py b/gui/kivy/plugins/__init__.py
       t@@ -1 +0,0 @@
       - 
   DIR diff --git a/gui/kivy/plugins/exchange_rate.py b/gui/kivy/plugins/exchange_rate.py
       t@@ -1,376 +0,0 @@
       -# -*- encoding: utf8 -*-
       -
       -'''Module exchange_rate:
       -
       -This module is responsible for getting the conversion rates from different
       -bitcoin exchanges.
       -'''
       -
       -import decimal
       -import json
       -
       -from kivy.network.urlrequest import UrlRequest
       -from kivy.event import EventDispatcher
       -from kivy.properties import (OptionProperty, StringProperty, AliasProperty,
       -    ListProperty)
       -from kivy.clock import Clock
       -from kivy.cache import Cache
       -
       -# Register local cache
       -Cache.register('history_rate', timeout=220)
       -
       -EXCHANGES = ["BitcoinAverage",
       -             "BitcoinVenezuela",
       -             "BitPay",
       -             "Blockchain",
       -             "BTCChina",
       -             "CaVirtEx",
       -             "Coinbase",
       -             "CoinDesk",
       -             "LocalBitcoins",
       -             "Winkdex"]
       -
       -HISTORY_EXCHNAGES = ['Coindesk',
       -                     'Winkdex',
       -                     'BitcoinVenezuela']
       -
       -
       -class Exchanger(EventDispatcher):
       -    ''' Provide exchanges rate between crypto and different national
       -    currencies. See Module Documentation for details.
       -    '''
       -
       -    symbols = {'ALL': u'Lek', 'AED': u'د.إ', 'AFN':u'؋', 'ARS': u'$',
       -        'AMD': u'֏', 'AWG': u'ƒ', 'ANG': u'ƒ', 'AOA': u'Kz', 'BDT': u'৳',
       -        'BHD': u'BD', 'BIF': u'FBu', 'BTC': u'BTC', 'BTN': u'Nu', 'CDF': u'FC',
       -        'CHF': u'CHF', 'CLF': u'UF', 'CLP':u'$', 'CVE': u'$', 'DJF':u'Fdj',
       -        'DZD': u'دج', 'AUD': u'$', 'AZN': u'ман', 'BSD': u'$', 'BBD': u'$',
       -        'BYR': u'p', 'CRC': u'₡', 'BZD': u'BZ$', 'BMD': u'$', 'BOB': u'$b',
       -        'BAM': u'KM', 'BWP': u'P', 'BGN': 'uлв', 'BRL': u'R$', 'BND': u'$',
       -        'KHR': u'៛', 'CAD': u'$', 'ERN': u'Nfk', 'ETB': u'Br', 'KYD': u'$',
       -        'USD': u'$', 'CLP': u'$', 'HRK': u'kn', 'CUP': u'₱', 'CZK': u'Kč',
       -        'DKK': u'kr', 'DOP': u'RD$', 'XCD': u'$', 'EGP': u'£', 'SVC': u'$' ,
       -        'EEK': u'kr', 'EUR': u'€', u'FKP': u'£', 'FJD': u'$', 'GHC': u'¢',
       -        'GIP': u'£', 'GTQ': u'Q', 'GBP': u'£', 'GYD': u'$', 'HNL': u'L',
       -        'HKD': u'$', 'HUF': u'Ft', 'ISK': u'kr', 'INR': u'₹', 'IDR': u'Rp',
       -        'IRR': u'﷼', 'IMP': '£', 'ILS': '₪', 'COP': '$', 'JMD': u'J$',
       -        'JPY': u'¥', 'JEP': u'£', 'KZT': u'лв', 'KPW': u'₩', 'KRW': u'₩',
       -        'KGS': u'лв', 'LAK': u'₭', 'LVL': u'Ls', 'CNY': u'¥'}
       -
       -    _use_exchange = OptionProperty('Blockchain', options=EXCHANGES)
       -    '''This is the exchange to be used for getting the currency exchange rates
       -    '''
       -
       -    _currency = StringProperty('EUR')
       -    '''internal use only
       -    '''
       -
       -    def _set_currency(self, value):
       -        value = str(value)
       -        if self.use_exchange == 'CoinDesk':
       -            self._update_cd_currency(self.currency)
       -            return
       -        self._currency = value
       -        self.parent.electrum_config.set_key('currency', value, True)
       -
       -    def _get_currency(self):
       -        self._currency = self.parent.electrum_config.get('currency', 'EUR')
       -        return self._currency
       -
       -    currency = AliasProperty(_get_currency, _set_currency, bind=('_currency',))
       -
       -    currencies = ListProperty(['EUR', 'GBP', 'USD'])
       -    '''List of currencies supported by the current exchanger plugin.
       -
       -    :attr:`currencies` is a `ListProperty` default to ['Eur', 'GBP'. 'USD'].
       -    '''
       -
       -    def _get_useex(self):
       -        if not self.parent:
       -            return self._use_exchange
       -
       -        self._use_exchange = self.parent.electrum_config.get('use_exchange',
       -                                                             'Blockchain')
       -        return self._use_exchange
       -
       -    def _set_useex(self, value):
       -        if not self.parent:
       -            return self._use_exchange
       -        self.parent.electrum_config.set_key('use_exchange', value, True)
       -        self._use_exchange = value
       -
       -    use_exchange = AliasProperty(_get_useex, _set_useex,
       -                                 bind=('_use_exchange', ))
       -
       -    def __init__(self, parent):
       -        super(Exchanger, self).__init__()
       -        self.parent = parent
       -        self.quote_currencies = None
       -        self.exchanges = EXCHANGES
       -        self.history_exchanges = HISTORY_EXCHNAGES
       -
       -    def exchange(self, btc_amount, quote_currency):
       -        if self.quote_currencies is None:
       -            return None
       -
       -        quote_currencies = self.quote_currencies.copy()
       -        if quote_currency not in quote_currencies:
       -            return None
       -
       -        return btc_amount * decimal.Decimal(quote_currencies[quote_currency])
       -
       -    def get_history_rate(self, item, btc_amt, mintime, maxtime):
       -        def on_success(request, response):
       -            response = json.loads(response)
       -
       -            try:
       -                hrate = response['bpi'][mintime]
       -                hrate = abs(btc_amt) * decimal.Decimal(hrate)
       -                Cache.append('history_rate', uid, hrate)
       -            except KeyError:
       -                hrate = 'not found'
       -
       -            self.parent.set_history_rate(item, hrate)
       -
       -        # Check local cache before getting data from remote
       -        exchange = 'coindesk'
       -        uid = '{}:{}'.format(exchange, mintime)
       -        hrate = Cache.get('history_rate', uid)
       -
       -        if hrate:
       -            return hrate
       -
       -        req = UrlRequest(url='https://api.coindesk.com/v1/bpi/historical'
       -                         '/close.json?start={}&end={}'
       -                         .format(mintime, maxtime)
       -            ,on_success=on_success, timeout=15)
       -        return None
       -
       -    def update_rate(self, dt):
       -        ''' This is called from :method:`start` every X seconds; to update the
       -        rates for currencies for the currently selected exchange.
       -        '''
       -        if not self.parent.network or not self.parent.network.is_connected():
       -            return
       -
       -        # temporarily disabled
       -        return
       -
       -        update_rates = {
       -            "BitcoinAverage": self.update_ba,
       -            "BitcoinVenezuela": self.update_bv,
       -            "BitPay": self.update_bp,
       -            "Blockchain": self.update_bc,
       -            "BTCChina": self.update_CNY,
       -            "CaVirtEx": self.update_cv,
       -            "CoinDesk": self.update_cd,
       -            "Coinbase": self.update_cb,
       -            "LocalBitcoins": self.update_lb,
       -            "Winkdex": self.update_wd,
       -        }
       -        try:
       -            update_rates[self.use_exchange]()
       -        except KeyError:
       -            return
       -
       -    def update_wd(self):
       -
       -        def on_success(request, response):
       -            response = json.loads(response)
       -            quote_currencies = {'USD': 0.0}
       -            lenprices = len(response["prices"])
       -            usdprice = response['prices'][lenprices-1]['y']
       -
       -            try:
       -                quote_currencies["USD"] = decimal.Decimal(usdprice)
       -            except KeyError:
       -                pass
       -
       -            self.quote_currencies = quote_currencies
       -            self.parent.set_currencies(quote_currencies)
       -
       -        req = UrlRequest(
       -            url='https://winkdex.com/static/data/0_600_288.json',
       -                        on_success=on_success,
       -                        timeout=5)
       -
       -    def update_cd_currency(self, currency):
       -
       -        def on_success(request, response):
       -            response = json.loads(response)
       -            quote_currencies = self.quote_currencies
       -            quote_currencies[currency] =\
       -                str(response['bpi'][str(currency)]['rate_float'])
       -            self.parent.set_currencies(quote_currencies)
       -
       -        req = UrlRequest(
       -            url='https://api.coindesk.com/v1/bpi/currentprice/'\
       -                + str(currency) + '.json',on_success=on_success, timeout=5)
       -
       -    def update_cd(self):
       -
       -        def on_success(request, response):
       -            quote_currencies = {}
       -            response = json.loads(response)
       -
       -            for cur in response:
       -                quote_currencies[str(cur["currency"])] = 0.0
       -
       -            self.quote_currencies = quote_currencies
       -            self.update_cd_currency(self.currency)
       -
       -        req = UrlRequest(
       -            url='https://api.coindesk.com/v1/bpi/supported-currencies.json',
       -                        on_success=on_success,
       -                        timeout=5)
       -
       -    def update_cv(self):
       -        def on_success(request, response):
       -            response = json.loads(response)
       -            quote_currencies = {"CAD": 0.0}
       -            cadprice = response["last"]
       -            try:
       -                quote_currencies["CAD"] = decimal.Decimal(cadprice)
       -                self.quote_currencies = quote_currencies
       -            except KeyError:
       -                pass
       -            self.parent.set_currencies(quote_currencies)
       -
       -        req = UrlRequest(url='https://www.cavirtex.com/api/CAD/ticker.json',
       -                        on_success=on_success,
       -                        timeout=5)
       -
       -    def update_CNY(self):
       -
       -        def on_success(request, response):
       -            quote_currencies = {"CNY": 0.0}
       -            cnyprice = response["ticker"]["last"]
       -            try:
       -                quote_currencies["CNY"] = decimal.Decimal(cnyprice)
       -                self.quote_currencies = quote_currencies
       -            except KeyError:
       -                pass
       -            self.parent.set_currencies(quote_currencies)
       -
       -        req = UrlRequest(url='https://data.btcchina.com/data/ticker',
       -                        on_success=on_success,
       -                        timeout=5)
       -
       -    def update_bp(self):
       -
       -        def on_success(request, response):
       -            quote_currencies = {}
       -            try:
       -                for r in response:
       -                    quote_currencies[str(r['code'])] = decimal.Decimal(r['rate'])
       -                self.quote_currencies = quote_currencies
       -            except KeyError:
       -                pass
       -            self.parent.set_currencies(quote_currencies)
       -
       -        req = UrlRequest(url='https://bitpay.com/api/rates',
       -                        on_success=on_success,
       -                        timeout=5)
       -
       -    def update_cb(self):
       -
       -        def _lookup_rate(response, quote_id):
       -            return decimal.Decimal(str(response[str(quote_id)]))
       -
       -        def on_success(request, response):
       -            quote_currencies = {}
       -            try:
       -                for r in response:
       -                    if r[:7] == "btc_to_":
       -                        quote_currencies[r[7:].upper()] =\
       -                            _lookup_rate(response, r)
       -                self.quote_currencies = quote_currencies
       -            except KeyError:
       -                pass
       -            self.parent.set_currencies(quote_currencies)
       -
       -        req = UrlRequest(
       -            url='https://coinbase.com/api/v1/currencies/exchange_rates',
       -            on_success=on_success,
       -            timeout=5)
       -
       -    def update_bc(self):
       -
       -        def _lookup_rate(response, quote_id):
       -            return decimal.Decimal(str(response[str(quote_id)]["15m"]))
       -
       -        def on_success(request, response):
       -            quote_currencies = {}
       -            try:
       -                for r in response:
       -                    quote_currencies[r] = _lookup_rate(response, r)
       -                self.quote_currencies = quote_currencies
       -            except KeyError, TypeError:
       -                pass
       -            self.parent.set_currencies(quote_currencies)
       -
       -        req = UrlRequest(url='https://blockchain.info/ticker',
       -                        on_success=on_success,
       -                        timeout=5)
       -
       -    def update_lb(self):
       -        def _lookup_rate(response, quote_id):
       -            return decimal.Decimal(response[str(quote_id)]["rates"]["last"])
       -
       -        def on_success(request, response):
       -            quote_currencies = {}
       -            try:
       -                for r in response:
       -                    quote_currencies[r] = _lookup_rate(response, r)
       -                self.quote_currencies = quote_currencies
       -            except KeyError:
       -                pass
       -            self.parent.set_currencies(quote_currencies)
       -
       -        req = UrlRequest(
       -            url='https://localbitcoins.com/bitcoinaverage/ticker-all-currencies/',
       -            on_success=on_success,
       -            timeout=5)
       -
       -
       -    def update_ba(self):
       -
       -        def on_success(request, response):
       -            quote_currencies = {}
       -            try:
       -                for r in response:
       -                    quote_currencies[r] = decimal.Decimal(response[r][u'last'])
       -                self.quote_currencies = quote_currencies
       -            except TypeError:
       -                pass
       -            self.parent.set_currencies(quote_currencies)
       -
       -        req = UrlRequest(url='https://api.bitcoinaverage.com/ticker/global/all',
       -                        on_success=on_success,
       -                        timeout=5)
       -
       -    def update_bv(self):
       -
       -        def on_success(request, response):
       -            quote_currencies = {}
       -            try:
       -                for r in response["BTC"]:
       -                    quote_currencies[r] = decimal.Decimal(response['BTC'][r])
       -                self.quote_currencies = quote_currencies
       -            except KeyError:
       -                pass
       -            self.parent.set_currencies(quote_currencies)
       -
       -        req = UrlRequest(url='https://api.bitcoinvenezuela.com/',
       -                        on_success=on_success,
       -                        timeout=5)
       -
       -    def start(self):
       -        self.update_rate(0)
       -        # check every 20 seconds
       -        Clock.unschedule(self.update_rate)
       -        Clock.schedule_interval(self.update_rate, 20)
       -
       -    def stop(self):
       -        Clock.unschedule(self.update_rate)
       -
   DIR diff --git a/gui/kivy/uix/screens.py b/gui/kivy/uix/screens.py
       t@@ -15,6 +15,8 @@ from kivy.factory import Factory
        from electrum.i18n import _
        from electrum.util import profiler
        from electrum import bitcoin
       +from electrum.util import timestamp_to_datetime
       +from electrum.plugins import run_hook
        
        class CScreen(Factory.Screen):
        
       t@@ -84,6 +86,10 @@ class HistoryScreen(CScreen):
                ra_dialog.item = item
                ra_dialog.open()
        
       +    def get_history_rate(self, btc_balance, timestamp):
       +        date = timestamp_to_datetime(timestamp)
       +        return run_hook('historical_value_str', btc_balance, date)
       +
            def parse_history(self, items):
                for item in items:
                    tx_hash, conf, value, timestamp, balance = item
       t@@ -121,7 +127,11 @@ class HistoryScreen(CScreen):
                        label = _('Pruned transaction outputs')
                        is_default_label = False
        
       -            yield (conf, icon, time_str, label, v_str, balance_str, tx_hash)
       +            quote_currency = 'USD'
       +            rate = self.get_history_rate(value, timestamp)
       +            quote_text = "..." if rate is None else "{0:.3} {1}".format(rate, quote_currency)
       +
       +            yield (conf, icon, time_str, label, v_str, balance_str, tx_hash, quote_text)
        
            def update(self, see_all=False):
        
       t@@ -134,20 +144,17 @@ class HistoryScreen(CScreen):
                history_add = history_card.ids.content.add_widget
                history_add(last_widget)
                RecentActivityItem = Factory.RecentActivityItem
       -        get_history_rate = self.app.get_history_rate
                count = 0
                for item in history:
                    count += 1
       -            conf, icon, date_time, address, amount, balance, tx = item
       +            conf, icon, date_time, address, amount, balance, tx, quote_text = item
                    ri = RecentActivityItem()
                    ri.icon = icon
                    ri.date = date_time
                    mintimestr = date_time.split()[0]
                    ri.address = address
                    ri.amount = amount
       -            ri.quote_text = get_history_rate(ref(ri),
       -                                             Decimal(amount),
       -                                             mintimestr)
       +            ri.quote_text = quote_text
                    ri.balance = balance
                    ri.confirmations = conf
                    ri.tx_hash = tx
   DIR diff --git a/gui/kivy/uix/ui_screens/network.kv b/gui/kivy/uix/ui_screens/network.kv
       t@@ -9,6 +9,7 @@ Popup:
                app.network.set_parameters(host.text, nd.port, nd.protocol, nd.proxy, auto_connect.active)
        
            BoxLayout:
       +
                orientation: 'vertical'
        
                GridLayout:
   DIR diff --git a/gui/kivy/uix/ui_screens/plugins.kv b/gui/kivy/uix/ui_screens/plugins.kv
       t@@ -0,0 +1,16 @@
       +Popup:
       +    title: _('Plugins')
       +    id: popup
       +    BoxLayout:
       +        orientation: 'vertical'
       +        GridLayout:
       +            size_hint_y: None
       +            cols: 2
       +            id: plugins_list
       +            on_parent:
       +                app.show_plugins(plugins_list)
       +        Button:
       +            size_hint_y: None
       +            height: '48dp'
       +            text: _('Close')
       +            on_release: popup.dismiss()
   DIR diff --git a/gui/kivy/uix/ui_screens/settings.kv b/gui/kivy/uix/ui_screens/settings.kv
       t@@ -1,27 +1,26 @@
        Popup:
            id: settings
            title: _('Settings')
       +
            BoxLayout:
        
       -        Button:
       -            size_hint_y: None
       -            height: '48dp'
       -            text: 'Button normal'
       -        
       -        Button:
       -            size_hint_y: None
       -            height: '48dp'
       -            text: 'Button down'
       -            state: 'down'
       +        orientation: 'vertical'
       +        size_hint_y: None
        
       -        Button:
       -            size_hint_y: None
       -            height: '48dp'
       -            text: 'Button disabled'
       -            disabled: True
       +        GridLayout:
       +            cols: 2
       +            Label:
       +                text: _('Base unit')
       +                height: '48dp'
       +            Spinner:
       +                text: 'BTC'
       +                values: ('BTC', 'mBTC')
       +                height: '48dp'
                    
                Button:
       -            size_hint_y: None
       +            #size_hint_y: None
                    height: '48dp'
       -            text: 'close'
       +            text: _('Close')
                    on_release: settings.dismiss()
       +        #Widget:
       +        #    size_hint_y: None
   DIR diff --git a/plugins/__init__.py b/plugins/__init__.py
       t@@ -57,7 +57,7 @@ descriptions = [
                'name': 'exchange_rate',
                'fullname': _("Exchange rates"),
                'description': _("Exchange rates and currency conversion tools."),
       -        'available_for': ['qt'],
       +        'available_for': ['qt','kivy'],
            },
            {
                'name': 'greenaddress_instant',
       t@@ -78,10 +78,10 @@ descriptions = [
                'name': 'labels',
                'fullname': _('LabelSync'),
                'description': '\n'.join([
       -            _("The new and improved LabelSync plugin. This can sync your labels across multiple Electrum installs by using a remote database to save your data. Labels, transactions ids and addresses are encrypted before they are sent to the remote server."),
       +            _("Synchronize your labels across multiple Electrum installs by using a remote database to save your data. Labels, transactions ids and addresses are encrypted before they are sent to the remote server."),
                    _("The label sync's server software is open-source as well and can be found on github.com/maran/electrum-sync-server")
                ]),
       -        'available_for': ['qt']
       +        'available_for': ['qt','kivy']
            },
            {
                'name': 'plot',
   DIR diff --git a/plugins/exchange_rate.py b/plugins/exchange_rate.py
       t@@ -423,6 +423,7 @@ class Plugin(BasePlugin, ThreadJob):
                    return "%s" % (self.ccy_amount_str(value, True))
                return _("No data")
        
       +    @hook
            def historical_value_str(self, satoshis, d_t):
                rate = self.exchange.historical_rate(self.ccy, d_t)
                # Frequently there is no rate for today, until tomorrow :)