Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAjay Garg <ajay@activitycentral.com>2012-10-12 13:04:37 (GMT)
committer Ajay Garg <ajay@activitycentral.com>2012-10-15 15:47:06 (GMT)
commitfb5307b786cb628fdddfb5742cdf34146119a313 (patch)
treec42abda7372626138a705cc6e42848069f7a238f
parentb16993db1deaf85793dd950159c4379067277992 (diff)
3G-Database Support.
Signed-off-by: Ajay Garg <ajay@activitycentral.com>
-rw-r--r--extensions/cpsection/modemconfiguration/Makefile.am1
-rw-r--r--extensions/cpsection/modemconfiguration/config.py25
-rwxr-xr-xextensions/cpsection/modemconfiguration/model.py187
-rw-r--r--extensions/cpsection/modemconfiguration/view.py198
-rw-r--r--src/jarabe/controlpanel/gui.py4
5 files changed, 384 insertions, 31 deletions
diff --git a/extensions/cpsection/modemconfiguration/Makefile.am b/extensions/cpsection/modemconfiguration/Makefile.am
index 3e2613e..46f8e70 100644
--- a/extensions/cpsection/modemconfiguration/Makefile.am
+++ b/extensions/cpsection/modemconfiguration/Makefile.am
@@ -3,4 +3,5 @@ sugardir = $(pkgdatadir)/extensions/cpsection/modemconfiguration
sugar_PYTHON = \
__init__.py \
model.py \
+ config.py \
view.py
diff --git a/extensions/cpsection/modemconfiguration/config.py b/extensions/cpsection/modemconfiguration/config.py
new file mode 100644
index 0000000..9e27814
--- /dev/null
+++ b/extensions/cpsection/modemconfiguration/config.py
@@ -0,0 +1,25 @@
+# -*- encoding: utf-8 -*-
+# Copyright (C) 2010 Andres 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 = "/usr/share/mobile-broadband-provider-info/serviceproviders.xml"
+PROVIDERS_FORMAT_SUPPORTED = "2.0"
+COUNTRY_CODES_PATH = "/usr/share/zoneinfo/iso3166.tab"
+
+GSM_COUNTRY_PATH = '/desktop/sugar/network/gsm/country'
+GSM_PROVIDERS_PATH = '/desktop/sugar/network/gsm/providers'
+GSM_PLAN_PATH = '/desktop/sugar/network/gsm/plan'
diff --git a/extensions/cpsection/modemconfiguration/model.py b/extensions/cpsection/modemconfiguration/model.py
index f457293..e33d881 100755
--- a/extensions/cpsection/modemconfiguration/model.py
+++ b/extensions/cpsection/modemconfiguration/model.py
@@ -19,14 +19,27 @@ import logging
import dbus
from gi.repository import Gtk
+import os
+import locale
+import logging
+import gconf
+
+from xml.etree.cElementTree import ElementTree
+from gettext import gettext as _
+
from jarabe.model import network
+from cpsection.modemconfiguration.config import PROVIDERS_PATH, \
+ PROVIDERS_FORMAT_SUPPORTED, \
+ COUNTRY_CODES_PATH
+
+
def get_connection():
return network.find_gsm_connection()
-def get_modem_settings():
+def get_modem_settings(callback):
modem_settings = {}
connection = get_connection()
if not connection:
@@ -48,6 +61,10 @@ def get_modem_settings():
modem_settings['password'] = gsm_secrets.get('password', '')
modem_settings['pin'] = gsm_secrets.get('pin', '')
+ # sl#3800: We return the settings, via the "_secrets_cb()
+ # method", instead of busy-waiting.
+ callback(modem_settings)
+
def _secrets_err_cb(err):
secrets_call_done[0] = True
if isinstance(err, dbus.exceptions.DBusException) and \
@@ -57,14 +74,11 @@ def get_modem_settings():
logging.error('Error retrieving GSM secrets: %s', err)
# must be called asynchronously as this re-enters the GTK main loop
+ #
+ # sl#3800: We return the settings, via the "_secrets_cb()" method,
+ # instead of busy-waiting.
connection.get_secrets('gsm', _secrets_cb, _secrets_err_cb)
- # wait til asynchronous execution completes
- while not secrets_call_done[0]:
- Gtk.main_iteration()
-
- return modem_settings
-
def _set_or_clear(_dict, key, value):
"""Update a dictionary value for a specific key. If value is None or
@@ -98,3 +112,162 @@ def set_modem_settings(modem_settings):
_set_or_clear(gsm_settings, 'apn', apn)
_set_or_clear(gsm_settings, 'pin', pin)
connection.update_settings(settings)
+
+
+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
+
+ # This dictionary wil store the values, with "country-name" as
+ # the key, and "country-code" as the value.
+ temp_dict = {}
+
+ 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:
+ temp_dict[codes[code]] = elem
+ else:
+ temp_dict[code] = elem
+
+ # Now, sort the list by country-names.
+ country_name_keys = temp_dict.keys()
+ country_name_keys.sort()
+
+ for country_name in country_name_keys:
+ self.append((country_name, temp_dict[country_name]))
+
+ 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 0
+
+ def search_index_by_code(self, code):
+ for index in range(0, len(self)):
+ if self[index][0] == code:
+ return index
+ 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]
+
+ def guess_providers_row(self):
+ # Simply return the first entry as the default.
+ return 0
+
+ def search_index_by_code(self, code):
+ for index in range(0, len(self)):
+ if self[index][0] == code:
+ return index
+ return -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]
+
+ def guess_plan_row(self):
+ # Simply return the first entry as the default.
+ return 0
+
+ def search_index_by_code(self, code):
+ for index in range(0, len(self)):
+ if self[index][0] == code:
+ return index
+ return -1
+
+
+def get_gconf_setting_string(gconf_key):
+ client = gconf.client_get_default()
+ return client.get_string(gconf_key) or ''
+
+def set_gconf_setting_string(gconf_key, gconf_setting_string_value):
+ client = gconf.client_get_default()
+ client.set_string(gconf_key, gconf_setting_string_value)
diff --git a/extensions/cpsection/modemconfiguration/view.py b/extensions/cpsection/modemconfiguration/view.py
index d5aa399..d218330 100644
--- a/extensions/cpsection/modemconfiguration/view.py
+++ b/extensions/cpsection/modemconfiguration/view.py
@@ -24,6 +24,10 @@ from sugar3.graphics import style
from jarabe.controlpanel.sectionview import SectionView
+from cpsection.modemconfiguration.config import GSM_COUNTRY_PATH, \
+ GSM_PROVIDERS_PATH, \
+ GSM_PLAN_PATH
+
APPLY_TIMEOUT = 1000
@@ -64,6 +68,17 @@ class ModemConfiguration(SectionView):
self.set_border_width(style.DEFAULT_SPACING)
self.set_spacing(style.DEFAULT_SPACING)
self._group = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
+ self._combo_group = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
+
+ scrolled_win = Gtk.ScrolledWindow()
+ scrolled_win.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.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'
@@ -71,41 +86,85 @@ class ModemConfiguration(SectionView):
self._text = Gtk.Label(label=explanation)
self._text.set_line_wrap(True)
self._text.set_alignment(0, 0)
- self.pack_start(self._text, False, False, 0)
+ main_box.pack_start(self._text, False, False, 0)
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, True, True, 0)
+ self._upper_box.show()
+
+ # Do not attach any 'change'-handlers for now.
+ # They will be attached (once per combobox), once the
+ # individual combobox is processed at startup.
+ self._country_store = model.CountryListStore()
+ self._country_combo = Gtk.ComboBox(model=self._country_store)
+ self._attach_combobox_widget(_('Country:'),
+ self._country_combo)
+
+ self._providers_combo = Gtk.ComboBox()
+ self._attach_combobox_widget(_('Provider:'),
+ self._providers_combo)
+
+ self._plan_combo = Gtk.ComboBox()
+ self._attach_combobox_widget(_('Plan:'),
+ self._plan_combo)
+
+ separator = Gtk.HSeparator()
+ main_box.pack_start(separator, True, True, 0)
+ 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, True, True, 0)
+ self._lower_box.show()
+
self._username_entry = EntryWithLabel(_('Username:'))
- self._username_entry.entry.connect('changed', self.__entry_changed_cb)
- self._group.add_widget(self._username_entry.label)
- self.pack_start(self._username_entry, False, True, 0)
- self._username_entry.show()
+ self._attach_entry_widget(self._username_entry)
self._password_entry = EntryWithLabel(_('Password:'))
- self._password_entry.entry.connect('changed', self.__entry_changed_cb)
- self._group.add_widget(self._password_entry.label)
- self.pack_start(self._password_entry, False, True, 0)
- self._password_entry.show()
+ self._attach_entry_widget(self._password_entry)
self._number_entry = EntryWithLabel(_('Number:'))
- self._number_entry.entry.connect('changed', self.__entry_changed_cb)
- self._group.add_widget(self._number_entry.label)
- self.pack_start(self._number_entry, False, True, 0)
- self._number_entry.show()
+ self._attach_entry_widget(self._number_entry)
self._apn_entry = EntryWithLabel(_('Access Point Name (APN):'))
- self._apn_entry.entry.connect('changed', self.__entry_changed_cb)
- self._group.add_widget(self._apn_entry.label)
- self.pack_start(self._apn_entry, False, True, 0)
- self._apn_entry.show()
+ self._attach_entry_widget(self._apn_entry)
self._pin_entry = EntryWithLabel(_('Personal Identity Number (PIN):'))
- self._pin_entry.entry.connect('changed', self.__entry_changed_cb)
- self._group.add_widget(self._pin_entry.label)
- self.pack_start(self._pin_entry, False, True, 0)
- self._pin_entry.show()
+ self._attach_entry_widget(self._pin_entry)
self.setup()
+ def _attach_combobox_widget(self, label_text, combobox_obj):
+ box = Gtk.HBox(spacing=style.DEFAULT_SPACING)
+ label = Gtk.Label(label_text)
+ self._group.add_widget(label)
+ label.set_alignment(1, 0.5)
+ box.pack_start(label, False, False, 0)
+ label.show()
+
+ self._combo_group.add_widget(combobox_obj)
+ cell = Gtk.CellRendererText()
+ cell.props.xalign = 0.5
+
+ cell.set_property('width-chars', 30)
+
+ combobox_obj.pack_start(cell, True)
+ combobox_obj.add_attribute(cell, 'text', 0)
+
+ box.pack_start(combobox_obj, False, False, 0)
+ combobox_obj.show()
+ self._upper_box.pack_start(box, False, False, 0)
+ box.show()
+
+ def _attach_entry_widget(self, entry_with_label_obj):
+ entry_with_label_obj.entry.connect('changed', self.__entry_changed_cb)
+ self._group.add_widget(entry_with_label_obj.label)
+ self._lower_box.pack_start(entry_with_label_obj, True, True, 0)
+ entry_with_label_obj.show()
+
def undo(self):
self._model.undo()
@@ -113,12 +172,31 @@ class ModemConfiguration(SectionView):
"""Populate an entry with text, without triggering its 'changed'
handler."""
entry = entrywithlabel.entry
- entry.handler_block_by_func(self.__entry_changed_cb)
+
+ # Do not block/unblock the callback functions.
+ #
+ # Thus, the savings will be persisted to the NM settings,
+ # whenever any setting on the UI changes (by user-intervention,
+ # or otherwise).
+ #entry.handler_block_by_func(self.__entry_changed_cb)
entry.set_text(text)
- entry.handler_unblock_by_func(self.__entry_changed_cb)
+ #entry.handler_unblock_by_func(self.__entry_changed_cb)
def setup(self):
- settings = self._model.get_modem_settings()
+ if self._model.has_providers_db():
+ persisted_country = self._model.get_gconf_setting_string(GSM_COUNTRY_PATH)
+ if (self._model.has_providers_db()) and (persisted_country != ''):
+ self._country_combo.set_active(self._country_store.search_index_by_code(persisted_country))
+ else:
+ self._country_combo.set_active(self._country_store.guess_country_row())
+
+ # Call the selected callback anyway, so as to chain-set the
+ # default values for providers and the plans.
+ self.__country_selected_cb(self._country_combo, setup=True)
+
+ self._model.get_modem_settings(self.populate_entries)
+
+ def populate_entries(self, settings):
self._populate_entry(self._username_entry,
settings.get('username', ''))
self._populate_entry(self._number_entry, settings.get('number', ''))
@@ -133,6 +211,78 @@ class ModemConfiguration(SectionView):
self._timeout_sid = GObject.timeout_add(APPLY_TIMEOUT,
self.__timeout_cb)
+ def _get_selected_text(self, combo):
+ active_iter = combo.get_active_iter()
+ return combo.get_model().get(active_iter, 0)[0]
+
+ def __country_selected_cb(self, combo, setup=False):
+ country = self._get_selected_text(combo)
+ self._model.set_gconf_setting_string(GSM_COUNTRY_PATH, country)
+
+ model = combo.get_model()
+ providers = model.get_row_providers(combo.get_active())
+ self._providers_liststore = self._model.ProviderListStore(providers)
+ self._providers_combo.set_model(self._providers_liststore)
+
+ # Set the default provider as well.
+ if setup:
+ persisted_provider = self._model.get_gconf_setting_string(GSM_PROVIDERS_PATH)
+ if persisted_provider == '':
+ self._providers_combo.set_active(self._providers_liststore.guess_providers_row())
+ else:
+ self._providers_combo.set_active(self._providers_liststore.search_index_by_code(persisted_provider))
+ else:
+ self._providers_combo.set_active(self._providers_liststore.guess_providers_row())
+
+ # Country-combobox processed once at startip; now, attach the
+ # change-handler.
+ self._country_combo.connect('changed', self.__country_selected_cb, False)
+
+ # Call the callback, so that default provider may be set.
+ self.__provider_selected_cb(self._providers_combo, setup)
+
+ def __provider_selected_cb(self, combo, setup=False):
+ provider = self._get_selected_text(combo)
+ self._model.set_gconf_setting_string(GSM_PROVIDERS_PATH, provider)
+
+ model = combo.get_model()
+ plans = model.get_row_plans(combo.get_active())
+ self._plan_liststore = self._model.PlanListStore(plans)
+ self._plan_combo.set_model(self._plan_liststore)
+
+ # Set the default plan as well.
+ if setup:
+ persisted_plan = self._model.get_gconf_setting_string(GSM_PLAN_PATH)
+ if persisted_plan == '':
+ self._plan_combo.set_active(self._plan_liststore.guess_plan_row())
+ else:
+ self._plan_combo.set_active(self._plan_liststore.search_index_by_code(persisted_plan))
+ else:
+ self._plan_combo.set_active(self._plan_liststore.guess_plan_row())
+
+ # Providers-combobox processed once at startip; now, attach the
+ # change-handler.
+ self._providers_combo.connect('changed', self.__provider_selected_cb, False)
+
+ # Call the callback, so that the default plan is set.
+ self.__plan_selected_cb(self._plan_combo, setup)
+
+ def __plan_selected_cb(self, combo, setup=False):
+ plan = self._get_selected_text(combo)
+ self._model.set_gconf_setting_string(GSM_PLAN_PATH, plan)
+
+ # Plan-combobox processed once at startip; now, attach the
+ # change-handler.
+ self._plan_combo.connect('changed', self.__plan_selected_cb, False)
+
+ model = combo.get_model()
+ plan = model.get_row_plan(combo.get_active())
+
+ self._populate_entry(self._username_entry, plan['username'])
+ self._populate_entry(self._password_entry, plan['password'])
+ self._populate_entry(self._apn_entry, plan['apn'])
+ self._populate_entry(self._number_entry, plan['number'])
+
def __timeout_cb(self):
self._timeout_sid = 0
settings = {}
diff --git a/src/jarabe/controlpanel/gui.py b/src/jarabe/controlpanel/gui.py
index f8afca3..1e8769c 100644
--- a/src/jarabe/controlpanel/gui.py
+++ b/src/jarabe/controlpanel/gui.py
@@ -208,6 +208,10 @@ class ControlPanel(Gtk.Window):
self.__accept_clicked_cb)
def show_section_view(self, option):
+ self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
+ GObject.idle_add(self._finally_show_section_view, option)
+
+ def _finally_show_section_view(self, option):
self._set_toolbar(self._section_toolbar)
icon = self._section_toolbar.get_icon()