#!/usr/bin/env python import copy import sys import math import dbus import os import subprocess import ConfigParser from dbus.mainloop.glib import DBusGMainLoop import pygtk pygtk.require("2.0") import gtk import gtk.glade import gobject import pango import gettext _ = gettext.gettext class GtkAlerter: def __getitem__(self, key): return self.widgets.get_widget(key) def __init__(self, gladefile, ag): self.widgets = gtk.glade.XML(gladefile) dic = { "CallAlertAccept_clicked_cb" : self.action_accept_clicked, "CallAlertAcceptHold_clicked_cb" : self.action_accept_hold_clicked, "CallAlertAcceptDrop_clicked_cb" : self.action_accept_drop_clicked, "CallAlertReject_clicked_cb" : self.action_reject_clicked, "CallAlertBusy_clicked_cb" : self.action_busy_clicked, "CallAlertIgnore_clicked_cb" : self.action_ignore_clicked, } self.widgets.signal_autoconnect(dic) self.ag = ag self.completed = False self.removed = False if ag.can_drop_waiting(): self['CallAlertReject'].show() else: self['CallAlertReject'].hide() # The UDUB feature seems to be implemented very # inconsistently across phones and handset makers. # Presenting it to the user is almost silly. if False and ag.can_drop_held_udub(): self['CallAlertBusy'].show() else: self['CallAlertBusy'].hide() self['CallAlertIgnore'].show() self.update_state() self['CallAlertDialog'].show_all() def update_state(self): callstate = self.ag.callstate (callid, callphbook) = self.ag.waiting_caller_info() if callid: if callphbook: self['CallAlertInfoLabel'].set_text( "From: %s (%s)" % (callphbook, callid)) else: self['CallAlertInfoLabel'].set_text( "From: %s" % callid) else: self['CallAlertInfoLabel'].set_text('') if callstate == 5: self['CallAlertAccept'].hide() else: self['CallAlertAcceptHold'].hide() self['CallAlertAcceptDrop'].hide() self['CallAlertReject'].hide() if callstate == 5: self['CallAlertAcceptHold'].show() self['CallAlertAcceptDrop'].show() else: self['CallAlertAccept'].show() if self.ag.can_drop_waiting(): self['CallAlertReject'].show() def remove(self): self.complete() del self.widgets del self.ag self.removed = True def complete(self): if not self.completed: self.completed = True self['CallAlertDialog'].hide() def action_accept_clicked(self, widget): if not self.removed: self.complete() self.ag.answer() def action_accept_hold_clicked(self, widget): if not self.removed: self.complete() self.ag.swap_hold_active() def action_accept_drop_clicked(self, widget): if not self.removed: self.complete() self.ag.swap_drop_active() def action_reject_clicked(self, widget): if not self.removed: self.complete() self.ag.hangup() def action_busy_clicked(self, widget): if not self.removed: self.complete() self.ag.drop_held_udub() def action_ignore_clicked(self, widget): if not self.removed: self.complete() # Telephone number processing is a serious pain in the rear. # In order to be able to compare telephone numbers, we must convert them # to a normalized form, which will be international form. class TelNormalizer: """A hacky telephone number normalizer""" def __init__(self): pass def _isnum(self, c): x = ord(c) return (x >= ord('0') and x <= ord('9')) def _isalpha(self, c): x = ord(c) return ((x >= ord('a') and x <= ord('z')) or (x >= ord('A') and x <= ord('Z'))) def _alpha_convert(self, c): x = ord(c) if x >= ord('a') and x <= ord('z'): c = c.upper() x = ord(c) if x >= ord('A') and x <= ord('C'): c = '2' elif x >= ord('D') and x <= ord('F'): c = '3' elif x >= ord('G') and x <= ord('I'): c = '4' elif x >= ord('J') and x <= ord('L'): c = '5' elif x >= ord('M') and x <= ord('O'): c = '6' elif x >= ord('P') and x <= ord('S'): c = '7' elif x >= ord('T') and x <= ord('V'): c = '8' elif x >= ord('W') and x <= ord('Z'): c = '9' return c def _simplify(self, tel): new = '' if not tel: return new if tel[0] == '+': new = '+' tel = tel[1:] for c in tel: if self._isnum(c) or c == '#' or c == '*': new = new + c elif self._isalpha(c): new = new + self._alpha_convert(c) elif (self._iswsp(c) or c == '-' or c == '.' or c == '(' or c == ')' or c == '~' or c == '/'): continue else: print "Odd character \"%s\" in tel" % c return new def normalize(self, tel, teltype): return self._simplify(tel) def compare(self, tel1, tel2): # FIXME: this is a horrible hack! if ((tel1[0] == '+' and tel2[0] == '+') or (tel1[0] != '+' and tel2[0] != '+')): return tel1 == tel2 if tel2[0] == '+': tmp = tel1 tel1 = tel2 tel2 = tmp if len(tel1) < len(tel2): return False start = len(tel1) - len(tel2) return tel1[start:] == tel2 # This is really _really_ stupid # If there was a vCard parser that was part of the standard # Python distribution, it would be used here. But there isn't # and the job isn't very complicated. class VCard: """A hacky vCard representation""" def __init__(self, fillbasic): self.vtype = 'vcard' self.attribs = [] if fillbasic: self.attribs.append(['version', None, None, '3.0']) self.attribs.append(['n', None, None, ';;;;']) self.attribs.append(['fn', None, None, '']) def findattrs(self, name): opts = [] for x in self.attribs: if x[0] == name: opts.append(x) return opts def findtype(self, attrs, xtype): opts = [] for x in attrs: if not xtype: opts.append(x) elif 'type' in x[2]: types = x[2]['type'] for y in types: if y == xtype: opts.append(x) return opts def findtels(self, teltype): return self.findtype(self.findattrs('tel'), teltype) def getuid(self): opts = self.findattrs('uid') if opts: return opts[0][3] return None def getname(self): opts = self.findattrs('name') if not opts: opts = self.findattrs('fn') opt = opts[0] # fixme: decode the name! return opt[3] def setname(self, name): opts = self.findattrs('name') if not opts: opts = self.findattrs('fn') opt = opts[0] # fixme: encode the name! opt[3] = name def gettel(self, teltype): opts = self.findtels(teltype) if opts: return opts[0][3] return '' def settel(self, teltype, num): opts = self.findtels(teltype) if opts: opts[0][3] = num else: self.attribs.append( ['tel', None, {'type': teltype}, num]) def remove(self): if hasattr(self, '_container'): self._container.remove(self) def merge(self, object): # TODO: merge changes to attributes print "Merge entry %s" % self.getuid() def _parse_error(self, message): raise Exception('VCARD object: %s' % message) def _parse_case_insensitive_param(self, name): return (name == 'type' or name == 'charset' or name == 'encoding') def _parse_case_list(self, list): out = [] for x in list: out.append(x.lower()) return out def _parse_add_content(self, name, groups, params, value): self.attribs.append([name, groups, params, value]) #print 'Name:%s groups:%s Value:%s' % (name, groups, value) for x in params: if self._parse_case_insensitive_param(x): params[x] = self._parse_case_list(params[x]) #print '\tParam: %s Value:%s' % (x, params[x]) def _parse_get_single(self, attr, reqd): al = self.findattrs(attr) v = None if not len(al): if not reqd: return None self._parse_error(_('Missing \"%s\" attribute') % attr) elif len(al) > 1: self._parse_error(_('Multiple \"%s\" attributes') % attr) else: v = al[0][3] return v def _parse_end(self): v = self._parse_get_single('n', True) v = self._parse_get_single('fn', True) v = self._parse_get_single('version', True) if v != '2.1' and v != '3.0': self._parse_error(_('Unrecognized version \'%s\'') % v) v = self._parse_get_single('uid', False) class VCardSet: """A hacky vCard container/manager object""" # This module implements a parser for vCard 2.1 and 3.0. # It does not parse each format strictly, as there are low-level # differences in the following areas: # - Whitespace is permitted between content tokens in 2.1, not 3.0 # - All parameters must be explicitly named in 3.0, not in 2.1 # # Input that claims to be of one standard but does not strictly # comply with that standard is silently accepted. def __init__(self): self._file_line = 0 self._save_line = None self._current_object = None self._objects = [] self._uids = {} self._object_list = gtk.ListStore(gobject.TYPE_PYOBJECT, gobject.TYPE_STRING) self.normalizer = TelNormalizer() def _isalpha(self, c): x = ord(c) return ((x >= ord('a') and x <= ord('z')) or (x >= ord('A') and x <= ord('Z'))) def _isnum(self, c): x = ord(c) return (x >= ord('0') and x <= ord('9')) def _iswsp(self, c): return c == ' ' or c == '\t' def _issafe(self, c): x = ord(c) return (self._iswsp(c) or x == 0x21 or (x >= 0x23 and x <= 0x2b) or (x >= 0x2d and x <= 0x39) or (x >= 0x3c and x <= 0x7e) or x > 0x7f) def _isqsafe(self, c): x = ord(c) return (_iswsp(c) or x == 0x21 or (x >= 0x23 and x <= 0x7e) or x > 0x7f) def _create_object(self, vtype): if vtype == 'vcard': return VCard(False) else: self._parse_error(_('Unknown value type %s') % vtype) def _add_object(self, object): if not hasattr(object, '_container'): uid = object.getuid() if uid and uid in self._uids: mergeit = self._uids[uid] mergeit.merge(object) else: object._container = self self._objects.append(object) self._uids[uid] = object iterx = self._object_list.append( row = [object, object.getname()]) object._iter = iterx def _remove_object(self, object): if hasattr(object, '_container'): i = 0 while i < len(self._objects): if self._objects[i] == object: del self._objects[i] else: i += 1 uid = object.getuid() if uid: del self._uids[uid] del object._container self._object_list.remove(object._iter) del object._iter def _process_line(self, name, groups, params, value): if name == 'begin': if self._current_object: self._parse_error(_('Nested directory object')) else: self._current_object = ( self._create_object(value.lower())) elif name == 'end': if not self._current_object: self._parse_error(_('Mismatched directory ' 'object END')) elif self._current_object.vtype != value.lower(): self._parse_error(_('Mismatched directory ' 'object type for END')) else: self._current_object._parse_end() self._add_object(self._current_object) self._current_object = None elif not self._current_object: self._parse_error(_('Content line outside ' 'directory object')) else: self._current_object._parse_add_content(name, groups, params, value) def _parse_error(self, message): raise Exception(_('Line %d: %s') % (self._file_line, message)) def _parse_skip_ws(self, line): pos = 0 while pos < len(line) and self._iswsp(line[pos]): pos += 1 return line[pos:] def _parse_name_groups(self, line): groups = [] name = None sep = 0 while True: if len(line) > sep: x = line[sep] if (self._isalpha(x) or self._isnum(x) or x == '-'): sep += 1 continue elif x == '.': if not sep: self._parse_error( _('Empty group definition')) groups.append(line[:sep]) line = line[(sep + 1):] sep = 0 continue if not sep: self._parse_error(_("Empty content name")) name = line[:sep] line = line[sep:] return (groups, name.lower(), line) def _parse_param_value(self, line): sep = 0 if line[0] == '"': line = line[1:] while True: if len(line) <= sep: self._parse_error( _("Malformed quoted " "parameter value")) x = line[sep] if self._isqsafe(x): sep += 1 continue elif x == '"': break else: self._parse_error( _("Malformed quoted " "parameter value")) value = '' if sep: value = line[:sep] line = line[(sep + 1):] return (value, line) while True: if len(line) > sep: x = line[sep] if self._issafe(x): sep += 1 continue break value = '' if sep: value = line[:sep] line = line[sep:] return (value, line) def _parse_param(self, line): name = None values = [] sep = 0 while True: if len(line) <= sep: self._parse_error(_("Malformed parameter")) x = line[sep] if self._isalpha(x) or self._isnum(x) or x == '-': sep += 1 else: if not sep: self._parse_error( _("Empty parameter name")) name = line[:sep] # vCard 2.1 hack line = self._parse_skip_ws(line[sep:]) if x == '=': # vCard 2.1 hack line = self._parse_skip_ws(line[1:]) break # vCard 2.1 hack # Property names without '=' are equivalent # to type=name values.append(name) return ('type', values, line) name = name.lower() while True: (x, line) = self._parse_param_value(line) values.append(x) if not len(line): return (name, values, line) # vCard 2.1 hack line = self._parse_skip_ws(line) x = line[0] if x != ',': return (name, values, line) line = line[1:] def _parse_line(self, line): (groups, name, line) = self._parse_name_groups(line) params = {} if not line: self._parse_error(_('Malformed content line')) # vCard 2.1 hack line = self._parse_skip_ws(line) while line[0] == ';': (param, values, line) = self._parse_param(line[1:]) if param in params: params[param].extend(values) else: params[param] = values # vCard 2.1 hack line = self._parse_skip_ws(line) if line[0] != ':': self._parse_error(_('Malformed content line')) # vCard 2.1 hack line = self._parse_skip_ws(line[1:]) self._process_line(name, groups, params, line) def _feed_line(self, line): if not line or not self._iswsp(line[0]): if self._save_line: self._parse_line(self._save_line) self._save_line = line elif self._save_line: self._save_line += line[1:] else: self._save_line = line[1:] self._file_line += 1 def _feed_done(self): self._feed_line(None) if self._current_object: self._parse_error(_('Unterminated object in stream')) def import_string(self, str): self._file_line = 0 line = '' for x in str.splitlines(): self._feed_line(x) self._feed_done() def import_file(self, path): f = open(os.path.expanduser(path), 'r') contents = f.read() f.close() self.import_string(contents) def search_tel(self, num_norm): print "Search for %s" % num_norm if not num_norm: return (None, None) for vc in self._objects: if vc.vtype != 'vcard': continue nums = vc.findtels(None) for ent in nums: xnum = self.normalizer.normalize(ent[3], 129) print "Check: %s" % xnum if self.normalizer.compare(num_norm, xnum): types = None if 'type' in ent[2]: types = ent[2]['type'] return (vc, types) return (None, None) def get_gtk_model(self): return self._object_list class HfConsole: """This is a user interface for the hfpd Bluetooth hands-free profile server""" DBUS_SERVICE_NAME = 'org.freedesktop.DBus' DBUS_INTERFACE_DBUS = 'org.freedesktop.DBus' DBUS_INTERFACE_PROPERTIES = 'org.freedesktop.DBus.Properties' DBUS_BUS_OBJECT = '/org/freedesktop/DBus' HFPD_HANDSFREE_INTERFACE_NAME = 'net.sf.nohands.hfpd.HandsFree' HFPD_SOUNDIO_INTERFACE_NAME = 'net.sf.nohands.hfpd.SoundIo' HFPD_AUDIOGATEWAY_INTERFACE_NAME = 'net.sf.nohands.hfpd.AudioGateway' HFPD_SERVICE_NAME = 'net.sf.nohands.hfpd' HFPD_HANDSFREE_OBJECT = '/net/sf/nohands/hfpd' HFPD_SOUNDIO_OBJECT = '/net/sf/nohands/hfpd/soundio' def addr_transform(self, addr): val = (str(addr[0:2]) + str(addr[3:5]) + str(addr[6:8]) + str(addr[9:11]) + str(addr[12:14]) + str(addr[15:17])) return val.upper() def addr_untransform(self, addr): val = (addr[0:2] + ':' + addr[2:4] + ':' + addr[4:6] + ':' + addr[6:8] + ':' + addr[8:10] + ':' + addr[10:12]) return val.upper() def create_cellrenderertext(self): cell = gtk.CellRendererText() cell.set_property('scale-set', True) cell.set_property('scale', pango.SCALE_X_LARGE) return cell def __init__(self): # Set the Glade file self.share_path = ['.', './data', '@gladedir@'] gladefile = 'hfconsole.glade' found = False for x in self.share_path: try: self.widgets = gtk.glade.XML(x + '/' + gladefile) self.gladefile = x + '/' + gladefile found = True except: continue if found: break if not found: print (_('Could not find required Glade XML file %s') % gladefile) exit(1) # Create our dictionay and connect it dic = { # Callbacks for the main window "AgSelector_button_press_event_cb" : self.ag_selector_open, "AgSelector_toggled_cb" : self.ag_selector_toggle, "AgScan_clicked_cb" : self.bar_ag_scan_clicked, "AgConnect_clicked_cb" : self.bar_ag_connect_clicked, "BarDialPad_clicked_cb" : self.bar_dialpad_clicked, "BarContacts_clicked_cb" : self.bar_contacts_clicked, "AgAudioToggle_toggled_cb" : self.bar_ag_audio_toggled, "AgVoiceRecogToggle_toggled_cb" : self.bar_ag_voice_recognition_toggled, "AgHangUp_clicked_cb" : self.bar_ag_hangup_clicked, "AgHoldCall_clicked_cb" : self.bar_ag_swaphold_clicked, "AgSwapCall_clicked_cb" : self.bar_ag_swaphold_clicked, "AgDial_clicked_cb" : self.bar_ag_dial_clicked, "AgRedial_clicked_cb" : self.bar_ag_redial_clicked, "ConfigOpen_clicked_cb" : self.bar_config_clicked, "DigitButton_pressed_cb" : self.digit_button_pressed, "DigitButton_released_cb" : self.digit_button_released, "DigitButton_clicked_cb" : self.digit_button_clicked, "PhoneNumEntry_changed_cb" : self.phone_num_changed, "PhoneNumBs_clicked_cb" : self.phone_num_bs, "Mute_toggled_cb" : self.mute_toggled, "on_MainWindow_destroy" : gtk.main_quit, "ContactsDelete_clicked_cb" : self.contacts_delete_clicked, "ContactsNew_clicked_cb" : self.contacts_new_clicked, "ContactsEdit_clicked_cb" : self.contacts_edit_clicked, # Known Devices tab "DevicesClose_clicked_cb" : self.devices_close_clicked, "DevicesAdd_clicked_cb" : self.devices_add_clicked, "DevicesDelete_clicked_cb" : self.devices_delete_clicked, "DevicesConnect_clicked_cb" : self.devices_connect_clicked, "DevicesDisconnect_clicked_cb" : self.devices_disconnect_clicked, "DevicesAutoConnectNever_toggled_cb" : self.devices_autoconnect_never_toggled, "DevicesAutoConnectDisabled_toggled_cb" : self.devices_autoconnect_disabled_toggled, "DevicesAutoConnectEnabled_toggled_cb" : self.devices_autoconnect_enabled_toggled, # New Device tab "NewDeviceClose_clicked_cb" : self.newdevice_close_clicked, "NewDeviceAdd_clicked_cb" : self.newdevice_add_clicked, "NewDeviceClear_clicked_cb" : self.newdevice_clear_clicked, # Callbacks for the scan tab "ScanAdd_clicked_cb" : self.scan_add_clicked, "ScanClose_clicked_cb" : self.scan_close_clicked, "ScanHistory_clicked_cb" : self.scan_history_clicked, "ScanRestart_clicked_cb" : self.scan_restart_clicked, # Callbacks for the history tab "HistoryOK_clicked_cb" : self.history_ok_clicked, "HistoryCancel_clicked_cb" : self.history_cancel_clicked, # Callbacks for the choose number tab "ChooseNumberCancel_clicked_cb" : self.choose_number_cancel_clicked, "ChooseNumberMobile_clicked_cb" : self.choose_number_mobile_clicked, "ChooseNumberHome_clicked_cb" : self.choose_number_home_clicked, "ChooseNumberWork_clicked_cb" : self.choose_number_work_clicked, # Callbacks for the config tab "ConfigOK_clicked_cb" : self.config_ok_clicked, "ConfigCancel_clicked_cb" : self.config_cancel_clicked, "ConfigInit_clicked_cb" : self.config_init_clicked, "ConfigDevices_clicked_cb" : self.config_devices_clicked, "ConfigDriver_changed_cb" : self.config_driver_changed, "ConfigPacketIntervalHint_toggled_cb" : self.config_packet_interval_toggled, "ConfigPacketInterval_value_changed_cb" : self.config_packet_interval_changed, "ConfigJitterWindowHint_toggled_cb" : self.config_jitter_window_toggled, "ConfigJitterWindow_value_changed_cb" : self.config_jitter_window_changed, "ConfigMinOutBufferHint_toggled_cb" : self.config_min_out_buffer_toggled, "ConfigMinOutBuffer_value_changed_cb" : self.config_min_out_buffer_changed, "ConfigFeedbackTest_toggled_cb" : self.config_loopback_toggled, "ConfigDenoise_toggled_cb" : self.config_denoise_changed, "ConfigEchoCancel_toggled_cb" : self.config_echo_cancel_changed, "ConfigEchoCancelTail_value_changed_cb" : self.config_echo_cancel_tail_changed, "ConfigAutoGain_value_changed_cb" : self.config_auto_gain_changed, "ConfigDereverb_toggled_cb" : self.config_dereverb_toggled, "ConfigDereverbValue_value_changed_cb" : self.config_dereverb_value_changed, "ConfigDereverbDecay_value_changed_cb" : self.config_dereverb_decay_changed, "ConfigRingerFile_file_set_cb" : self.config_ringer_file_set, "ConfigDspTest_clicked_cb" : self.config_dsptest_open, "on_ConfigDialog_close" : gtk.main_quit, # Callbacks for the DSP test "DspTestForward_clicked_cb" : self.config_dsptest_forward, "DspTestBack_clicked_cb" : self.config_dsptest_back, "DspTestClose_clicked_cb" : self.config_dsptest_close, "DspTestNotebook_switch_page_cb" : self.config_dsptest_switch_page, "DspTestRecStart1_toggled_cb" : self.config_dsptest_recstart1_toggled, "DspTestRecStart2_toggled_cb" : self.config_dsptest_recstart2_toggled, "DspTestPlayStart_toggled_cb" : self.config_dsptest_playstart_toggled, # Callbacks for the initial configuration page "InitConfDone_clicked_cb" : self.initconf_done_clicked, "InitConfBack_clicked_cb" : self.initconf_back_clicked, "InitConfForward_clicked_cb" : self.initconf_forward_clicked, "InitConfNotebook_switch_page_cb" : self.initconf_switch_page, } self.widgets.signal_autoconnect(dic) self.ag_list = gtk.Menu() self.ag_list_separator = gtk.SeparatorMenuItem() self.ag_list_separator.set_no_show_all(True) self.ag_list.append(self.ag_list_separator) self.ag_list_scan = gtk.MenuItem(_('Search for Device...')) self.ag_list_scan.connect('activate', self.ag_selector_scan_activate) self.ag_list.append(self.ag_list_scan) self.ag_list_manage = gtk.MenuItem(_('Manage Devices...')) self.ag_list_manage.connect('activate', self.ag_selector_manage_activate) self.ag_list.append(self.ag_list_manage) self.ag_list.connect('selection-done', self.ag_selector_close) self.ag_list.show_all() self.ag_list_detail = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_PYOBJECT) self['DevicesList'].set_model(self.ag_list_detail) tvcolumn = gtk.TreeViewColumn(_('Name')) self['DevicesList'].append_column(tvcolumn) cell = self.create_cellrenderertext() tvcolumn.pack_start(cell, True) tvcolumn.add_attribute(cell, 'text', 0) tvcolumn = gtk.TreeViewColumn(_('Address')) self['DevicesList'].append_column(tvcolumn) cell = self.create_cellrenderertext() tvcolumn.pack_start(cell, True) tvcolumn.add_attribute(cell, 'text', 1) self['DevicesList'].get_selection().connect('changed', self.devices_select) self.devices_ag = None self.ag_list_new = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_PYOBJECT) self['NewDeviceList'].set_model(self.ag_list_new) tvcolumn = gtk.TreeViewColumn(_('Name')) self['NewDeviceList'].append_column(tvcolumn) cell = self.create_cellrenderertext() tvcolumn.pack_start(cell, True) tvcolumn.add_attribute(cell, 'text', 0) tvcolumn = gtk.TreeViewColumn(_('Address')) self['NewDeviceList'].append_column(tvcolumn) cell = self.create_cellrenderertext() tvcolumn.pack_start(cell, True) tvcolumn.add_attribute(cell, 'text', 1) self['NewDeviceList'].get_selection().connect('changed', self.newdevice_select) self.ag_list_new.connect('row-inserted', self.newdevice_row_added) self.ag_list_new.connect('row-deleted', self.newdevice_row_removed) self.newdevice_ag = None self['ConfigDriverDevList'].set_model( gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)) self['ConfigDriverDevList'].set_text_column(0) # Set a file filter for the ringer chooser filt = gtk.FileFilter() filt.set_name(_('WAV Audio Files')) filt.add_pattern('*.wav') self["ConfigRingerFile"].add_filter(filt) self.inquiry = False self.scanresults = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_UINT, gobject.TYPE_STRING) self['ScanResults'].set_model(self.scanresults) tvcolumn = gtk.TreeViewColumn(_('Device')) self['ScanResults'].append_column(tvcolumn) cell = self.create_cellrenderertext() tvcolumn.pack_start(cell, True) tvcolumn.add_attribute(cell, 'text', 0) self.scanselect = self['ScanResults'].get_selection() self.scanselect.connect('changed', self.scan_select) self.historyresults = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_UINT, gobject.TYPE_STRING) self['HistoryResults'].set_model(self.historyresults) tvcolumn = gtk.TreeViewColumn(_('Device')) self['HistoryResults'].append_column(tvcolumn) cell = self.create_cellrenderertext() tvcolumn.pack_start(cell, True) tvcolumn.add_attribute(cell, 'text', 0) self.historyselect = self['HistoryResults'].get_selection() self.historyselect.connect('changed', self.history_select) # Configure D-Bus loop = DBusGMainLoop() self.dbus = dbus.SessionBus(mainloop=loop) self.ags = {} self.ags_added = {} self.soundmonitor = False self.selected_ag = None self.selected_ag_lock = False self.command_audio_attach_state = False self.nobt = False self.nested = False self.frontpage = 0 self.frontnotebook_set(self.frontpage) self.alerter_factory = (lambda x: GtkAlerter(self.gladefile, x)) self.vcards = VCardSet() sort = gtk.TreeModelSort(self.vcards.get_gtk_model()) def name_sort(model, iter1, iter2): n1 = model.get_value(iter1, 1) n2 = model.get_value(iter2, 1) if n1 < n2: return -1 if n1 == n2: return 0 return 1 sort.set_sort_func(0, name_sort) sort.set_sort_column_id(0, gtk.SORT_ASCENDING) self['ContactsList'].set_model(sort) tvcolumn = gtk.TreeViewColumn(_('Name')) self['ContactsList'].append_column(tvcolumn) cell = self.create_cellrenderertext() tvcolumn.pack_start(cell, True) tvcolumn.add_attribute(cell, 'text', 1) contactselect = self['ContactsList'].get_selection() contactselect.connect('changed', self.contacts_select) def command_audio_attach(self, state): if state == self.command_audio_attach_state: return self.command_audio_attach_state = state if state: exc = self.command_audio_attach_acmd else: exc = self.command_audio_attach_dcmd if exc: p = subprocess.Popen(exc, shell=True) def getprop_hfpd(self, prop): return self.hfpd_props.Get(self.HFPD_HANDSFREE_INTERFACE_NAME, prop) def getprop_soundio(self, prop): return self.soundio_props.Get(self.HFPD_SOUNDIO_INTERFACE_NAME, prop) def setprop_hfpd(self, name, value): self.hfpd_props.Set(self.HFPD_HANDSFREE_INTERFACE_NAME, name, value) def setprop_soundio(self, name, value): self.soundio_props.Set(self.HFPD_SOUNDIO_INTERFACE_NAME, name, value) def Start(self): try: self.busctl = dbus.Interface( self.dbus.get_object(self.DBUS_SERVICE_NAME, self.DBUS_BUS_OBJECT), dbus_interface=self.DBUS_INTERFACE_DBUS) except dbus.exceptions.DBusException, (ex): self.fatal(_('Could not connect to D-Bus:\n%s') % str(ex)) return try: self.hfpd = dbus.Interface( self.dbus.get_object(self.HFPD_SERVICE_NAME, self.HFPD_HANDSFREE_OBJECT), dbus_interface = self.HFPD_HANDSFREE_INTERFACE_NAME) self.hfpd_props = dbus.Interface( self.dbus.get_object(self.HFPD_SERVICE_NAME, self.HFPD_HANDSFREE_OBJECT), dbus_interface=self.DBUS_INTERFACE_PROPERTIES) self.soundio = dbus.Interface( self.dbus.get_object(self.HFPD_SERVICE_NAME, self.HFPD_SOUNDIO_OBJECT), dbus_interface = self.HFPD_SOUNDIO_INTERFACE_NAME) self.soundio_props = dbus.Interface( self.dbus.get_object(self.HFPD_SERVICE_NAME, self.HFPD_SOUNDIO_OBJECT), dbus_interface=self.DBUS_INTERFACE_PROPERTIES) except dbus.exceptions.DBusException, (ex): self.fatal(_('Could not connect to hfpd:\n%s\n\n' 'Ensure that hfpd and its D-Bus ' 'service file are installed correctly.\n' 'If the problem persists, try starting ' 'hfpd manually, e.g. \"hfpd\", or out of ' 'your build directory, e.g. ' '\"hfpd/hfpd\"') % str(ex)) return try: v = self.getprop_hfpd('Version') except: v = 0 my_version = 4 if v < my_version: self.fatal(_('Version mismatch with hfpd!\n' 'hfpd version: %(hfpdver)d\n' 'hfconsole version: %(hfconsolever)d\n\n' 'Ensure that hfpd is installed ' 'correctly.\n' 'If the problem persists, try ' '\"killall hfpd\"') % {'hfpdver': v, 'hfconsolever': my_version}) return elif v != my_version: self.fatal(_('Version mismatch with hfpd!\n' 'hfpd version: %(hfpdver)d\n' 'hfconsole version: %(hfconsolever)d\n\n' 'Ensure that hfconsole is installed correctly.\n' 'If the problem persists, try running hfconsole ' 'out of your build directory, e.g. ' '\"data/hfconsole\"') % {'hfpdver': v, 'hfconsolever': my_version}) return self.configfile_name = os.path.expanduser('~/.hfconsolerc') self.configfile = ConfigParser.ConfigParser() self.configfile.read([self.configfile_name]) if not self.configfile.has_section('options'): self.configfile.add_section('options') if not self.configfile.has_section('devices'): self.configfile.add_section('devices') if not self.configfile.has_section('history'): self.configfile.add_section('history') cmd = None if self.configfile.has_option('options', 'attach_command'): cmd = self.configfile.get('options', 'attach_command') self.command_audio_attach_acmd = cmd cmd = None if self.configfile.has_option('options', 'detach_command'): cmd = self.configfile.get('options', 'detach_command') self.command_audio_attach_dcmd = cmd cmd = None if self.configfile.has_option('options', 'default_ringtone'): cmd = self.configfile.get('options', 'default_ringtone') if not cmd: # Search for the default ringtone file for x in self.share_path: cmd = x + '/hfconsole_ring.wav' cmd = os.path.normpath( os.path.join(os.getcwd(), os.path.expanduser(cmd))) if os.access(cmd, os.R_OK): break cmd = None self.default_ringtone = cmd loadags = self.configfile.items('devices') for x in loadags: x = self.addr_untransform(x[0]) self.add_audiogateway(x, False) try: pass #self.vcards.import_file('~/.hfconsole.vcf') except Exception, (ex): print 'Load contacts: %s' % str(ex) self.busctl.connect_to_signal("NameOwnerChanged", self.hfpd_lost) self.hfpd.connect_to_signal("SystemStateChanged", self.hfpd_system_state) self.hfpd.connect_to_signal("InquiryResult", self.hfpd_inquiry_result) self.hfpd.connect_to_signal("InquiryStateChanged", self.hfpd_inquiry_complete) self.hfpd.connect_to_signal("AudioGatewayAdded", self.hfpd_ag_added) self.hfpd.connect_to_signal("AudioGatewayRemoved", self.hfpd_ag_removed) self.hfpd.connect_to_signal("LogMessage", self.hfpd_log_message) self.soundio.connect_to_signal("StateChanged", self.soundio_state_changed) self.soundio.connect_to_signal("StreamAborted", self.soundio_stream_aborted) self.soundio.connect_to_signal("MuteChanged", self.soundio_mute_changed) self.soundio.connect_to_signal("SkewNotify", self.soundio_skew_notify) self['Mute'].set_active(self.getprop_soundio('Mute')) self.ag_select_changed(None) self.system_ctx = self['StatusBar'].get_context_id( 'SystemState') self.soundio_status_ctx = self['StatusBar'].get_context_id( 'SoundIoStatus') self.soundio_skew_monitor = None self.nobt = not self.getprop_hfpd('SystemState') self.hfpd_system_state(not self.nobt) if self.nobt: try: self.hfpd.Start() self.nobt = False except dbus.exceptions.DBusException, (ex): if (ex.get_dbus_name() != 'net.sf.nohands.hfpd.Error'): self.fatal(str(ex)) return self.setprop_hfpd('AutoRestart', True) ags = self.getprop_hfpd('AudioGateways') for x in ags: self.hfpd_ag_added(str(x)) if not self.selected_ag: self.ag_none_selected() self.soundio_state_changed(self.getprop_soundio('State')) hwg['MainWindow'].show() def __getitem__(self, key): return self.widgets.get_widget(key) def error_closed(self, dlg, response): dlg.hide() def error_message(self, msg): dlg = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg) window = self['MainWindow'] dlg.set_title(window.get_title()) dlg.set_transient_for(window) dlg.set_position(gtk.WIN_POS_CENTER_ON_PARENT) dlg.connect('response', self.error_closed) dlg.show() return dlg def fatal_closed(self, widget, response): # dbus-python needs to fix its exception handler os._exit(1) def fatal(self, msg): dlg = self.error_message(msg) dlg.connect('response', self.fatal_closed) class AudioGateway: def reset_indicators(self): self.signal = -1 self.battery = -1 self.message = False self.vm = False self.noservice = False self.voicerecog = False self.inbandringtone = False def __init__(self, hfc, sbus, hfpd, agpath, box): self.hfc = hfc self.path = agpath self.hfpd = hfpd self.alerter = None self.caller_id = None self.reset_indicators() self.autoreconnect = 1 self.autoreconnect_flag = False self.normalizer = TelNormalizer() self.state = 0 self.callstate = 0 self.audiostate = 0 self.disp = hfc.ag_disp_new(self) self.dbusobj = sbus.get_object( hfc.HFPD_SERVICE_NAME, agpath) self.ag = dbus.Interface(self.dbusobj, dbus_interface = hfc.HFPD_AUDIOGATEWAY_INTERFACE_NAME) self.agprops = dbus.Interface(self.dbusobj, dbus_interface = hfc.DBUS_INTERFACE_PROPERTIES) self.sigs = [] sig = self.ag.connect_to_signal("NameResolved", self.name_resolved) self.sigs.append(sig) sig = self.ag.connect_to_signal("StateChanged", self.state_changed) self.sigs.append(sig) sig = self.ag.connect_to_signal("CallStateChanged", self.call_state_changed) self.sigs.append(sig) sig = self.ag.connect_to_signal("AudioStateChanged", self.audio_state_changed) self.sigs.append(sig) sig = self.ag.connect_to_signal("AutoReconnectChanged", self.autoreconnect_changed) self.sigs.append(sig) sig = self.ag.connect_to_signal("IndicatorChanged", self.indicator_changed) self.sigs.append(sig) sig = self.ag.connect_to_signal("Ring", self.ring_notify) self.sigs.append(sig) sig = self.ag.connect_to_signal( "VoiceRecognitionActiveChanged", self.voice_recognition_changed) self.sigs.append(sig) sig = self.ag.connect_to_signal( "InBandRingToneEnableChanged", self.inbandringtone_changed) self.sigs.append(sig) self.addr = self.getprop('Address') self.name = self.getprop('Name') if self.name == self.addr: self.name = self.hfc.ag_history_getname( self.addr) self.state = self.getprop('State') self.callstate = self.getprop('CallState') self.audiostate = self.getprop('AudioState') self.voicerecog = self.getprop( 'VoiceRecognitionActive') self.inbandringtone = self.getprop( 'InBandRingToneEnable') self.state_changed(self.state, False) self.call_state_changed(self.callstate) self.audio_state_changed(self.audiostate) self.disp.set_name(self.name) self.hfc.ag_update_history(self.addr, None) def has_feature(self, feat): return (hasattr(self, 'features') and self.features[feat]) def is_connecting(self): return self.state == 3 def is_connected(self): return self.state == 4 def can_dial(self): return self.is_connected() and (self.callstate == 1 or self.callstate == 4) def can_dtmf(self): return self.is_connected() and (self.callstate == 2 or self.callstate == 3 or self.callstate == 5) def can_hold(self): return (self.is_connected() and self.has_feature('SwapHoldActive') and ((self.callstate == 3) or (self.callstate == 5))) def has_any_call(self): return self.is_connected() and (self.callstate != 1) def has_waiting_call(self): return self.is_connected() and (self.callstate == 4 or self.callstate == 5) def has_message(self): return self.is_connected() and (self.message or self.vm) def can_drop_waiting(self): return self.has_feature('RejectCall') def can_drop_held_udub(self): return self.has_feature('DropHeldUdub') def has_voice_recognition(self): return (self.is_connected() and self.has_feature('VoiceRecognition')) def get_voice_recognition(self): return self.voicerecog def set_voice_recognition(self, st): self.ag.SetVoiceRecognition(dbus.Boolean(st), reply_handler=lambda : None, error_handler=self.command_failed) def voice_recognition_changed(self, st): self.voicerecog = st self.disp.set_state() def selectable(self): return self.is_mine() def select_priority(self): if self.has_audio(): return 4 if self.has_any_call(): return 3 if self.has_service(): return 2 if self.is_connected(): return 1 return 0 def is_mine(self): return self.hfc.ags_added.has_key(self.addr) def load_config(self): ar = True if self.hfc.ag_config_has_option(self.addr, 'autoreconnect'): ar = self.hfc.ag_config_get_option(self.addr, 'autoreconnect') try: ar = int(ar) except: ar = 2 if ar != self.autoreconnect: self.autoreconnect = ar if ar == 2: self.set_autoreconnect_flag(True) else: self.set_autoreconnect_flag(False) def save_config(self): self.hfc.ag_config_set_option(self.addr, 'autoreconnect', self.autoreconnect) def update_mine(self): self.disp.set_name(None) self.disp.set_name(self.name) def get_autoreconnect(self): return self.autoreconnect def set_autoreconnect(self, value): if value == self.autoreconnect: return self.autoreconnect = value if value == 2: self.set_autoreconnect_flag(True) else: self.set_autoreconnect_flag(False) self.save_config() self.disp.set_state() def get_autoreconnect_flag(self): return self.autoreconnect_flag def set_autoreconnect_flag(self, value): value = bool(value) if value != self.autoreconnect_flag: self.setprop('AutoReconnect', dbus.Boolean(value)) self.autoreconnect_flag = value def has_service(self): return self.is_connected() and not self.noservice def signal_level(self): return self.signal def battery_level(self): return self.battery def audio_connecting(self): return self.audiostate == 2 def has_audio(self): return self.audiostate == 3 def wants_audio(self): return (self.callstate == 2 or self.callstate == 3 or self.callstate == 5) def name_resolved(self, name): self.name = name self.disp.set_name(self.name) self.hfc.ag_update_history(self.addr, self.name) def state_changed(self, state, voluntary): self.state = int(state) if self.is_connected(): self.features = self.getprop('Features') if (self.is_mine() and self.get_autoreconnect() == 1): self.set_autoreconnect(2) else: self.reset_indicators() self.call_state_changed(1) if hasattr(self, 'features'): del self.features self.disp.set_state() def call_state_changed(self, state): self.callstate = int(state) self.disp.set_state() if (self.callstate != 4 and self.callstate != 5): self.alert_close() def audio_state_changed(self, state): self.audiostate = int(state) self.disp.set_state() def soundio_state_changed(self): self.disp.set_state() def inbandringtone_changed(self, st): self.inbandringtone = st # So far we don't really care about this, # because audio gateways get priority for the # sound card. def autoreconnect_changed(self, value): value = bool(value) if self.autoreconnect_flag != value: self.autoreconnect_flag = value if value and self.get_autoreconnect() == 0: self.set_autoreconnect(2) elif not value and self.get_autoreconnect() ==2: self.set_autoreconnect(1) def indicator_changed(self, name, val): #print "Indicator %s -> %d" % (name, int(val)) if name == 'service': self.noservice = not val self.disp.set_state() elif name == 'signal': self.signal = int(val) self.disp.set_state() elif name == 'battchg': self.battery = int(val) self.disp.set_state() elif name == 'voice mail': self.vm = int(val) self.disp.set_state() elif name == 'message': self.vm = int(val) self.disp.set_state() def alert_close(self): if self.alerter: self.alerter.remove() self.alerter = None self.hfc.ag_ring_stop(self) self.caller_id = None self.caller_phbook = None def alert_open(self): if not self.alerter: self.alerter = self.hfc.alerter_factory(self) self.alerter.update_state() def ring_notify(self, number, number_type, subaddr, satype, phbook_ent): if self.caller_id != number: self.caller_id = number if not phbook_ent: num_norm = self.normalizer.normalize( number, number_type) phbook_ent = self.hfc.contacts_lookup( num_norm) self.caller_phbook = phbook_ent self.alert_open() if (self.alerter and not self.alerter.completed and self.callstate == 4): self.hfc.ag_ring(self) def waiting_caller_info(self): return (self.caller_id, self.caller_phbook) def connect(self): self.ag.Connect() def disconnect(self): self.ag.Disconnect() def command_failed(self, reason): print _("Command failed: %s") % reason def open_audio(self): self.ag.OpenAudio() def close_audio(self): self.ag.CloseAudio() def dial(self, number): self.ag.Dial(number, reply_handler=lambda : None, error_handler=self.command_failed) def redial(self): self.ag.Redial(reply_handler=lambda : None, error_handler=self.command_failed) def hangup(self): self.ag.HangUp(reply_handler=lambda : None, error_handler=self.command_failed) def answer(self): self.ag.Answer(reply_handler=lambda : None, error_handler=self.command_failed) def drop_held_udub(self): self.ag.CallDropHeldUdub(reply_handler=lambda : None, error_handler=self.command_failed) def swap_hold_active(self): self.ag.CallSwapHoldActive(reply_handler=lambda : None, error_handler=self.command_failed) def swap_drop_active(self): self.ag.CallSwapDropActive(reply_handler=lambda : None, error_handler=self.command_failed) def call_link(self): self.ag.CallLink(reply_handler=lambda : None, error_handler=self.command_failed) def call_transfer(self): self.ag.CallTransfer(reply_handler=lambda : None, error_handler=self.command_failed) def dtmf(self, char): self.ag.SendDtmf(char[0], reply_handler=lambda : None, error_handler=self.command_failed) def do_remove(self): self.hfc.ag_remove_from_config(self.addr) if self.hfc.ags_added.has_key(self.addr): del self.hfc.ags_added[self.addr] x = self.setprop('Known', False) self.hfpd.RemoveDevice(self.addr) def remove(self): self.disp.remove() del self.disp while self.sigs: sig = self.sigs.pop() sig.remove() def getprop(self, prop): return self.agprops.Get( self.hfc.HFPD_AUDIOGATEWAY_INTERFACE_NAME, prop) def setprop(self, prop, val): return self.agprops.Set( self.hfc.HFPD_AUDIOGATEWAY_INTERFACE_NAME, prop, val) class AgStateDisplay: def __init__(self, hf, ag): self.hf = hf self.ag = ag self.item = None self.row_detail = None self.row_new = None def selector_toggle(self, widget): if widget.get_active(): self.hf.ag_selector_changed(self.ag) def remove_item(self): if self.item: self.hf.ag_list.remove(self.item) self.item = None kids = self.hf.ag_list.get_children() count = len(kids) if count == 3: self.hf.ag_list_separator.hide() def set_name(self, name): if not name: self.remove_item() if self.row_detail: self.hf.ag_list_detail.remove( self.row_detail) self.row_detail = None if self.row_new: self.hf.ag_list_new.remove( self.row_new) self.row_new = None return if not self.ag.is_mine(): if not self.row_new: self.row_new = (self.hf.ag_list_new. append( row=[name, self.ag.addr, self])) else: self.hf.ag_list_new.set(self.row_new, 0, name) return self.remove_item() group = None kids = self.hf.ag_list.get_children() count = len(kids) if count > 3: group = kids[0] self.hf.ag_list_separator.show() self.item = gtk.RadioMenuItem(group, name) self.item.connect('toggled', self.selector_toggle) self.item.show_all() if self.ag == self.hf.selected_ag: self.item.set_active(True) self.hf.ag_list.insert(self.item, count - 3) if self.ag == self.hf.selected_ag: self.hf['AgSelectorLabel'].set_text(name) if not self.row_detail: self.row_detail = (self.hf.ag_list_detail. append( row=[name, self.ag.addr, self])) else: self.hf.ag_list_detail.set(self.row_detail, 0, name) def remove(self): if self.ag == self.hf.selected_ag: self.hf.selected_ag = None self.hf.ag_none_selected() self.ag = None self.set_name(None) def do_mainwindow_state(self, string): self.hf['AudioGatewayStateLabel'].set_markup( string) def mainwindow_state(self): if self.hf.selected_ag != self.ag: return state = self.ag.state callstate = self.ag.callstate lab = 'Invalid State' if not self.ag.is_connected(): if state == 1: lab = _('Destroyed') if state == 2: lab = _('Detached') if state == 3: lab = _('Attaching') lab += '\n' else: if callstate == 1: lab = _('Ready') if not self.ag.has_service(): lab = _('No Service') if callstate == 2: lab = _('Dialing') if callstate == 3: lab = _('Call Established') if callstate == 4: lab = _('Ringing') if callstate == 5: lab = _('Waiting Call') lab += '\n' if self.ag.has_audio(): lab += _('Audio Open') elif self.ag.audio_connecting(): lab += _('Audio Connecting') self.do_mainwindow_state(lab) self.hf.ag_configure_gui(self.ag) def devices_state(self): if self.hf.devices_ag != self.ag: return self.hf.nested = True self.hf['DevicesProperties'].set_sensitive(True) self.hf['DevicesDevAddr'].set_text(self.ag.addr) v = self.ag.get_autoreconnect() if v == 0: v = 'DevicesAutoConnectNever' elif v == 1: v = 'DevicesAutoConnectDisabled' else: v = 'DevicesAutoConnectEnabled' self.hf[v].set_active(True) if self.ag.is_connected(): self.hf['DevicesConnect'].hide() self.hf['DevicesDisconnect'].show() else: self.hf['DevicesConnect'].show() self.hf['DevicesConnect'].set_sensitive( not self.ag.is_connecting()) self.hf['DevicesDisconnect'].hide() self.hf.nested = False def update_state(self): self.mainwindow_state() self.devices_state() def set_state(self): self.hf.ag_state_changed(self.ag) self.update_state() def ag_disp_new(self, ag): disp = self.AgStateDisplay(self, ag) return disp def ag_selector_set(self, ag): do_arrow = False if ag: lab = ag.name ag.disp.item.set_active(True) self['AgSelectorArrow'].show() else: lab = _('Search for Device...') self['AgSelectorArrow'].hide() self['AgSelectorLabel'].set_text(lab) def ag_selector_menu_position(self, menu): widget = self['AgSelector'] alloc = widget.get_allocation() (x, y) = widget.get_parent_window().get_origin() x += alloc.x y += alloc.y + alloc.height return (x, y, True) def ag_selector_open(self, widget, event): kids = self.ag_list.get_children() count = len(kids) if count == 3: return self.nested = True self['AgSelector'].set_active(True) self.nested = False self.ag_list.popup(None, None, self.ag_selector_menu_position, event.button, event.get_time()) def ag_selector_close(self, widget): self.nested = True self['AgSelector'].set_active(False) self.nested = False def ag_selector_toggle(self, widget): if self.nested: return self.nested = True if widget.get_active(): widget.set_active(False) self.scan_open() self.nested = False def ag_selector_scan_activate(self, widget): self.scan_open() def ag_selector_manage_activate(self, widget): self.devices_open() def ag_selector_changed(self, ag): if not ag.selectable(): ag = None if self.selected_ag and self.selected_ag.selectable(): self.selected_ag.item.set_active(True) ag = self.selected_ag self.ag_selector_set(ag) return # If the user explicitly picks an audio gateway of lesser # priority than the currently selected AG, consider the # selection to be locked to that audio gateway. lock = False if self.selected_ag and (self.selected_ag.select_priority() > ag.select_priority()): lock = True self.ag_select_changed(ag, lock) def hfpd_lost(self, name, old_owner, new_owner): if name != self.HFPD_SERVICE_NAME or new_owner != '': return msg = _('HFPD Process Disconnected') print msg self.fatal(msg) def hfpd_ag_added(self, agpath): if agpath not in self.ags: # This can fail if the signals get # stacked up ag = self.AudioGateway(self, self.dbus, self.hfpd, agpath, self['AudioGatewayBox']) self.ags[agpath] = ag if ag.is_mine(): ag.load_config() else: self.newdevice_open() if (self.ags_added.has_key(ag.addr) and self.ags_added[ag.addr] == 2): if (not self.selected_ag or not self.selected_ag.wants_audio()): self.ags_added[ag.addr] = 1 self.ag_select_changed(ag) def hfpd_ag_removed(self, agpath): if agpath in self.ags: self.ags[agpath].remove() del self.ags[agpath] if not self.selected_ag: self.ag_none_selected() def ag_none_selected(self): (ag, prio) = self.ag_best() self.ag_select_changed(ag) def ag_best(self): best_ag = None best_prio = -1 for xag in self.ags.values(): if xag.selectable() and ( not best_ag or (best_prio < xag.select_priority())): best_ag = xag best_prio = xag.select_priority() return (best_ag, best_prio) def ag_stop_audio_unselected(self): for xag in self.ags.values(): if xag != self.selected_ag and xag.has_audio(): xag.close_audio() def ag_push_audio(self): # This process is messy # We let the audio gateway connect to us, then # we configure the pipe. Passing False as the # second parameter to AudioGatewayStart ensures that # we don't initiate audio connections. if (self.selected_ag and self.selected_ag.has_audio() and (self.soundstate == 2 or self.soundstate == 5 or self.soundstate == 6 or self.soundstate == 7)): try: self.soundio.AudioGatewayStart( self.selected_ag.path, False) self.command_audio_attach(True) except: self.soundio_status_msg( _('Failed to Open Primary Sound Card'), 5000) if self.selected_ag.has_audio(): self.selected_ag.close_audio() self.ag_stop_audio_unselected() def ag_state_changed(self, ag): #print "AG state changed %s %slock" % (ag.name, self.selected_ag_lock) if ag and ag == self.selected_ag: self.ag_push_audio() if self.selected_ag_lock and self.selected_ag.selectable(): self.ag_stop_audio_unselected() return # Evaluate all AGs and maybe change selection (best, prio) = self.ag_best() if (not self.selected_ag or not self.selected_ag.selectable() or (self.selected_ag.select_priority() < prio)): self.ag_select_changed(best) if not self.selected_ag: self.ag_stop_audio_unselected() def ag_select_changed(self, ag, lock = False): if not ag: lock = False elif not lock and self.selected_ag == ag: lock = self.selected_ag_lock self.selected_ag = ag self.selected_ag_lock = lock self.ag_selector_set(ag) if ag: ag.disp.update_state() else: self.ag_configure_gui(None) if ag: self.ag_push_audio() def ag_ring(self, ag): if (self.default_ringtone and self.soundstate == 2): # Try to play the ring tone try: self.soundio.FileStart(self.default_ringtone, False) self.ag_play_target = ag except: pass def ag_ring_stop(self, ag): if (hasattr(self, 'ag_play_target') and self.ag_play_target == ag): del self.ag_play_target self.soundio.Stop() def ag_config_has_option(self, addr, name): section = 'ag_' + self.addr_transform(addr) if not self.configfile.has_section(section): return False return self.configfile.has_option(section, name) def ag_config_get_option(self, addr, name): section = 'ag_' + self.addr_transform(addr) if not self.configfile.has_section(section): return None return self.configfile.get(section, name, raw=True) def ag_config_set_option(self, addr, name, value): section = 'ag_' + self.addr_transform(addr) if not self.configfile.has_section(section): self.configfile.add_section(section) self.configfile.set(section, name, value) self.configfile.write(open(self.configfile_name, 'w+')) def ag_add_to_config(self, addr): self.configfile.set('devices', self.addr_transform(addr), True) self.configfile.write(open(self.configfile_name, 'w+')) def ag_remove_from_config(self, addr): self.configfile.remove_option('devices', self.addr_transform(addr)) section = 'ag_' + self.addr_transform(addr) self.configfile.remove_section(section) self.configfile.write(open(self.configfile_name, 'w+')) def ag_update_history(self, addr, name): addrx = self.addr_transform(addr) has = self.configfile.has_option('history', addrx) if not name and has: return if has and name == self.configfile.get('history', addrx): return if not name: name = addr self.configfile.set('history', addrx, name) self.configfile.write(open(self.configfile_name, 'w+')) def ag_history_getname(self, addr): addrx = self.addr_transform(addr) if not self.configfile.has_option('history', addrx): return None return self.configfile.get('history', addrx) def add_audiogateway(self, addr, selectit): agpath = None try: if selectit: self.ags_added[addr] = 2 else: self.ags_added[addr] = 1 # We will use the accompanying AudioGatewayAdded # signal to construct the AudioGateway object above agpath = self.hfpd.AddDevice(addr, False) except: del self.ags_added[addr] self.error_message(_('Could not attach Audio Gateway')) def hfpd_log_message(self, level, msg): print 'HFPD: ' + msg def digit_button_char(self, but): assert but.get_name()[:11] == 'DigitButton' text = but.get_name()[11:] if text == 'Ast': text = '*' elif text == 'Pnd': text = '#' return text def digit_button_output_char(self, char): entry = self['PhoneNumEntry'] entry.set_text(entry.get_text() + char) def digit_button_plus_timeout_cancel(self): if hasattr(self, 'digit_plus_timeout'): gobject.source_remove(self.digit_plus_timeout) del self.digit_plus_timeout def digit_button_plus_timeout_set(self, timeout): self.digit_plus_timeout = gobject.timeout_add( timeout, self.digit_button_plus_timeout) def digit_button_plus_timeout(self): self.digit_button_plus_timeout_cancel() self.nested = True self['DigitButton0'].released() self.nested = False self.digit_button_output_char('+') return False def digit_button_pressed(self, but): ag = self.selected_ag text = self.digit_button_char(but) if not ag or not ag.can_dtmf(): if text == '0' and ag and ag.can_dial(): self.digit_button_plus_timeout_set(1000) return ag.dtmf(text) def digit_button_released(self, but): if self.nested: return text = self.digit_button_char(but) if text == '0': self.digit_button_plus_timeout_cancel() def digit_button_clicked(self, but): if self.nested: return ag = self.selected_ag if not ag or not ag.can_dial(): return text = self.digit_button_char(but) self.digit_button_output_char(text) def phone_num_changed(self, widget): self.ag_configure_gui(self.selected_ag) def phone_num_bs(self, but): entry = self['PhoneNumEntry'] entry.set_text(entry.get_text()[:-1]) def ag_configure_gui(self, ag): sel_dis = False connect = False connect_ip = False digits = False do_notebook = False dial = False dial_gray = False hold = False swap = False hangup = False audio = False audio_ip = False audio_dn = False voicerecog = False voicerecog_dn = False record = False record_dn = False mainstatus = False status_page = 0 battstatus = False batt_page = 0 message = False if ag: mainstatus = True if not ag.is_connected(): connect = True connect_ip = ag.is_connecting() or self.nobt status_page = 8 else: status_page = 7 if ag.has_service(): status_page = ag.signal_level() if status_page < 0 or status_page > 5: status_page = 6 digits = ag.can_dial() or ag.can_dtmf() dial = ag.can_dial() hold = ag.can_hold() do_notebook = not ag.can_dial() hangup = not dial if hold and ag.has_waiting_call(): swap = True hold = False audio = True if ag.has_audio(): audio_dn = True elif ag.audio_connecting(): audio_ip = True message = ag.has_message() batt_page = ag.battery_level() battstatus = True if batt_page < 0 or batt_page > 5: batt_page = 0 battstatus = False if (ag.has_voice_recognition() and ag.can_dial()): voicerecog = True voicerecog_dn = ( ag.get_voice_recognition()) # Display the "no bluetooth" icon if BT is disconnected # and the AG is not connected if self.nobt and (not ag or (not ag.is_connected() and not ag.is_connecting())): mainstatus = True status_page = 9 sel_dis = True # Gray out the dial button if the UI does not have a # phone number entered or a contact selected if dial: pad = True if not do_notebook and self.frontpage == 1: pad = False if pad: dial_gray = not bool(self['PhoneNumEntry']. get_text()) else: sel = self['ContactsList'].get_selection() dial_gray = (sel.count_selected_rows() == 0) def set_vis(widget, visible): if visible: widget.show() else: widget.hide() self.nested = True self['AgSelector'].set_sensitive(not sel_dis) self['DigitButtonBox'].set_sensitive(digits) self['PhoneNumEntryBox'].set_sensitive(dial) set_vis(self['AgConnect'], connect) self['AgConnect'].set_sensitive(not connect_ip) set_vis(self['AgHangUp'], hangup) set_vis(self['AgHoldCall'], hold) set_vis(self['AgSwapCall'], swap) set_vis(self['AgDial'], dial) self['AgDial'].set_sensitive(not dial_gray) set_vis(self['AgRedial'], dial) set_vis(self['AgAudioToggle'], audio) self['AgAudioToggle'].set_active(audio_dn) self['AgAudioToggle'].set_sensitive(not audio_ip) set_vis(self['AgVoiceRecogToggle'], voicerecog) self['AgVoiceRecogToggle'].set_active(voicerecog_dn) set_vis(self['RecordCall'], record) self['RecordCall'].set_active(record_dn) set_vis(self['AgMainStatus'], mainstatus) self['AgMainStatus'].set_current_page(status_page) set_vis(self['AgBatteryStatus'], battstatus) self['AgBatteryStatus'].set_current_page(batt_page) set_vis(self['MessageIndicator'], message) self.nested = False if do_notebook: self.frontnotebook_set(0) else: self.frontnotebook_set(self.frontpage) def bar_ag_dial_clicked(self, widget): ag = self.selected_ag if not ag: return if not ag.can_dial(): return if self.frontpage == 0: entry = self['PhoneNumEntry'] num = entry.get_text() entry.set_text('') if not num: return ag.dial(num) else: sel = self['ContactsList'].get_selection() (model, iterx) = sel.get_selected() if not iterx: return vcard = model.get_value(iterx, 0) nums = vcard.findtels(None) if not len(nums): pass elif len(nums) == 1: num = vcard.gettel(None) ag.dial(num) else: # Ask the user to pick a number self.choose_number_open(vcard) def bar_ag_scan_clicked(self, widget): self.scan_open() def bar_ag_connect_clicked(self, widget): try: if self.selected_ag: self.selected_ag.connect() except dbus.exceptions.DBusException, (ex): self.error_message( _('Could not attach Audio Gateway: %s') % str(ex)) def frontnotebook_set(self, page): dialpad_but = False contacts_but = False page = 0 if page == 0: #contacts_but = True if (self.selected_ag and self.selected_ag.is_connected() and not self.selected_ag.can_dial()): contacts_but = False pass elif page == 1: dialpad_but = True def set_vis(widget, visible): if visible: widget.show() else: widget.hide() self['FrontNotebook'].set_current_page(page) set_vis(self['BarDialPad'], dialpad_but) set_vis(self['BarContacts'], contacts_but) def bar_dialpad_clicked(self, widget): self.frontpage = 0 self.ag_configure_gui(self.selected_ag) def bar_contacts_clicked(self, widget): self.frontpage = 1 self.ag_configure_gui(self.selected_ag) def bar_ag_hangup_clicked(self, widget): ag = self.selected_ag if not ag: return ag.hangup() def bar_ag_swaphold_clicked(self, widget): ag = self.selected_ag if not ag or not ag.can_hold(): return ag.swap_hold_active() def bar_ag_redial_clicked(self, widget): ag = self.selected_ag if not ag or not ag.can_dial(): return ag.redial() def bar_ag_audio_toggled(self, widget): if not self.selected_ag: return if not widget.get_active(): if (self.selected_ag.has_audio() or self.selected_ag.audio_connecting()): self.selected_ag.close_audio() return if (self.selected_ag.has_audio() or self.selected_ag.audio_connecting()): return try: self.selected_ag.open_audio() except dbus.exceptions.DBusException, (ex): self.error_message( _('Could not open audio connection: %s') % str(ex)) widget.set_active(False) def bar_ag_voice_recognition_toggled(self, widget): if (self.nested or not self.selected_ag or not self.selected_ag.has_voice_recognition()): return self.selected_ag.set_voice_recognition(widget.get_active()) def bar_config_clicked(self, widget): self.config_open() def contacts_new_clicked(self, widget): pass def contacts_edit_clicked(self, widget): pass def contacts_delete_clicked(self, widget): pass def contacts_select(self, widget): self.ag_configure_gui(self.selected_ag) def contacts_lookup(self, num_norm): (vcard, types) = self.vcards.search_tel(num_norm) if not vcard: return None nfmt = _('%s (Home)') for x in types: if x == 'cell': nfmt = _('%s (Mobile)') elif x == 'work': nfmt = _('%s (Work)') return nfmt % vcard.getname() def choose_number_open(self, vcard): tel_cell = vcard.gettel('cell') tel_home = vcard.gettel('home') tel_work = vcard.gettel('work') def fix_button(prefix, tel): prefix = 'ChooseNumber' + prefix if tel: self[prefix + 'Label'].set_text(tel) self[prefix].show() else: self[prefix].hide() fix_button('Mobile', tel_cell) fix_button('Home', tel_home) fix_button('Work', tel_work) self.topnotebook_set(self['ChooseNumberTab']) def choose_number_done(self): self.topnotebook_set(None) def choose_number_cancel_clicked(self, widget): self.choose_number_done() def choose_number_finish(self, num): self.choose_number_done() if self.selected_ag and self.selected_ag.can_dial(): self.selected_ag.dial(num) def choose_number_mobile_clicked(self, widget): num = self['ChooseNumberMobileLabel'].get_text() self.choose_number_finish(num) def choose_number_home_clicked(self, widget): num = self['ChooseNumberHomeLabel'].get_text() self.choose_number_finish(num) def choose_number_work_clicked(self, widget): num = self['ChooseNumberWorkLabel'].get_text() self.choose_number_finish(num) def hfpd_system_msg_clear(self): if hasattr(self, 'system_msg'): self['StatusBar'].remove(self.system_ctx, self.system_msg) del self.system_msg if hasattr(self, 'system_msg_timeout'): gobject.source_remove(self.system_msg_timeout) del self.system_msg_timeout return False def hfpd_system_msg(self, msg, timeout): self.hfpd_system_msg_clear() self.system_msg = self['StatusBar'].push(self.system_ctx, msg) if timeout: self.system_msg_timeout = gobject.timeout_add( timeout, self.hfpd_system_msg_clear) def hfpd_system_state(self, state): if not state: self.nobt = True self.hfpd_system_msg(_('Bluetooth Unavailable'), None) else: self.nobt = False self.hfpd_system_msg_clear() self.ag_configure_gui(self.selected_ag) def topnotebook_set(self, page): nb = self['HiddenNotebook'] if not hasattr(self, 'notebook_stack'): self.notebook_stack = [] if page: self.notebook_stack.append(nb.get_current_page()) nb.set_current_page(nb.page_num(page)) elif len(self.notebook_stack): nb.set_current_page(self.notebook_stack.pop()) else: nb.set_current_page(0) def topnotebook_get(self): nb = self['HiddenNotebook'] return nb.get_nth_page(nb.get_current_page()) def devices_open(self): self.topnotebook_set(self['DevicesTab']) def devices_close_clicked(self, widget): self.topnotebook_set(None) def devices_add_clicked(self, widget): self.scan_open() def devices_delete_clicked(self, widget): if self.devices_ag: self.ag_remove_from_config(self.devices_ag.addr) self.devices_ag.do_remove() def devices_connect_clicked(self, widget): if self.devices_ag: self.devices_ag.connect() def devices_disconnect_clicked(self, widget): if self.devices_ag: self.devices_ag.disconnect() def devices_autoconnect_toggled(self, widget, value): if self.nested or not widget.get_active(): return if self.devices_ag: self.devices_ag.set_autoreconnect(value) def devices_autoconnect_never_toggled(self, widget): self.devices_autoconnect_toggled(widget, 0) def devices_autoconnect_disabled_toggled(self, widget): self.devices_autoconnect_toggled(widget, 1) def devices_autoconnect_enabled_toggled(self, widget): self.devices_autoconnect_toggled(widget, 2) def devices_select(self, selection): if not selection.count_selected_rows(): self.devices_ag = None self['DevicesDevAddr'].set_text('') self['DevicesConnect'].hide() self['DevicesDisconnect'].show() self['DevicesProperties'].set_sensitive(False) return (store, iter) = selection.get_selected() disp = store.get_value(iter, 2) self.devices_ag = disp.ag disp.devices_state() self['DevicesProperties'].show() def newdevice_open(self): tab = self['NewDeviceTab'] if self.topnotebook_get() == tab: return itx = self.ag_list_new.get_iter_root() if itx: self['NewDeviceList'].get_selection().select_iter(itx) self.topnotebook_set(tab) def newdevice_close_clicked(self, widget): self.topnotebook_set(None) def newdevice_add_clicked(self, widget): ag = self.newdevice_ag self.add_audiogateway(ag.addr, False) ag.update_mine() self.ag_add_to_config(ag.addr) ag.set_autoreconnect(2) self.ag_select_changed(ag) def newdevice_clear_clicked(self, widget): xiter = self.ag_list_new.get_iter_root() while xiter: disp = self.ag_list_new.get_value(xiter, 2) disp.ag.do_remove() xiter = self.ag_list_new.iter_next(xiter) def newdevice_select(self, selection): if not selection.count_selected_rows(): self['NewDeviceAdd'].set_sensitive(False) return (store, iter) = selection.get_selected() disp = store.get_value(iter, 2) self.newdevice_ag = disp.ag self['NewDeviceAdd'].set_sensitive(True) def newdevice_row_added(self, list, path, iter): self['NewDeviceClear'].set_sensitive(True) def newdevice_row_removed(self, list, path): if not list.get_iter_root(): self['NewDeviceClear'].set_sensitive(False) tab = self['NewDeviceTab'] if self.topnotebook_get() == tab: self.topnotebook_set(None) def inquiry_show_activity(self): self['ScanActivityIndicator'].pulse() return True def inquiry_started(self): self.inquiry = True self['ScanRestart'].set_sensitive(False) self['ScanActivityIndicator'].set_fraction(0) self['ScanActivity'].show() self.inquiry_animation_timeout = gobject.timeout_add( 175, self.inquiry_show_activity) def inquiry_stopped(self): self.inquiry = False self['ScanActivity'].hide() self['ScanRestart'].set_sensitive(True) if hasattr(self, 'inquiry_animation_timeout'): gobject.source_remove(self.inquiry_animation_timeout) del self.inquiry_animation_timeout def scan_restart(self): self.scanresults.clear() self['ScanAdd'].set_sensitive(False) try: self.hfpd.StartInquiry() except: return False self.inquiry_started() return True def scan_open(self): if not self.scan_restart(): self.error_message(_('Could not start inquiry')) return self.topnotebook_set(self['ScanTab']) def scan_close(self): self.topnotebook_set(None) if self.inquiry: try: self.hfpd.StopInquiry() except: pass self.inquiry_stopped() def scan_create_session(self, store, iter): # Create a session for the selected bdaddr self.scan_close() assert store and iter addr = store.get_value(iter, 2) self.ag_add_to_config(addr) self.ag_config_set_option(addr, 'autoreconnect', 2) self.add_audiogateway(addr, True) def scan_add_clicked(self, widget): assert (self.scanselect.count_selected_rows() <= 1) (store, iter) = self.scanselect.get_selected() self.scan_create_session(store, iter) def scan_close_clicked(self,widget): self.scan_close() def scan_history_clicked(self, widget): self.history_open() def scan_restart_clicked(self, widget): if not self.scan_restart(): self.error_message(_('Could not start inquiry')) def scan_select(self, selection): if not selection.count_selected_rows(): self['ScanAdd'].set_sensitive(False) else: self['ScanAdd'].set_sensitive(True) class NameResolveReq: def __init__(self, hfc, addr, store): self.addr = addr self.store = store self.hfc = hfc def complete(self, name): ix = self.store.get_iter_first() while ix: if self.store.get_value(ix, 2) == self.addr: self.store.set_value(ix, 0, name) ix = self.store.iter_next(ix) self.hfc.ag_update_history(self.addr, name) self.hfc = None def failed(self, name): ix = self.store.get_iter_first() while ix: if self.store.get_value(ix, 2) == self.addr: name = self.store.get_value(ix, 2) name += ' ' + _('(unknown name)') self.store.set_value(ix, 0, name) ix = self.store.iter_next(ix) self.hfc = None def hfpd_inquiry_result(self, addr, devclass): if self.inquiry: self.scanresults.append(row=[addr, devclass, addr]) self.ag_update_history(addr, None) def hfpd_inquiry_complete(self, sval): if sval: return if not self.inquiry: return self.inquiry_stopped() ix = self.scanresults.get_iter_first() while ix: addr = self.scanresults.get_value(ix, 2) req = self.NameResolveReq(self, addr, self.scanresults) self.hfpd.GetName(addr, reply_handler=req.complete, error_handler=req.failed) ix = self.scanresults.iter_next(ix) # History box def history_open(self): self.historyresults.clear() self['HistoryOK'].set_sensitive(False) # Populate the device history results from the config file ags = self.configfile.items('history') for x in ags: addr = self.addr_untransform(x[0]) name = x[1] if not name: name = addr else: name = "%s (%s)" % (name, addr) self.historyresults.append(row=[name, 0, addr]) self.topnotebook_set(self['HistoryTab']) def history_close(self): self.topnotebook_set(None) def history_ok_clicked(self, widget): self.history_close() assert self.historyselect.count_selected_rows() <= 1 (store, iter) = self.historyselect.get_selected() self.scan_create_session(store, iter) def history_cancel_clicked(self, widget): self.history_close() def history_select(self, selection): if not selection.count_selected_rows(): self['HistoryOK'].set_sensitive(False) else: self['HistoryOK'].set_sensitive(True) # Configuration dialog related methods def config_open(self): savecfg = self.config_get_vals() savecfg['packetintervalsave'] = savecfg['packetinterval'] savecfg['jitterwindowsave'] = savecfg['jitterwindow'] savecfg['minbufferfillsave'] = savecfg['minbufferfill'] savecfg['echocancelsave'] = savecfg['echocancel'] if savecfg['echocancelsave'] == 0: savecfg['echocancelsave'] = 100 savecfg['dereverbsave'] = savecfg['dereverb'] savecfg['dereverbdecaysave'] = savecfg['dereverbdecay'] self.config = copy.copy(savecfg) if savecfg['secmode'] == 0: self['ConfigSecNone'].set_active(True) elif savecfg['secmode'] == 1: self['ConfigSecAuth'].set_active(True) else: self['ConfigSecCrypt'].set_active(True) self['ConfigAcceptUnknown'].set_active( savecfg['acceptunknown']) self['ConfigVoicePersist'].set_active( savecfg['voicepersist']) self['ConfigDriver'].get_model().clear() for x in savecfg['drivers']: self['ConfigDriver'].append_text(x) self['ConfigDriver'].set_active(savecfg['driver']) self['ConfigDriverDevList'].child.set_text( savecfg['driveropts']) self.config_packet_interval(savecfg['packetinterval']) self.config_jitter_window(savecfg['jitterwindow']) self.config_min_out_buffer(savecfg['minbufferfill']) self['ConfigDenoise'].set_active(savecfg['denoise']) self.config_echo_cancel(savecfg['echocancel']) self['ConfigAutoGain'].set_value(savecfg['autogain']) if self.config['dereverb'] == 0: self['ConfigDereverb'].set_active(False) self['ConfigDereverbValue'].set_sensitive(False) self['ConfigDereverbValue'].set_value(0.0) self['ConfigDereverbDecay'].set_sensitive(False) self['ConfigDereverbDecay'].set_value(0.0) self['ConfigDereverbValueLabel'].set_text('') self['ConfigDereverbDecayLabel'].set_text('') else: self['ConfigDereverb'].set_active(True) self['ConfigDereverbValue'].set_sensitive(True) self['ConfigDereverbValue'].set_value( self.config['dereverb']) self['ConfigDereverbDecay'].set_sensitive(True) self['ConfigDereverbDecay'].set_value( self.config['dereverbdecay']) cmd = self.command_audio_attach_acmd if cmd: self['ConfigAudioAttachCommand'].set_text(cmd) cmd = self.command_audio_attach_dcmd if cmd: self['ConfigAudioDetachCommand'].set_text(cmd) cmd = self.default_ringtone if cmd: self['ConfigRingerFile'].set_filename(cmd) self.config_savecfg = savecfg self.topnotebook_set(self['ConfigTab']) def config_close(self): self['ConfigFeedbackTest'].set_active(False) del self.config if hasattr(self, 'loopback_active'): del self.loopback_active if hasattr(self, 'config_savecfg'): del self.config_savecfg self.topnotebook_set(None) def config_ok_clicked(self, widget): try: self.config_save_vals() except dbus.exceptions.DBusException, (ex): self.error_message(str(ex)) self.config_close() def config_cancel_clicked(self, widget): # Restore the save values try: self.config_restore_vals(self.config_savecfg) except dbus.exceptions.DBusException, (ex): self.error_message(str(ex)) self.config_close() def config_init_clicked(self, widget): self.initconf_open() def config_devices_clicked(self, widget): self.devices_open() def config_get_vals(self): spr = self.soundio_props.GetAll( self.HFPD_SOUNDIO_INTERFACE_NAME) hfpr = self.hfpd_props.GetAll( self.HFPD_HANDSFREE_INTERFACE_NAME) vals = dict() vals['secmode'] = int(hfpr['SecMode']) vals['acceptunknown'] = hfpr['AcceptUnknown'] vals['voicepersist'] = hfpr['VoicePersist'] vals['drivers'] = [] vals['driver'] = 0 index = 0 for x in spr['Drivers']: vals['drivers'].append(str(x[0])) if (vals['drivers'][index].lower() == str(spr['DriverName']).lower()): vals['driver'] = index index += 1 vals['driveropts'] = str(spr['DriverOpts']) vals['packetinterval'] = int(spr['PacketIntervalHint']) vals['jitterwindow'] = spr['JitterWindowHint'] vals['minbufferfill'] = spr['MinBufferFillHint'] vals['denoise'] = spr['Denoise'] vals['echocancel'] = int(spr['EchoCancelTail']) vals['autogain'] = int(spr['AutoGain']) / 1000 vals['dereverb'] = float(spr['DereverbLevel']) vals['dereverbdecay'] = float(spr['DereverbDecay']) vals['ringer'] = '' return vals def config_save_vals(self): self['ConfigFeedbackTest'].set_active(False) if self['ConfigSecNone'].get_active(): secmode = 0 elif self['ConfigSecAuth'].get_active(): secmode = 1 else: secmode = 2 self.setprop_hfpd('SecMode', dbus.Byte(secmode)) self.setprop_hfpd('AcceptUnknown', dbus.Boolean( self['ConfigAcceptUnknown'].get_active())) self.setprop_hfpd('VoicePersist', dbus.Boolean( self['ConfigVoicePersist'].get_active())) self.soundio.SetDriver(self['ConfigDriver'].get_active_text(), self['ConfigDriverDevList'].child. get_text()) self.hfpd.SaveSettings() cmd = self['ConfigAudioAttachCommand'].get_text() self.command_audio_attach_acmd = cmd self.configfile.set('options', 'attach_command', cmd) cmd = self['ConfigAudioDetachCommand'].get_text() self.command_audio_attach_dcmd = cmd self.configfile.set('options', 'detach_command', cmd) cmd = self['ConfigRingerFile'].get_filename() if self.default_ringtone != cmd: self.default_ringtone = cmd self.configfile.set('options', 'default_ringtone', cmd) self.configfile.write(open(self.configfile_name, 'w+')) def config_restore_vals(self, vals): self['ConfigFeedbackTest'].set_active(False) self.soundio.SetDriver(self.config['drivers'][ self.config['driver']], self.config['driveropts']) self.setprop_soundio('PacketIntervalHint', dbus.UInt32(vals['packetinterval'])) self.setprop_soundio('JitterWindowHint', dbus.UInt32(vals['jitterwindow'])) self.setprop_soundio('MinBufferFillHint', dbus.UInt32(vals['minbufferfill'])) self.setprop_soundio('Denoise', dbus.Boolean(vals['denoise'])) self.setprop_soundio('EchoCancelTail', dbus.UInt32(vals['echocancel'])) self.setprop_soundio('AutoGain', dbus.UInt32(vals['autogain'] * 1000)) self.setprop_soundio('DereverbLevel', dbus.Double(vals['dereverb'])) self.setprop_soundio('DereverbDecay', dbus.Double(vals['dereverbdecay'])) def config_driver_changed(self, widget): # Ask for the device list model = self['ConfigDriverDevList'].get_model() model.clear() drivername = self['ConfigDriver'].get_active_text() devlist = None if not drivername: return try: devlist = self.soundio.ProbeDevices(drivername) except dbus.exceptions.DBusException, (ex): print _('Device probe failed: %s') % str(ex) if devlist: for x in devlist: model.append([x[0], x[1]]) def constrain(self, val, min, max): if val < min: val = min elif val > max: val = max return val def value_transform(self, val, exp, minv, maxv): val = self.constrain(val, 0.0, 1.0) return int(minv + (pow(val, exp) * (maxv - minv))) def value_untransform(self, val, exp, minv, maxv): val = self.constrain(val, minv, maxv) return pow(float(val - minv) / (maxv - minv), 1.0 / exp) def format_ms(self, value): return _('%dms') % value def config_packet_interval(self, value): self.nested = True if value != 0: value = self.constrain(value, 5, 1000) self['ConfigPacketIntervalHint'].set_active(True) self['ConfigPacketIntervalLabel'].set_text( self.format_ms(value)) self['ConfigPacketInterval'].set_sensitive(True) self['ConfigPacketInterval'].set_value( self.value_untransform(value, 3, 5, 1000)) else: value = 0 self['ConfigPacketIntervalHint'].set_active(False) self['ConfigPacketIntervalLabel'].set_text('') self['ConfigPacketInterval'].set_sensitive(False) self['ConfigPacketInterval'].set_value(0) if self.config['packetinterval'] != value: self.config['packetinterval'] = value self.setprop_soundio('PacketIntervalHint', dbus.UInt32(value)) self.nested = False def config_packet_interval_toggled(self, widget): if widget.get_active(): value = self.config['packetintervalsave'] value = self.constrain(value, 5, 1000) else: self.config['packetintervalsave'] = ( self.config['packetinterval']) value = 0 if not self.nested: self.config_packet_interval(value) def config_packet_interval_changed(self, widget): value = self.value_transform(widget.get_value(), 3, 5, 1000) if not self.nested: self.config_packet_interval(value) def config_jitter_window(self, value): self.nested = True if value != 0: value = self.constrain(value, 5, 1000) self['ConfigJitterWindowHint'].set_active(True) self['ConfigJitterWindowLabel'].set_text( self.format_ms(value)) self['ConfigJitterWindow'].set_sensitive(True) self['ConfigJitterWindow'].set_value( self.value_untransform(value, 3, 5, 1000)) else: value = 0 self['ConfigJitterWindowHint'].set_active(False) self['ConfigJitterWindowLabel'].set_text('') self['ConfigJitterWindow'].set_sensitive(False) self['ConfigJitterWindow'].set_value(0) if self.config['jitterwindow'] != value: self.config['jitterwindow'] = value self.setprop_soundio('JitterWindowHint', dbus.UInt32(value)) self.nested = False def config_jitter_window_toggled(self, widget): if widget.get_active(): value = self.config['jitterwindowsave'] value = self.constrain(value, 5, 1000) else: self.config['jitterwindowsave'] = ( self.config['jitterwindow']) value = 0 if not self.nested: self.config_jitter_window(value) def config_jitter_window_changed(self, widget): value = self.value_transform(widget.get_value(), 3, 5, 1000) if not self.nested: self.config_jitter_window(value) def config_min_out_buffer(self, value): self.nested = True if value != 0: value = self.constrain(value, 5, 1000) self['ConfigMinOutBufferHint'].set_active(True) self['ConfigMinOutBufferLabel'].set_text( self.format_ms(value)) self['ConfigMinOutBuffer'].set_sensitive(True) self['ConfigMinOutBuffer'].set_value( self.value_untransform(value, 3, 5, 1000)) else: value = 0 self['ConfigMinOutBufferHint'].set_active(False) self['ConfigMinOutBufferLabel'].set_text('') self['ConfigMinOutBuffer'].set_sensitive(False) self['ConfigMinOutBuffer'].set_value(0) if self.config['minbufferfill'] != value: self.config['minbufferfill'] = value self.setprop_soundio('MinBufferFillHint', dbus.UInt32(value)) self.nested = False def config_min_out_buffer_toggled(self, widget): if widget.get_active(): value = self.config['minbufferfillsave'] value = self.constrain(value, 5, 1000) else: self.config['minbufferfillsave'] = ( self.config['minbufferfill']) value = 0 if not self.nested: self.config_min_out_buffer(value) def config_min_out_buffer_changed(self, widget): value = self.value_transform(widget.get_value(), 3, 5, 1000) if not self.nested: self.config_min_out_buffer(value) def config_loopback_toggled(self, widget): if not hasattr(self, 'loopback_active'): self.loopback_active = False if widget.get_active(): if not self.loopback_active: try: self.soundio.SetDriver(self[ 'ConfigDriver']. get_active_text(), self['ConfigDriverDevList'].child. get_text()) except: widget.set_active(False) self.error_message(_('Cannot Apply ' 'Sound Driver Settings')) return try: if self['Mute'].get_active(): self.setprop_soundio( 'Mute', False) self.soundio.LoopbackStart() except dbus.exceptions.DBusException, (ex): widget.set_active(False) self.error_message(_('Could not open ' 'Audio Device:\n' '%s') % str(ex)) return self.loopback_active = True self.soundio_skew_monitor = ( self.config_skew_notify) pkt = self.getprop_soundio('PacketInterval') self['ConfigRealPacketSizeLabel'].set_text( self.format_ms(pkt)) else: if self.loopback_active: self.soundio.Stop() self.soundio_skew_monitor = None self.config_skew_clear(True) self.loopback_active = False self['ConfigRealPacketSizeLabel'].set_text('') def config_skew_clear(self, do_hide): if do_hide: self['ConfigSkewNotice'].hide() self['ConfigSkewNoticeLabel'].hide() self['ConfigSkewNoticeLabel'].set_text('') if hasattr(self, 'config_skew_timeout'): gobject.source_remove(self.config_skew_timeout) del self.config_skew_timeout return False def config_skew_notify(self, skewtype, count): timeout = 1500 if skewtype == 0: msg = _('Sound Card Failure') timeout = 5000 elif skewtype == 1: msg = _('Sound Card Overrun/Underrun: %d/sec') % count elif skewtype == 2: if count < 0: submsg = _('(Playback Slower)') count = -count else: submsg = _('(Capture Slower)') msg = ((_('Sound Card Capture/Playback Skew: ' '%1.3f%%') % count) + '\n' + submsg) else: return self.config_skew_clear(False) self['ConfigSkewNotice'].show() self['ConfigSkewNoticeLabel'].show() self['ConfigSkewNoticeLabel'].set_text(msg) self.config_skew_timeout = gobject.timeout_add( timeout, self.config_skew_clear, True) def config_denoise_changed(self, widget): value = widget.get_active() if bool(self.config['denoise']) != value: self.config['denoise'] = value self.setprop_soundio('Denoise', dbus.Boolean(value)) def config_echo_cancel(self, value): self.nested = True if value != 0: value = self.constrain(value, 5, 1000) self['ConfigEchoCancel'].set_active(True) self['ConfigEchoCancelTailLabel'].set_text( self.format_ms(value)) self['ConfigEchoCancelTail'].set_sensitive(True) self['ConfigEchoCancelTail'].set_value( self.value_untransform(value, 3, 5, 1000)) else: value = 0 self['ConfigEchoCancel'].set_active(False) self['ConfigEchoCancelTailLabel'].set_text('') self['ConfigEchoCancelTail'].set_sensitive(False) self['ConfigEchoCancelTail'].set_value(0) if self.config['echocancel'] != value: self.config['echocancel'] = value self.setprop_soundio('EchoCancelTail', dbus.UInt32(value)) self.nested = False def config_echo_cancel_changed(self, widget): if widget.get_active(): value = self.config['echocancelsave'] value = self.constrain(value, 5, 1000) else: self.config['echocancelsave'] = ( self.config['echocancel']) value = 0 if not self.nested: self.config_echo_cancel(value) def config_echo_cancel_tail_changed(self, widget): value = self.value_transform(widget.get_value(), 3, 5, 1000) if not self.nested: self.config_echo_cancel(value) def config_auto_gain_changed(self, widget): value = int(widget.get_value()) self['ConfigAutoGainLabel'].set_text(str(value)) if self.config['autogain'] != value: self.config['autogain'] = value self.setprop_soundio('AutoGain', dbus.UInt32(value * 1000)) def config_dereverb_toggled(self, widget): value = widget.get_active() self['ConfigDereverbValue'].set_sensitive(value) self['ConfigDereverbDecay'].set_sensitive(value) if value: self['ConfigDereverbValue'].set_value( self.config['dereverbsave']) self.config_dereverb_value_changed( self['ConfigDereverbValue']) self['ConfigDereverbDecay'].set_value( self.config['dereverbdecaysave']) self.config_dereverb_decay_changed( self['ConfigDereverbDecay']) else: self.config['dereverbsave'] = self.config['dereverb'] self['ConfigDereverbValue'].set_value(0.0) self['ConfigDereverbValueLabel'].set_text('') self.config['dereverb'] = 0.0 self.config['dereverbdecaysave'] = self.config[ 'dereverbdecay'] self['ConfigDereverbDecay'].set_value(0.0) self['ConfigDereverbDecayLabel'].set_text('') def config_dereverb_value_changed(self, widget): value = widget.get_value() self['ConfigDereverbValueLabel'].set_text('%0.2f' % value) if self.config['dereverb'] != value: self.config['dereverb'] = value self.setprop_soundio('DereverbLevel', dbus.Double(value)) def config_dereverb_decay_changed(self, widget): value = widget.get_value() self['ConfigDereverbDecayLabel'].set_text('%0.2f' % value) if self.config['dereverbdecay'] != value: self.config['dereverbdecay'] = value self.setprop_soundio('DereverbDecay', dbus.Double(value)) def config_ringer_file_set(self, widget): pass def config_dsptest_pageid(self, widget): return widget.get_current_page() def config_dsptest_open(self, widget): self['ConfigFeedbackTest'].set_active(False) try: self.soundio.SetDriver(self['ConfigDriver']. get_active_text(), self['ConfigDriverDevList'].child. get_text()) except: self.error_message(_('Cannot Apply Sound Driver ' 'Settings')) return self['DspTestNotebook'].set_current_page(1) self['DspTestNotebook'].set_current_page(0) self.topnotebook_set(self['DspTestTab']) self['DspTestForward'].grab_focus() def connect_soundmonitor(self, to): if self.soundmonitor: self.soundmonitor.remove() self.soundmonitor = None if to: self.soundmonitor = self.soundio.connect_to_signal( "MonitorNotify", to) def config_dsptest_mon1(self, pos, amp): self['DspTestRecPosition1'].set_fraction(float(pos)/80000) self['DspTestMicVolume1'].set_fraction( pow(float(amp) / 65535, 0.33)) if amp > self.maxvol: self.maxvol = amp def config_dsptest_mon2(self, pos, amp): self['DspTestRecPosition2'].set_fraction(float(pos)/80000) self['DspTestMicVolume2'].set_fraction( pow(float(amp) / 65535, 0.33)) def config_dsptest_mon3(self, pos, amp): self['DspTestPlayPosition'].set_fraction(float(pos)/80000) def config_dsptest_set_buttons(self, back, fwd): self['DspTestBack'].set_sensitive(back) self['DspTestForward'].set_sensitive(fwd) def config_dsptest_back(self, widget): self['DspTestNotebook'].prev_page() def config_dsptest_forward(self, widget): self['DspTestNotebook'].next_page() def config_dsptest_setup(self, pageid): if not hasattr(self, 'membuf_active'): self.membuf_active = False if self.membuf_active: self.soundio.Stop() if pageid == 0: self.maxvol = 0 self.config_dsptest_set_buttons(False, True) elif pageid == 1: self.config_dsptest_set_buttons(True, False) self['DspTestRecPosition1'].set_fraction(0.0) self.connect_soundmonitor(self.config_dsptest_mon1) self['DspTestRecStart1'].set_active(True) elif pageid == 2: self.config_dsptest_set_buttons(True, True) self['DspTestRecPosition2'].set_fraction(0.0) self.connect_soundmonitor(self.config_dsptest_mon2) elif pageid == 3: self.config_dsptest_set_buttons(True, True) self['DspTestPlayPosition'].set_fraction(0.0) self.connect_soundmonitor(self.config_dsptest_mon3) self['DspTestPlayStart'].set_active(True) else: self.config_dsptest_set_buttons(True, False) def config_dsptest_close(self, widget): self['DspTestRecStart1'].set_active(False) self['DspTestRecStart2'].set_active(False) self['DspTestPlayStart'].set_active(False) self.topnotebook_set(None) if hasattr(self, 'membuf_active'): if self.membuf_active: self.soundio.Stop() del self.membuf_active self.connect_soundmonitor(None) if hasattr(self, 'maxvol'): del self.maxvol def config_dsptest_switch_page(self, widget, page, page_num): self.config_dsptest_setup(page_num) def config_dsptest_xxx_toggled(self, widget, page, doin, doout): if widget.get_active(): if doin and not doout: self.soundio.MembufClear() try: # 8KHz sample rate is standard # We request a 10 second, 80Ksamp buffer, # and 16 updates/sec. self.soundio.MembufStart(doin, doout, 80000, 500) except: widget.set_active(False) self.error_message(_('Could not open ' 'Audio Device')) return self.membuf_active = True self.config_dsptest_set_buttons(True, True) return if self.membuf_active: try: self.soundio.Stop() except: pass self.membuf_active = False def config_dsptest_recstart1_toggled(self, widget): self.config_dsptest_xxx_toggled(widget, self['DspTestPage1'], True, False) def config_dsptest_recstart2_toggled(self, widget): self.config_dsptest_xxx_toggled(widget, self['DspTestPage2'], True, True) def config_dsptest_playstart_toggled(self, widget): self.config_dsptest_xxx_toggled(widget, self['DspTestPage3'], False, True) def soundio_state_changed(self, state): agpath = self.getprop_soundio('AudioGateway') self.soundstate = int(state) if self.soundstate == 2: if hasattr(self, 'ag_play_target'): del self.ag_play_target self.command_audio_attach(False) if (hasattr(self, 'loopback_active') and self.loopback_active): self.loopback_active = False self['ConfigFeedbackTest'].set_active(False) self['ConfigRealPacketSizeLabel'].set_text('') if (hasattr(self, 'membuf_active') and self.membuf_active): self.membuf_active = False self['DspTestRecStart1'].set_active(False) self['DspTestRecStart2'].set_active(False) self['DspTestPlayStart'].set_active(False) self['DspTestMicVolume1'].set_fraction(0.0) self['DspTestMicVolume2'].set_fraction(0.0) self.ag_push_audio() if self.soundstate == 3 or self.soundstate == 4: agpath = self.getprop_soundio('AudioGateway') if agpath: ag = self.ags[agpath] if ag: ag.soundio_state_changed() def soundio_stream_aborted(self, excode, descr): if (excode == 'net.sf.nohands.hfpd.Error.SoundIoSoundCardFailed'): self.soundio_status_msg( _('Primary Sound Card Failure'), 5000) if self.soundio_skew_monitor: self.soundio_skew_monitor(0, 0) self.soundio_state_changed(2) def soundio_mute_changed(self, state): self['Mute'].set_active(bool(state)) def mute_toggled(self, but): self.setprop_soundio('Mute', but.get_active()) def soundio_status_msg_clear(self): if hasattr(self, 'soundio_status_msgid'): self['StatusBar'].remove(self.soundio_status_ctx, self.soundio_status_msgid) del self.soundio_status_msgid if hasattr(self, 'soundio_status_timeout'): gobject.source_remove(self.soundio_status_timeout) del self.soundio_status_timeout if hasattr(self, 'soundio_skew_type'): del self.soundio_skew_type return False def soundio_status_msg(self, msg, timeout): self.soundio_status_msg_clear() self.soundio_status_msgid = self['StatusBar'].push( self.soundio_status_ctx, msg) if timeout: self.soundio_status_timeout = gobject.timeout_add( timeout, self.soundio_status_msg_clear) def soundio_skew_notify(self, skewtype, count): if (hasattr(self, 'soundio_skew_type') and self.soundio_skew_type < skewtype): return if skewtype == 1: msg = _('Sound Card Overrun/Underrun') elif skewtype == 2: msg = _('Sound Card Playback/Capture Clock Skew') elif skewtype == 3: msg = _('Severe Bluetooth Playback/Capture Clock Skew') elif skewtype == 4: msg = _('Severe Sound Card / Bluetooth Clock Skew') else: return self.soundio_status_msg(msg, 5000) self.soundio_skew_type = skewtype if self.soundio_skew_monitor: self.soundio_skew_monitor(skewtype, count) def initconf_open(self): self['InitConfNotebook'].set_current_page(1) self['InitConfNotebook'].set_current_page(0) self.topnotebook_set(self['InitConfTab']) self['InitConfForward'].grab_focus() def initconf_close(self): self.topnotebook_set(None) def initconf_set_buttons(self, back, fwd): self['InitConfBack'].set_sensitive(back) self['InitConfForward'].set_sensitive(fwd) def initconf_done_clicked(self, widget): self.initconf_close() def initconf_back_clicked(self, widget): self['InitConfNotebook'].prev_page() def initconf_forward_clicked(self, widget): self['InitConfNotebook'].next_page() def initconf_switch_page(self, widget, page, page_num): if page_num == 0: self.initconf_set_buttons(False, True) elif page_num == 1: self.initconf_set_buttons(True, False) if __name__ == "__main__": hwg = HfConsole() hwg.Start() gtk.main()