summaryrefslogtreecommitdiff
path: root/data/hfconsole.in
diff options
context:
space:
mode:
authorsamr7 <samr7@126591fb-c623-4b62-a76d-97a8e4f34109>2009-01-09 10:10:52 +0000
committersamr7 <samr7@126591fb-c623-4b62-a76d-97a8e4f34109>2009-01-09 10:10:52 +0000
commit3b1c199304d10df53db10ffe974976796eeced81 (patch)
tree74dad0dff1b5b4c6f728cd2bf46109a57b1299a8 /data/hfconsole.in
parent9ac76264349a78631c0dfb4f1a0c48d7c9c58318 (diff)
downloadnohands-3b1c199304d10df53db10ffe974976796eeced81.tar.gz
Checkpoint of large change set, including:
- Minor HFP state machine overhaul - Move auto-reconnect handling to RfcommService/Session - Add timeouts for RFCOMM connection and HFP commands - Support CNUM, CLCC, and COPS commands in libhfp/hfpd - CmdCallDropActive -> CmdCallDropIndex - Add CallDropIndex() and CallPrivateConsult() to hfpd - Support voice recognition activation in libhfp/hfpd/hfconsole - Propagate in-band ring tone state to hfpd/hfconsole - Add '+' international prefix symbol to hfconsole keypad git-svn-id: http://nohands.svn.sourceforge.net/svnroot/nohands/trunk@80 126591fb-c623-4b62-a76d-97a8e4f34109
Diffstat (limited to 'data/hfconsole.in')
-rwxr-xr-xdata/hfconsole.in592
1 files changed, 550 insertions, 42 deletions
diff --git a/data/hfconsole.in b/data/hfconsole.in
index 4573982..7977ea9 100755
--- a/data/hfconsole.in
+++ b/data/hfconsole.in
@@ -15,6 +15,7 @@ pygtk.require("2.0")
import gtk
import gtk.glade
import gobject
+import pango
import gettext
_ = gettext.gettext
@@ -47,7 +48,10 @@ class GtkAlerter:
self['CallAlertReject'].show()
else:
self['CallAlertReject'].hide()
- if ag.can_drop_held_udub():
+ # 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()
@@ -114,21 +118,206 @@ class GtkAlerter:
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._tel_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
- pass
+ 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)
@@ -141,26 +330,83 @@ class VCardSet:
return c == ' ' or c == '\t'
def _issafe(self, c):
x = ord(c)
- return (self._iswsp(c)
- or x == 0x21 or
+ 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
+ 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):
- print "Name:%s groups:%s value:%s" % (name, groups, value)
- for param in params:
- print "\t%s=%s" % (param, params[param])
+ 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))
+ 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
@@ -185,7 +431,7 @@ class VCardSet:
self._parse_error(_("Empty content name"))
name = line[:sep]
line = line[sep:]
- return (groups, name, line)
+ return (groups, name.lower(), line)
def _parse_param_value(self, line):
sep = 0
if line[0] == '"':
@@ -240,36 +486,51 @@ class VCardSet:
self._parse_error(
_("Empty parameter name"))
name = line[:sep]
+ # vCard 2.1 hack
+ line = self._parse_skip_ws(line[sep:])
if x == '=':
- line = line[(sep + 1):]
+ # vCard 2.1 hack
+ line = self._parse_skip_ws(line[1:])
break
- line = line[sep:]
- return (name, values, line)
+ # 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):
- print "Line:%s" % 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'))
- self._process_line(name, groups, params, line[1:])
+ # 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]):
@@ -292,11 +553,32 @@ class VCardSet:
self._feed_line(x)
self._feed_done()
def import_file(self, path):
- f = open(path, 'r')
+ 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
@@ -322,6 +604,12 @@ class HfConsole:
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
@@ -355,6 +643,8 @@ class HfConsole:
"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,
@@ -362,10 +652,16 @@ class HfConsole:
"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,
@@ -402,6 +698,16 @@ class HfConsole:
"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,
@@ -491,12 +797,12 @@ class HfConsole:
self['DevicesList'].set_model(self.ag_list_detail)
tvcolumn = gtk.TreeViewColumn(_('Name'))
self['DevicesList'].append_column(tvcolumn)
- cell = gtk.CellRendererText()
+ 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 = gtk.CellRendererText()
+ cell = self.create_cellrenderertext()
tvcolumn.pack_start(cell, True)
tvcolumn.add_attribute(cell, 'text', 1)
self['DevicesList'].get_selection().connect('changed',
@@ -509,12 +815,12 @@ class HfConsole:
self['NewDeviceList'].set_model(self.ag_list_new)
tvcolumn = gtk.TreeViewColumn(_('Name'))
self['NewDeviceList'].append_column(tvcolumn)
- cell = gtk.CellRendererText()
+ 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 = gtk.CellRendererText()
+ cell = self.create_cellrenderertext()
tvcolumn.pack_start(cell, True)
tvcolumn.add_attribute(cell, 'text', 1)
self['NewDeviceList'].get_selection().connect('changed',
@@ -543,7 +849,7 @@ class HfConsole:
self['ScanResults'].set_model(self.scanresults)
tvcolumn = gtk.TreeViewColumn(_('Device'))
self['ScanResults'].append_column(tvcolumn)
- cell = gtk.CellRendererText()
+ cell = self.create_cellrenderertext()
tvcolumn.pack_start(cell, True)
tvcolumn.add_attribute(cell, 'text', 0)
self.scanselect = self['ScanResults'].get_selection()
@@ -555,7 +861,7 @@ class HfConsole:
self['HistoryResults'].set_model(self.historyresults)
tvcolumn = gtk.TreeViewColumn(_('Device'))
self['HistoryResults'].append_column(tvcolumn)
- cell = gtk.CellRendererText()
+ cell = self.create_cellrenderertext()
tvcolumn.pack_start(cell, True)
tvcolumn.add_attribute(cell, 'text', 0)
self.historyselect = self['HistoryResults'].get_selection()
@@ -574,12 +880,33 @@ class HfConsole:
self.nobt = False
self.nested = False
- self.frontpage = 0 #1
+ self.frontpage = 1
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
@@ -650,7 +977,7 @@ class HfConsole:
except:
v = 0
- my_version = 3
+ my_version = 4
if v < my_version:
self.fatal(_('Version mismatch with hfpd!\n'
'hfpd version: %(hfpdver)d\n'
@@ -694,6 +1021,12 @@ class HfConsole:
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",
@@ -779,6 +1112,8 @@ class HfConsole:
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
@@ -788,6 +1123,7 @@ class HfConsole:
self.reset_indicators()
self.autoreconnect = 1
self.autoreconnect_flag = False
+ self.normalizer = TelNormalizer()
self.state = 0
self.callstate = 0
@@ -826,6 +1162,14 @@ class HfConsole:
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')
@@ -835,6 +1179,10 @@ class HfConsole:
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)
@@ -872,6 +1220,18 @@ class HfConsole:
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):
@@ -965,6 +1325,11 @@ class HfConsole:
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:
@@ -1001,9 +1366,16 @@ class HfConsole:
if not self.alerter:
self.alerter = self.hfc.alerter_factory(self)
self.alerter.update_state()
- def ring_notify(self, caller_id, phbook_ent):
- self.caller_id = caller_id
- self.caller_phbook = phbook_ent
+ 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()
def waiting_caller_info(self):
return (self.caller_id, self.caller_phbook)
@@ -1451,19 +1823,47 @@ class HfConsole:
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
- text = self.digit_button_char(but)
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)
- entry = self['PhoneNumEntry']
- entry.set_text(entry.get_text() + text)
+ 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])
@@ -1474,12 +1874,15 @@ class HfConsole:
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
@@ -1504,9 +1907,9 @@ class HfConsole:
hold = ag.can_hold()
do_notebook = not ag.can_dial()
hangup = not dial
- if hold:
- swap = ag.has_waiting_call()
- hold = not swap
+ if hold and ag.has_waiting_call():
+ swap = True
+ hold = False
audio = True
if ag.has_audio():
audio_dn = True
@@ -1518,6 +1921,11 @@ class HfConsole:
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
@@ -1527,10 +1935,24 @@ class HfConsole:
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)
@@ -1540,10 +1962,13 @@ class HfConsole:
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)
@@ -1551,6 +1976,7 @@ class HfConsole:
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:
@@ -1558,14 +1984,32 @@ class HfConsole:
def bar_ag_dial_clicked(self, widget):
ag = self.selected_ag
- entry = self['PhoneNumEntry']
if not ag:
return
if not ag.can_dial():
return
- num = entry.get_text()
- entry.set_text('')
- ag.dial(num)
+ 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()
@@ -1581,13 +2025,15 @@ class HfConsole:
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
- if page == 1:
+ pass
+ elif page == 1:
dialpad_but = True
def set_vis(widget, visible):
@@ -1600,10 +2046,11 @@ class HfConsole:
def bar_dialpad_clicked(self, widget):
self.frontpage = 0
- self.frontnotebook_set(0)
+ self.ag_configure_gui(self.selected_ag)
+
def bar_contacts_clicked(self, widget):
self.frontpage = 1
- self.frontnotebook_set(1)
+ self.ag_configure_gui(self.selected_ag)
def bar_ag_hangup_clicked(self, widget):
ag = self.selected_ag
@@ -1638,8 +2085,68 @@ class HfConsole:
_('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,
@@ -2568,6 +3075,7 @@ class HfConsole:
self.initconf_set_buttons(False, True)
elif page_num == 1:
self.initconf_set_buttons(True, False)
+
if __name__ == "__main__":
hwg = HfConsole()
hwg.Start()