diff options
author | Ajay Garg <ajay@activitycentral.com> | 2012-10-12 13:04:37 (GMT) |
---|---|---|
committer | Ajay Garg <ajay@activitycentral.com> | 2012-10-15 15:47:06 (GMT) |
commit | fb5307b786cb628fdddfb5742cdf34146119a313 (patch) | |
tree | c42abda7372626138a705cc6e42848069f7a238f | |
parent | b16993db1deaf85793dd950159c4379067277992 (diff) |
3G-Database Support.
Signed-off-by: Ajay Garg <ajay@activitycentral.com>
-rw-r--r-- | extensions/cpsection/modemconfiguration/Makefile.am | 1 | ||||
-rw-r--r-- | extensions/cpsection/modemconfiguration/config.py | 25 | ||||
-rwxr-xr-x | extensions/cpsection/modemconfiguration/model.py | 187 | ||||
-rw-r--r-- | extensions/cpsection/modemconfiguration/view.py | 198 | ||||
-rw-r--r-- | src/jarabe/controlpanel/gui.py | 4 |
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() |