[Dextrose] [PATCH] Database support for 3G control panel

Martin Abente martin.abente.lahaye at gmail.com
Tue Jan 11 09:52:52 EST 2011


From: Andrés Ambrois <andresambrois at gmail.com>

For more information please look at #1630

---
 configure.ac                                       |    1 +
 .../cpsection/modemconfiguration/Makefile.am       |    2 +
 .../cpsection/modemconfiguration/config.py.in      |   20 ++
 extensions/cpsection/modemconfiguration/model.py   |  119 +++++++++++++
 extensions/cpsection/modemconfiguration/view.py    |  184 ++++++++++++++++----
 5 files changed, 292 insertions(+), 34 deletions(-)
 create mode 100644 extensions/cpsection/modemconfiguration/config.py.in
 mode change 100755 => 100644 extensions/cpsection/modemconfiguration/model.py

diff --git a/configure.ac b/configure.ac
index 13a2f09..68a8bf3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -56,6 +56,7 @@ extensions/cpsection/frame/Makefile
 extensions/cpsection/keyboard/Makefile
 extensions/cpsection/language/Makefile
 extensions/cpsection/modemconfiguration/Makefile
+extensions/cpsection/modemconfiguration/config.py
 extensions/cpsection/Makefile
 extensions/cpsection/network/Makefile
 extensions/cpsection/power/Makefile
diff --git a/extensions/cpsection/modemconfiguration/Makefile.am b/extensions/cpsection/modemconfiguration/Makefile.am
index 3e2613e..525e02e 100644
--- a/extensions/cpsection/modemconfiguration/Makefile.am
+++ b/extensions/cpsection/modemconfiguration/Makefile.am
@@ -4,3 +4,5 @@ sugar_PYTHON = 		\
 	__init__.py	\
 	model.py	\
 	view.py		
+
+nodist_sugar_PYTHON = config.py
diff --git a/extensions/cpsection/modemconfiguration/config.py.in b/extensions/cpsection/modemconfiguration/config.py.in
new file mode 100644
index 0000000..6fa688e
--- /dev/null
+++ b/extensions/cpsection/modemconfiguration/config.py.in
@@ -0,0 +1,20 @@
+# -*- encoding: utf-8 -*-
+# Copyright (C) 2010 Andrés Ambrois
+#
+# 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 2 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  US
+
+PROVIDERS_PATH = "@prefix@/share/mobile-broadband-provider-info/serviceproviders.xml"
+PROVIDERS_FORMAT_SUPPORTED = "2.0"
+COUNTRY_CODES_PATH = "@prefix@/share/zoneinfo/iso3166.tab"
diff --git a/extensions/cpsection/modemconfiguration/model.py b/extensions/cpsection/modemconfiguration/model.py
old mode 100755
new mode 100644
index 2545ce1..dbec47d
--- a/extensions/cpsection/modemconfiguration/model.py
+++ b/extensions/cpsection/modemconfiguration/model.py
@@ -15,11 +15,22 @@
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  US
 
 import gconf
+import gtk
+import os
+import locale
+import logging
+
+from xml.etree.cElementTree import ElementTree
+from gettext import gettext as _
 
 from jarabe.model.network import GSM_USERNAME_PATH, GSM_PASSWORD_PATH, \
                                  GSM_NUMBER_PATH, GSM_APN_PATH, GSM_PIN_PATH, \
                                  GSM_PUK_PATH
 
+from cpsection.modemconfiguration.config import PROVIDERS_PATH, \
+                                                PROVIDERS_FORMAT_SUPPORTED, \
+                                                COUNTRY_CODES_PATH
+
 def get_username():
     client = gconf.client_get_default()
     return client.get_string(GSM_USERNAME_PATH) or ''
@@ -68,3 +79,111 @@ def set_puk(puk):
     client = gconf.client_get_default()
     client.set_string(GSM_PUK_PATH, puk)
 
+def has_providers_db():
+    if not os.path.isfile(COUNTRY_CODES_PATH):
+        logging.debug("Mobile broadband provider database: Country " \
+                          "codes path %s not found.", COUNTRY_CODES_PATH)
+        return False
+    try:
+        tree = ElementTree(file=PROVIDERS_PATH)
+    except (IOError, SyntaxError), e:
+        logging.debug("Mobile broadband provider database: Could not read " \
+                          "provider information %s error=%s", PROVIDERS_PATH)
+        return False
+    else:
+        elem = tree.getroot()
+        if elem is None or elem.get('format') != PROVIDERS_FORMAT_SUPPORTED:
+            logging.debug("Mobile broadband provider database: Could not " \
+                          "read provider information. %s is wrong format.",
+                          elem.get('format'))
+            return False
+        return True
+
+
+class CountryListStore(gtk.ListStore):
+    COUNTRY_CODE = locale.getdefaultlocale()[0][3:5].lower()
+
+    def __init__(self):
+        gtk.ListStore.__init__(self, str, object)
+        codes = {}
+        with open(COUNTRY_CODES_PATH) as codes_file:
+            for line in codes_file:
+                if line.startswith('#'):
+                    continue
+                code, name = line.split('\t')[:2]
+                codes[code.lower()] = name.strip()
+        etree = ElementTree(file=PROVIDERS_PATH).getroot()
+        self._country_idx = None
+        i = 0
+        for elem in etree.findall('.//country'):
+            code = elem.attrib['code']
+            if code == self.COUNTRY_CODE:
+                self._country_idx = i
+            else:
+                i += 1
+            if code in codes:
+                self.append((codes[code], elem))
+            else:
+                self.append((code, elem))
+
+    def get_row_providers(self, row):
+        return self[row][1]
+
+    def guess_country_row(self):
+        if self._country_idx is not None:
+            return self._country_idx
+        else:
+            return -1
+
+class ProviderListStore(gtk.ListStore):
+    def __init__(self, elem):
+        gtk.ListStore.__init__(self, str, object)
+        for provider_elem in elem.findall('.//provider'):
+            apns = provider_elem.findall('.//apn')
+            if not apns:
+                # Skip carriers with CDMA entries only
+                continue
+            self.append((provider_elem.find('.//name').text, apns))
+
+    def get_row_plans(self, row):
+        return self[row][1]
+
+class PlanListStore(gtk.ListStore):
+    LANG_NS_ATTR = '{http://www.w3.org/XML/1998/namespace}lang'
+    LANG = locale.getdefaultlocale()[0][:2]
+    DEFAULT_NUMBER = '*99#'
+
+    def __init__(self, elems):
+        gtk.ListStore.__init__(self, str, object)
+        for apn_elem in elems:
+            plan = {}
+            names = apn_elem.findall('.//name')
+            if names:
+                for name in names:
+                    if name.get(self.LANG_NS_ATTR) is None:
+                        # serviceproviders.xml default value
+                        plan['name'] = name.text
+                    elif name.get(self.LANG_NS_ATTR) == self.LANG:
+                        # Great! We found a name value for our locale!
+                        plan['name'] = name.text
+                        break
+            else:
+                plan['name'] = _('Default')
+            plan['apn'] = apn_elem.get('value')
+            user = apn_elem.find('.//username')
+            if user is not None:
+                plan['username'] = user.text
+            else:
+                plan['username'] = ''
+            passwd = apn_elem.find('.//password')
+            if passwd is not None:
+                plan['password'] = passwd.text
+            else:
+                plan['password'] = ''
+
+            plan['number'] = self.DEFAULT_NUMBER
+
+            self.append((plan['name'], plan))
+
+    def get_row_plan(self, row):
+        return self[row][1]
diff --git a/extensions/cpsection/modemconfiguration/view.py b/extensions/cpsection/modemconfiguration/view.py
index b236f3f..3b03b3d 100644
--- a/extensions/cpsection/modemconfiguration/view.py
+++ b/extensions/cpsection/modemconfiguration/view.py
@@ -14,8 +14,6 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  US
 
-import os
-import logging
 from gettext import gettext as _
 
 import gtk
@@ -31,7 +29,7 @@ class EntryWithLabel(gtk.HBox):
     __gtype_name__ = "SugarEntryWithLabel"
 
     def __init__(self, label_text):
-        gtk.HBox.__init__(self, spacing=style.DEFAULT_SPACING)
+        gtk.HBox.__init__(self, spacing=style.DEFAULT_SPACING * 2)
 
         self._timeout_sid = 0
         self._changed_handler = None
@@ -44,11 +42,11 @@ class EntryWithLabel(gtk.HBox):
         self.pack_start(self.label, expand=False)
         self.label.show()
 
-        self._entry = gtk.Entry(25)
-        self._entry.connect('changed', self.__entry_changed_cb)
-        self._entry.set_width_chars(25)
-        self.pack_start(self._entry, expand=False)
-        self._entry.show()
+        self.entry = gtk.Entry(25)
+        self.entry.connect('changed', self.__entry_changed_cb)
+        self.entry.set_width_chars(25)
+        self.pack_start(self.entry, expand=False)
+        self.entry.show()
 
     def __entry_changed_cb(self, widget, data=None):
         if self._timeout_sid:
@@ -59,11 +57,11 @@ class EntryWithLabel(gtk.HBox):
     def __timeout_cb(self):
         self._timeout_sid = 0
 
-        if self._entry.get_text() == self.get_value():
+        if self.entry.get_text() == self.get_value():
             return False
 
         try:
-            self.set_value(self._entry.get_text()) 
+            self.set_value(self.entry.get_text())
         except ValueError:
             self._is_valid = False
         else:
@@ -74,18 +72,20 @@ class EntryWithLabel(gtk.HBox):
         return False
 
     def set_text_from_model(self):
-        self._entry.set_text(self.get_value()) 
+        self.entry.set_text(self.get_value())
 
     def get_value(self):
         raise NotImplementedError
 
-    def set_value(self):
+    def set_value(self, value):
         raise NotImplementedError    
 
     def _get_is_valid(self):
         return self._is_valid
+
     is_valid = gobject.property(type=bool, getter=_get_is_valid, default=True)
 
+
 class UsernameEntry(EntryWithLabel):
     def __init__(self, model):
         EntryWithLabel.__init__(self, _('Username:'))
@@ -97,6 +97,7 @@ class UsernameEntry(EntryWithLabel):
     def set_value(self, username):
         self._model.set_username(username)
 
+
 class PasswordEntry(EntryWithLabel):
     def __init__(self, model):
         EntryWithLabel.__init__(self, _('Password:'))
@@ -108,6 +109,7 @@ class PasswordEntry(EntryWithLabel):
     def set_value(self, password):
         self._model.set_password(password)
 
+
 class NumberEntry(EntryWithLabel):
     def __init__(self, model):
         EntryWithLabel.__init__(self, _('Number:'))
@@ -119,6 +121,7 @@ class NumberEntry(EntryWithLabel):
     def set_value(self, number):
         self._model.set_number(number)
 
+
 class ApnEntry(EntryWithLabel):
     def __init__(self, model):
         EntryWithLabel.__init__(self, _('Access Point Name (APN):'))
@@ -130,6 +133,7 @@ class ApnEntry(EntryWithLabel):
     def set_value(self, apn):
         self._model.set_apn(apn)
 
+
 class PinEntry(EntryWithLabel):
     def __init__(self, model):
         EntryWithLabel.__init__(self, _('Personal Identity Number (PIN):'))
@@ -141,6 +145,7 @@ class PinEntry(EntryWithLabel):
     def set_value(self, pin):
         self._model.set_pin(pin)
 
+
 class PukEntry(EntryWithLabel):
     def __init__(self, model):
         EntryWithLabel.__init__(self, _('Personal Unblocking Key (PUK):'))
@@ -160,61 +165,150 @@ class ModemConfiguration(SectionView):
         self._model = model
         self.restart_alerts = alerts
 
-        self.set_border_width(style.DEFAULT_SPACING)
         self.set_spacing(style.DEFAULT_SPACING)
-        self._group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
+
+        label_group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
+        combo_group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
+
+        scrolled_win = gtk.ScrolledWindow()
+        scrolled_win.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        scrolled_win.show()
+        self.add(scrolled_win)
+
+        main_box = gtk.VBox(spacing=style.DEFAULT_SPACING)
+        main_box.set_border_width(style.DEFAULT_SPACING)
+        main_box.show()
+        scrolled_win.add_with_viewport(main_box)
 
         explanation = _("You will need to provide the following " \
                             "information to set up a mobile " \
                             "broadband connection to a cellular "\
                             "(3G) network.")
         self._text = gtk.Label(explanation)
-        self._text.set_width_chars(100)
         self._text.set_line_wrap(True)
         self._text.set_alignment(0, 0)
-        self.pack_start(self._text, False)
+        main_box.pack_start(self._text, False)
         self._text.show()
 
+        if model.has_providers_db():
+            self._upper_box = gtk.VBox(spacing=style.DEFAULT_SPACING)
+            self._upper_box.set_border_width(style.DEFAULT_SPACING)
+            main_box.pack_start(self._upper_box, expand=False)
+            self._upper_box.show()
+
+
+            box = gtk.HBox(spacing=style.DEFAULT_SPACING * 2)
+            label = gtk.Label(_('Country:'))
+            label.set_alignment(1, 0.5)
+            label_group.add_widget(label)
+            box.pack_start(label, False)
+            label.show()
+            country_store = model.CountryListStore()
+            country_combo = gtk.ComboBox(country_store)
+            combo_group.add_widget(country_combo)
+            cell = gtk.CellRendererText()
+            cell.props.xalign = 0.5
+            country_combo.pack_start(cell)
+            country_combo.add_attribute(cell, 'text', 0)
+            country_combo.connect('changed', self.__country_selected_cb)
+            box.pack_start(country_combo, False)
+            country_combo.show()
+            self._upper_box.pack_start(box, False)
+            box.show()
+
+            box = gtk.HBox(spacing=style.DEFAULT_SPACING * 2)
+            label = gtk.Label(_('Provider:'))
+            label.set_alignment(1, 0.5)
+            label_group.add_widget(label)
+            box.pack_start(label, False)
+            label.show()
+            self._providers_combo = gtk.ComboBox()
+            combo_group.add_widget(self._providers_combo)
+            cell = gtk.CellRendererText()
+            cell.props.xalign = 0.5
+            self._providers_combo.pack_start(cell)
+            self._providers_combo.add_attribute(cell, 'text', 0)
+            self._providers_combo.connect('changed',
+                                          self.__provider_selected_cb)
+            box.pack_start(self._providers_combo, False)
+            self._providers_combo.show()
+            self._upper_box.pack_start(box, False)
+            box.show()
+
+            box = gtk.HBox(spacing=style.DEFAULT_SPACING*2)
+            label = gtk.Label(_('Plan:'))
+            label.set_alignment(1, 0.5)
+            label_group.add_widget(label)
+            box.pack_start(label, False)
+            label.show()
+            self._plan_combo = gtk.ComboBox()
+            combo_group.add_widget(self._plan_combo)
+            cell = gtk.CellRendererText()
+            cell.props.xalign = 0.5
+            self._plan_combo.pack_start(cell)
+            self._plan_combo.add_attribute(cell, 'text', 0)
+            self._plan_combo.connect('changed', self.__plan_selected_cb)
+            box.pack_start(self._plan_combo, False)
+            self._plan_combo.show()
+            self._upper_box.pack_start(box, False)
+            box.show()
+
+            country_combo.set_active(country_store.guess_country_row())
+
+            separator = gtk.HSeparator()
+            main_box.pack_start(separator, False)
+            separator.show()
+
+        self._lower_box = gtk.VBox(spacing=style.DEFAULT_SPACING)
+        self._lower_box.set_border_width(style.DEFAULT_SPACING)
+        main_box.pack_start(self._lower_box, expand=False)
+        self._lower_box.show()
+
         self._username_entry = UsernameEntry(model)
         self._username_entry.connect('notify::is-valid',
                                      self.__notify_is_valid_cb)
-        self._group.add_widget(self._username_entry.label)
-        self.pack_start(self._username_entry, expand=False)
+        label_group.add_widget(self._username_entry.label)
+        combo_group.add_widget(self._username_entry.entry)
+        self._lower_box.pack_start(self._username_entry, fill=False)
         self._username_entry.show()
 
         self._password_entry = PasswordEntry(model)
         self._password_entry.connect('notify::is-valid',
                                      self.__notify_is_valid_cb)
-        self._group.add_widget(self._password_entry.label)
-        self.pack_start(self._password_entry, expand=False)
+        label_group.add_widget(self._password_entry.label)
+        combo_group.add_widget(self._password_entry.entry)
+        self._lower_box.pack_start(self._password_entry, fill=False)
         self._password_entry.show()
 
         self._number_entry = NumberEntry(model)
         self._number_entry.connect('notify::is-valid',
                                    self.__notify_is_valid_cb)
-        self._group.add_widget(self._number_entry.label)
-        self.pack_start(self._number_entry, expand=False)
+        label_group.add_widget(self._number_entry.label)
+        combo_group.add_widget(self._number_entry.entry)
+        self._lower_box.pack_start(self._number_entry, fill=False)
         self._number_entry.show()
 
         self._apn_entry = ApnEntry(model)
         self._apn_entry.connect('notify::is-valid',
                                 self.__notify_is_valid_cb)
-        self._group.add_widget(self._apn_entry.label)
-        self.pack_start(self._apn_entry, expand=False)
+        label_group.add_widget(self._apn_entry.label)
+        combo_group.add_widget(self._apn_entry.entry)
+        self._lower_box.pack_start(self._apn_entry, fill=False)
         self._apn_entry.show()
 
         self._pin_entry = PinEntry(model)
         self._pin_entry.connect('notify::is-valid',
                                 self.__notify_is_valid_cb)
-        self._group.add_widget(self._pin_entry.label)
-        self.pack_start(self._pin_entry, expand=False)
+        label_group.add_widget(self._pin_entry.label)
+        self._lower_box.pack_start(self._pin_entry, fill=False)
         self._pin_entry.show()
         
         self._puk_entry = PukEntry(model)
         self._puk_entry.connect('notify::is-valid',
                                 self.__notify_is_valid_cb)
-        self._group.add_widget(self._puk_entry.label)
-        self.pack_start(self._puk_entry, expand=False)        
+        label_group.add_widget(self._puk_entry.label)
+        combo_group.add_widget(self._puk_entry.entry)
+        self._lower_box.pack_start(self._puk_entry, fill=False)
         self._puk_entry.show()
 
         self.setup()
@@ -232,14 +326,37 @@ class ModemConfiguration(SectionView):
     def undo(self):
         self._model.undo()
 
+    def __country_selected_cb(self, combo):
+        model = combo.get_model()
+        providers = model.get_row_providers(combo.get_active())
+        self._providers_combo.set_model(
+            self._model.ProviderListStore(providers))
+
+    def __provider_selected_cb(self, combo):
+        model = combo.get_model()
+        plans = model.get_row_plans(combo.get_active())
+        self._plan_combo.set_model(self._model.PlanListStore(plans))
+
+    def __plan_selected_cb(self, combo):
+        model = combo.get_model()
+        plan = model.get_row_plan(combo.get_active())
+        self._username_entry.set_value(plan['username'])
+        self._username_entry.set_text_from_model()
+        self._password_entry.set_value(plan['password'])
+        self._password_entry.set_text_from_model()
+        self._number_entry.set_value(plan['number'])
+        self._number_entry.set_text_from_model()
+        self._apn_entry.set_value(plan['apn'])
+        self._apn_entry.set_text_from_model()
+
     def _validate(self):
         if self._username_entry.is_valid and \
-            self._password_entry.is_valid and \
+                self._password_entry.is_valid and \
                 self._number_entry.is_valid and \
-                    self._apn_entry.is_valid and \
-                        self._pin_entry.is_valid and \
-                            self._puk_entry.is_valid:
-                                self.props.is_valid = True
+                self._apn_entry.is_valid and \
+                self._pin_entry.is_valid and \
+                self._puk_entry.is_valid:
+            self.props.is_valid = True
         else:
             self.props.is_valid = False
 
@@ -247,4 +364,3 @@ class ModemConfiguration(SectionView):
         if entry.is_valid:
             self.needs_restart = True
         self._validate()
-
-- 
1.7.1



More information about the Dextrose mailing list