summaryrefslogtreecommitdiff
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
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
-rw-r--r--data/hfconsole.glade1492
-rwxr-xr-xdata/hfconsole.in592
-rw-r--r--data/hfpd-dbus-doc.cs473
-rw-r--r--hfpd/objects.cpp340
-rw-r--r--hfpd/objects.h34
-rw-r--r--include/libhfp/bt.h2
-rw-r--r--include/libhfp/hfp.h394
-rw-r--r--include/libhfp/rfcomm.h60
-rw-r--r--libhfp/hfp.cpp1210
-rw-r--r--libhfp/rfcomm.cpp301
10 files changed, 3592 insertions, 1306 deletions
diff --git a/data/hfconsole.glade b/data/hfconsole.glade
index 8df3a37..81dc971 100644
--- a/data/hfconsole.glade
+++ b/data/hfconsole.glade
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.4.5 on Tue Dec 23 12:42:43 2008 -->
+<!--Generated with glade3 3.4.5 on Fri Jan 9 01:42:09 2009 -->
<glade-interface>
<widget class="GtkWindow" id="MainWindow">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
@@ -68,7 +68,7 @@
<child>
<widget class="GtkImage" id="image22">
<property name="visible">True</property>
- <property name="tooltip" translatable="yes">Has Service</property>
+ <property name="tooltip" translatable="yes">Poor Reception</property>
<property name="pixbuf">signal-0.png</property>
</widget>
</child>
@@ -193,6 +193,7 @@
<child>
<widget class="GtkImage" id="image29">
<property name="visible">True</property>
+ <property name="tooltip" translatable="yes">No Phone</property>
<property name="pixbuf">noag.png</property>
</widget>
<packing>
@@ -208,6 +209,7 @@
<child>
<widget class="GtkImage" id="NoBluetoothIndicator">
<property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Bluetooth Unavailable</property>
<property name="pixbuf">nobt.png</property>
</widget>
<packing>
@@ -236,6 +238,7 @@
<child>
<widget class="GtkImage" id="image34">
<property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Low Battery</property>
<property name="pixbuf">battery-0.png</property>
</widget>
</child>
@@ -248,6 +251,7 @@
<child>
<widget class="GtkImage" id="image33">
<property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Battery Charge</property>
<property name="pixbuf">battery-1.png</property>
</widget>
<packing>
@@ -263,6 +267,7 @@
<child>
<widget class="GtkImage" id="image32">
<property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Battery Charge</property>
<property name="pixbuf">battery-2.png</property>
</widget>
<packing>
@@ -278,6 +283,7 @@
<child>
<widget class="GtkImage" id="image31">
<property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Battery Charge</property>
<property name="pixbuf">battery-3.png</property>
</widget>
<packing>
@@ -293,6 +299,7 @@
<child>
<widget class="GtkImage" id="image17">
<property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Battery Charge</property>
<property name="pixbuf">battery-4.png</property>
</widget>
<packing>
@@ -308,6 +315,7 @@
<child>
<widget class="GtkImage" id="image8">
<property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Battery Charge</property>
<property name="pixbuf">battery-5.png</property>
</widget>
<packing>
@@ -377,6 +385,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
+ <property name="tooltip" translatable="yes">Show Keypad</property>
<property name="focus_on_click">False</property>
<property name="response_id">0</property>
<signal name="clicked" handler="BarDialPad_clicked_cb"/>
@@ -393,6 +402,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
+ <property name="tooltip" translatable="yes">Show Contacts</property>
<property name="focus_on_click">False</property>
<property name="response_id">0</property>
<signal name="clicked" handler="BarContacts_clicked_cb"/>
@@ -518,6 +528,24 @@
<property name="position">7</property>
</packing>
</child>
+ <child>
+ <widget class="GtkToggleButton" id="AgVoiceRecogToggle">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Voice Recognition</property>
+ <property name="focus_on_click">False</property>
+ <property name="response_id">0</property>
+ <signal name="toggled" handler="AgVoiceRecogToggle_toggled_cb"/>
+ <child>
+ <widget class="GtkImage" id="image41">
+ <property name="visible">True</property>
+ <property name="pixbuf">vr.png</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">8</property>
+ </packing>
+ </child>
</widget>
<packing>
<property name="expand">False</property>
@@ -558,6 +586,7 @@
<property name="has_focus">True</property>
<property name="is_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <signal name="changed" handler="PhoneNumEntry_changed_cb"/>
</widget>
</child>
<child>
@@ -596,96 +625,100 @@
<property name="row_spacing">8</property>
<property name="homogeneous">True</property>
<child>
- <widget class="GtkButton" id="DigitButtonAst">
+ <widget class="GtkButton" id="DigitButton3">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="focus_on_click">False</property>
<property name="response_id">0</property>
- <signal name="pressed" handler="DigitButton_pressed_cb" object="*"/>
- <signal name="clicked" handler="DigitButton_clicked_cb" object="*"/>
+ <signal name="pressed" handler="DigitButton_pressed_cb" object="3"/>
+ <signal name="clicked" handler="DigitButton_clicked_cb" object="3"/>
<child>
- <widget class="GtkLabel" id="DigitLabelAst">
+ <widget class="GtkLabel" id="DigitLabel3">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;*&lt;/span&gt;
-</property>
+ <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;3&lt;/span&gt;
+def</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_CENTER</property>
</widget>
</child>
</widget>
<packing>
- <property name="top_attach">3</property>
- <property name="bottom_attach">4</property>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
<property name="y_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkButton" id="DigitButton7">
+ <widget class="GtkButton" id="DigitButton6">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="focus_on_click">False</property>
<property name="response_id">0</property>
- <signal name="pressed" handler="DigitButton_pressed_cb" object="7"/>
- <signal name="clicked" handler="DigitButton_clicked_cb" object="7"/>
+ <signal name="pressed" handler="DigitButton_pressed_cb" object="6"/>
+ <signal name="clicked" handler="DigitButton_clicked_cb" object="6"/>
<child>
- <widget class="GtkLabel" id="DigitLabel7">
+ <widget class="GtkLabel" id="DigitLabel6">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;7&lt;/span&gt;
-pqrs</property>
+ <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;6&lt;/span&gt;
+mno</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_CENTER</property>
</widget>
</child>
</widget>
<packing>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
<property name="y_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkButton" id="DigitButton4">
+ <widget class="GtkButton" id="DigitButton9">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="focus_on_click">False</property>
<property name="response_id">0</property>
- <signal name="pressed" handler="DigitButton_pressed_cb" object="4"/>
- <signal name="clicked" handler="DigitButton_clicked_cb" object="4"/>
+ <signal name="pressed" handler="DigitButton_pressed_cb" object="9"/>
+ <signal name="clicked" handler="DigitButton_clicked_cb" object="9"/>
<child>
- <widget class="GtkLabel" id="DigitLabel4">
+ <widget class="GtkLabel" id="DigitLabel9">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;4&lt;/span&gt;
-ghi</property>
+ <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;9&lt;/span&gt;
+wxyz</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_CENTER</property>
</widget>
</child>
</widget>
<packing>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
<property name="y_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkButton" id="DigitButton1">
+ <widget class="GtkButton" id="DigitButtonPnd">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="focus_on_click">False</property>
<property name="response_id">0</property>
- <signal name="pressed" handler="DigitButton_pressed_cb" object="1"/>
- <signal name="clicked" handler="DigitButton_clicked_cb" object="1"/>
+ <signal name="pressed" handler="DigitButton_pressed_cb" object="#"/>
+ <signal name="clicked" handler="DigitButton_clicked_cb" object="#"/>
<child>
- <widget class="GtkLabel" id="DigitLabel1">
+ <widget class="GtkLabel" id="DigitLabelPnd">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;1&lt;/span&gt;
+ <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;#&lt;/span&gt;
</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_CENTER</property>
@@ -693,24 +726,29 @@ ghi</property>
</child>
</widget>
<packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
<property name="y_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkButton" id="DigitButton2">
+ <widget class="GtkButton" id="DigitButton0">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="focus_on_click">False</property>
<property name="response_id">0</property>
- <signal name="pressed" handler="DigitButton_pressed_cb" object="2"/>
- <signal name="clicked" handler="DigitButton_clicked_cb" object="2"/>
+ <signal name="pressed" handler="DigitButton_pressed_cb" object="0"/>
+ <signal name="clicked" handler="DigitButton_clicked_cb" object="0"/>
+ <signal name="released" handler="DigitButton_released_cb"/>
<child>
- <widget class="GtkLabel" id="DigitLabel2">
+ <widget class="GtkLabel" id="DigitLabel0">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;2&lt;/span&gt;
-abc</property>
+ <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;0&lt;/span&gt;
++</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_CENTER</property>
</widget>
@@ -719,24 +757,26 @@ abc</property>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
<property name="y_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkButton" id="DigitButton5">
+ <widget class="GtkButton" id="DigitButton8">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="focus_on_click">False</property>
<property name="response_id">0</property>
- <signal name="pressed" handler="DigitButton_pressed_cb" object="5"/>
- <signal name="clicked" handler="DigitButton_clicked_cb" object="5"/>
+ <signal name="pressed" handler="DigitButton_pressed_cb" object="8"/>
+ <signal name="clicked" handler="DigitButton_clicked_cb" object="8"/>
<child>
- <widget class="GtkLabel" id="DigitLabel5">
+ <widget class="GtkLabel" id="DigitLabel8">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;5&lt;/span&gt;
-jkl</property>
+ <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;8&lt;/span&gt;
+tuv</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_CENTER</property>
</widget>
@@ -745,26 +785,26 @@ jkl</property>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
<property name="y_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkButton" id="DigitButton8">
+ <widget class="GtkButton" id="DigitButton5">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="focus_on_click">False</property>
<property name="response_id">0</property>
- <signal name="pressed" handler="DigitButton_pressed_cb" object="8"/>
- <signal name="clicked" handler="DigitButton_clicked_cb" object="8"/>
+ <signal name="pressed" handler="DigitButton_pressed_cb" object="5"/>
+ <signal name="clicked" handler="DigitButton_clicked_cb" object="5"/>
<child>
- <widget class="GtkLabel" id="DigitLabel8">
+ <widget class="GtkLabel" id="DigitLabel5">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;8&lt;/span&gt;
-tuv</property>
+ <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;5&lt;/span&gt;
+jkl</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_CENTER</property>
</widget>
@@ -773,26 +813,26 @@ tuv</property>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
<property name="y_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkButton" id="DigitButton0">
+ <widget class="GtkButton" id="DigitButton2">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="focus_on_click">False</property>
<property name="response_id">0</property>
- <signal name="pressed" handler="DigitButton_pressed_cb" object="0"/>
- <signal name="clicked" handler="DigitButton_clicked_cb" object="0"/>
+ <signal name="pressed" handler="DigitButton_pressed_cb" object="2"/>
+ <signal name="clicked" handler="DigitButton_clicked_cb" object="2"/>
<child>
- <widget class="GtkLabel" id="DigitLabel0">
+ <widget class="GtkLabel" id="DigitLabel2">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;0&lt;/span&gt;
-</property>
+ <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;2&lt;/span&gt;
+abc</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_CENTER</property>
</widget>
@@ -801,25 +841,23 @@ tuv</property>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">3</property>
- <property name="bottom_attach">4</property>
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
<property name="y_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkButton" id="DigitButtonPnd">
+ <widget class="GtkButton" id="DigitButton1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="focus_on_click">False</property>
<property name="response_id">0</property>
- <signal name="pressed" handler="DigitButton_pressed_cb" object="#"/>
- <signal name="clicked" handler="DigitButton_clicked_cb" object="#"/>
+ <signal name="pressed" handler="DigitButton_pressed_cb" object="1"/>
+ <signal name="clicked" handler="DigitButton_clicked_cb" object="1"/>
<child>
- <widget class="GtkLabel" id="DigitLabelPnd">
+ <widget class="GtkLabel" id="DigitLabel1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;#&lt;/span&gt;
+ <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;1&lt;/span&gt;
</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_CENTER</property>
@@ -827,92 +865,84 @@ tuv</property>
</child>
</widget>
<packing>
- <property name="left_attach">2</property>
- <property name="right_attach">3</property>
- <property name="top_attach">3</property>
- <property name="bottom_attach">4</property>
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
<property name="y_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkButton" id="DigitButton9">
+ <widget class="GtkButton" id="DigitButton4">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="focus_on_click">False</property>
<property name="response_id">0</property>
- <signal name="pressed" handler="DigitButton_pressed_cb" object="9"/>
- <signal name="clicked" handler="DigitButton_clicked_cb" object="9"/>
+ <signal name="pressed" handler="DigitButton_pressed_cb" object="4"/>
+ <signal name="clicked" handler="DigitButton_clicked_cb" object="4"/>
<child>
- <widget class="GtkLabel" id="DigitLabel9">
+ <widget class="GtkLabel" id="DigitLabel4">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;9&lt;/span&gt;
-wxyz</property>
+ <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;4&lt;/span&gt;
+ghi</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_CENTER</property>
</widget>
</child>
</widget>
<packing>
- <property name="left_attach">2</property>
- <property name="right_attach">3</property>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
<property name="y_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkButton" id="DigitButton6">
+ <widget class="GtkButton" id="DigitButton7">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="focus_on_click">False</property>
<property name="response_id">0</property>
- <signal name="pressed" handler="DigitButton_pressed_cb" object="6"/>
- <signal name="clicked" handler="DigitButton_clicked_cb" object="6"/>
+ <signal name="pressed" handler="DigitButton_pressed_cb" object="7"/>
+ <signal name="clicked" handler="DigitButton_clicked_cb" object="7"/>
<child>
- <widget class="GtkLabel" id="DigitLabel6">
+ <widget class="GtkLabel" id="DigitLabel7">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;6&lt;/span&gt;
-mno</property>
+ <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;7&lt;/span&gt;
+pqrs</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_CENTER</property>
</widget>
</child>
</widget>
<packing>
- <property name="left_attach">2</property>
- <property name="right_attach">3</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
<property name="y_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkButton" id="DigitButton3">
+ <widget class="GtkButton" id="DigitButtonAst">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="focus_on_click">False</property>
<property name="response_id">0</property>
- <signal name="pressed" handler="DigitButton_pressed_cb" object="3"/>
- <signal name="clicked" handler="DigitButton_clicked_cb" object="3"/>
+ <signal name="pressed" handler="DigitButton_pressed_cb" object="*"/>
+ <signal name="clicked" handler="DigitButton_clicked_cb" object="*"/>
<child>
- <widget class="GtkLabel" id="DigitLabel3">
+ <widget class="GtkLabel" id="DigitLabelAst">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;3&lt;/span&gt;
-def</property>
+ <property name="label" translatable="yes">&lt;span size="xx-large" weight="heavy"&gt;*&lt;/span&gt;
+</property>
<property name="use_markup">True</property>
<property name="justify">GTK_JUSTIFY_CENTER</property>
</widget>
</child>
</widget>
<packing>
- <property name="left_attach">2</property>
- <property name="right_attach">3</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
<property name="y_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
</packing>
@@ -981,6 +1011,7 @@ def</property>
<property name="label">gtk-delete</property>
<property name="use_stock">True</property>
<property name="response_id">0</property>
+ <signal name="clicked" handler="ContactsDelete_clicked_cb"/>
</widget>
</child>
<child>
@@ -991,6 +1022,7 @@ def</property>
<property name="label">gtk-new</property>
<property name="use_stock">True</property>
<property name="response_id">0</property>
+ <signal name="clicked" handler="ContactsNew_clicked_cb"/>
</widget>
<packing>
<property name="position">1</property>
@@ -1004,6 +1036,7 @@ def</property>
<property name="label">gtk-edit</property>
<property name="use_stock">True</property>
<property name="response_id">0</property>
+ <signal name="clicked" handler="ContactsEdit_clicked_cb"/>
</widget>
<packing>
<property name="position">2</property>
@@ -1080,42 +1113,42 @@ def</property>
</packing>
</child>
<child>
- <widget class="GtkToggleButton" id="RecordCall">
+ <widget class="GtkButton" id="ConfigOpen">
<property name="visible">True</property>
- <property name="receives_default">True</property>
- <property name="tooltip" translatable="yes">Record Call</property>
+ <property name="tooltip" translatable="yes">Open Preferences</property>
<property name="focus_on_click">False</property>
<property name="response_id">0</property>
+ <signal name="clicked" handler="ConfigOpen_clicked_cb"/>
<child>
- <widget class="GtkImage" id="image11">
+ <widget class="GtkImage" id="image9">
<property name="visible">True</property>
- <property name="stock">gtk-media-record</property>
+ <property name="stock">gtk-preferences</property>
<property name="icon_size">6</property>
</widget>
</child>
</widget>
<packing>
<property name="position">2</property>
+ <property name="secondary">True</property>
</packing>
</child>
<child>
- <widget class="GtkButton" id="ConfigOpen">
+ <widget class="GtkToggleButton" id="RecordCall">
<property name="visible">True</property>
- <property name="tooltip" translatable="yes">Open Preferences</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip" translatable="yes">Record Call</property>
<property name="focus_on_click">False</property>
<property name="response_id">0</property>
- <signal name="clicked" handler="ConfigOpen_clicked_cb"/>
<child>
- <widget class="GtkImage" id="image9">
+ <widget class="GtkImage" id="image11">
<property name="visible">True</property>
- <property name="stock">gtk-preferences</property>
+ <property name="stock">gtk-media-record</property>
<property name="icon_size">6</property>
</widget>
</child>
</widget>
<packing>
<property name="position">3</property>
- <property name="secondary">True</property>
</packing>
</child>
</widget>
@@ -1774,55 +1807,52 @@ At least one unknown device attempted to connect. Would you like to add a devic
<property name="column_spacing">8</property>
<property name="row_spacing">8</property>
<child>
- <widget class="GtkLabel" id="label4">
+ <widget class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">Mobile:</property>
+ <property name="label" translatable="yes">Name:</property>
</widget>
<packing>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkEntry" id="ContactMobile">
+ <widget class="GtkEntry" id="ContactName">
<property name="visible">True</property>
<property name="can_focus">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkEntry" id="ContactHome">
+ <widget class="GtkLabel" id="label44">
<property name="visible">True</property>
- <property name="can_focus">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;b&gt;Telephone Numbers&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
</widget>
<packing>
- <property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">3</property>
- <property name="bottom_attach">4</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label36">
+ <widget class="GtkLabel" id="label42">
<property name="visible">True</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">Home:</property>
+ <property name="label" translatable="yes">Work:</property>
</widget>
<packing>
- <property name="top_attach">3</property>
- <property name="bottom_attach">4</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@@ -1842,52 +1872,55 @@ At least one unknown device attempted to connect. Would you like to add a devic
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label42">
+ <widget class="GtkLabel" id="label36">
<property name="visible">True</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">Work:</property>
+ <property name="label" translatable="yes">Home:</property>
</widget>
<packing>
- <property name="top_attach">4</property>
- <property name="bottom_attach">5</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label44">
+ <widget class="GtkEntry" id="ContactHome">
<property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">&lt;b&gt;Telephone Numbers&lt;/b&gt;</property>
- <property name="use_markup">True</property>
+ <property name="can_focus">True</property>
</widget>
<packing>
+ <property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkEntry" id="ContactName">
+ <widget class="GtkEntry" id="ContactMobile">
<property name="visible">True</property>
<property name="can_focus">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label3">
+ <widget class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">Name:</property>
+ <property name="label" translatable="yes">Mobile:</property>
</widget>
<packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@@ -1991,12 +2024,13 @@ The contact you wish to call has multiple telephone numbers. Select the number
<property name="spacing">8</property>
<property name="layout_style">GTK_BUTTONBOX_START</property>
<child>
- <widget class="GtkButton" id="button1">
+ <widget class="GtkButton" id="ChooseNumberMobile">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="focus_on_click">False</property>
<property name="response_id">0</property>
+ <signal name="clicked" handler="ChooseNumberMobile_clicked_cb"/>
<child>
<widget class="GtkHBox" id="hbox18">
<property name="visible">True</property>
@@ -2013,12 +2047,25 @@ The contact you wish to call has multiple telephone numbers. Select the number
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label64">
+ <widget class="GtkVBox" id="vbox13">
<property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">&lt;b&gt;Mobile&lt;/b&gt;
-</property>
- <property name="use_markup">True</property>
+ <child>
+ <widget class="GtkLabel" id="label64">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;b&gt;Mobile&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="ChooseNumberMobileLabel">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
</widget>
<packing>
<property name="position">1</property>
@@ -2032,12 +2079,13 @@ The contact you wish to call has multiple telephone numbers. Select the number
</packing>
</child>
<child>
- <widget class="GtkButton" id="button2">
+ <widget class="GtkButton" id="ChooseNumberHome">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="focus_on_click">False</property>
<property name="response_id">0</property>
+ <signal name="clicked" handler="ChooseNumberHome_clicked_cb"/>
<child>
<widget class="GtkHBox" id="hbox19">
<property name="visible">True</property>
@@ -2054,12 +2102,25 @@ The contact you wish to call has multiple telephone numbers. Select the number
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label68">
+ <widget class="GtkVBox" id="vbox14">
<property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">&lt;b&gt;Home&lt;/b&gt;
-</property>
- <property name="use_markup">True</property>
+ <child>
+ <widget class="GtkLabel" id="label68">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;b&gt;Home&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="ChooseNumberHomeLabel">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
</widget>
<packing>
<property name="position">1</property>
@@ -2074,12 +2135,13 @@ The contact you wish to call has multiple telephone numbers. Select the number
</packing>
</child>
<child>
- <widget class="GtkButton" id="button3">
+ <widget class="GtkButton" id="ChooseNumberWork">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="focus_on_click">False</property>
<property name="response_id">0</property>
+ <signal name="clicked" handler="ChooseNumberWork_clicked_cb"/>
<child>
<widget class="GtkHBox" id="hbox20">
<property name="visible">True</property>
@@ -2096,12 +2158,25 @@ The contact you wish to call has multiple telephone numbers. Select the number
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label69">
+ <widget class="GtkVBox" id="vbox15">
<property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">&lt;b&gt;Work&lt;/b&gt;
-</property>
- <property name="use_markup">True</property>
+ <child>
+ <widget class="GtkLabel" id="label69">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;b&gt;Work&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="ChooseNumberWorkLabel">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
</widget>
<packing>
<property name="position">1</property>
@@ -2130,9 +2205,10 @@ The contact you wish to call has multiple telephone numbers. Select the number
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="label" translatable="yes">gtk-cancel</property>
+ <property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="response_id">0</property>
+ <signal name="clicked" handler="ChooseNumberCancel_clicked_cb"/>
</widget>
</child>
</widget>
@@ -2307,114 +2383,185 @@ The contact you wish to call has multiple telephone numbers. Select the number
<property name="column_spacing">8</property>
<property name="row_spacing">8</property>
<child>
- <widget class="GtkLabel" id="ConfigMinOutBufferLabel">
+ <widget class="GtkFrame" id="frame2">
<property name="visible">True</property>
- <property name="xalign">0</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label_xalign">0</property>
+ <child>
+ <widget class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="bottom_padding">8</property>
+ <property name="left_padding">12</property>
+ <child>
+ <widget class="GtkVBox" id="vbox10">
+ <property name="visible">True</property>
+ <property name="spacing">8</property>
+ <child>
+ <widget class="GtkHBox" id="hbox8">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="border_width">10</property>
+ <property name="spacing">10</property>
+ <child>
+ <widget class="GtkToggleButton" id="ConfigFeedbackTest">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Feedback Test</property>
+ <property name="response_id">0</property>
+ <signal name="toggled" handler="ConfigFeedbackTest_toggled_cb"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label21">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Real Packet Size:</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="ConfigRealPacketSizeLabel">
+ <property name="width_request">64</property>
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox111">
+ <property name="visible">True</property>
+ <property name="spacing">8</property>
+ <child>
+ <widget class="GtkImage" id="ConfigSkewNotice">
+ <property name="no_show_all">True</property>
+ <property name="stock">gtk-dialog-warning</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="ConfigSkewNoticeLabel">
+ <property name="no_show_all">True</property>
+ <property name="xalign">0</property>
+ <property name="wrap">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">4</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label20">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">&lt;b&gt;Test&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
</widget>
<packing>
- <property name="left_attach">2</property>
<property name="right_attach">3</property>
- <property name="top_attach">4</property>
- <property name="bottom_attach">5</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkHScale" id="ConfigMinOutBuffer">
+ <widget class="GtkComboBox" id="ConfigDriver">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="adjustment">1 0 1 0.01 0.10000000000000001 0</property>
- <property name="draw_value">False</property>
- <property name="value_pos">GTK_POS_RIGHT</property>
- <signal name="value_changed" handler="ConfigMinOutBuffer_value_changed_cb"/>
+ <property name="items" translatable="yes"></property>
+ <signal name="changed" handler="ConfigDriver_changed_cb"/>
</widget>
<packing>
<property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">4</property>
- <property name="bottom_attach">5</property>
- <property name="y_options"></property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="ConfigJitterWindowLabel">
- <property name="visible">True</property>
- <property name="xalign">0</property>
- </widget>
- <packing>
- <property name="left_attach">2</property>
<property name="right_attach">3</property>
- <property name="top_attach">3</property>
- <property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkCheckButton" id="ConfigJitterWindowHint">
+ <widget class="GtkLabel" id="label10">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="label" translatable="yes">Jitter Window</property>
<property name="xalign">0</property>
- <property name="response_id">0</property>
- <property name="draw_indicator">True</property>
- <signal name="toggled" handler="ConfigJitterWindowHint_toggled_cb"/>
+ <property name="label" translatable="yes">Device / Options</property>
</widget>
<packing>
- <property name="top_attach">3</property>
- <property name="bottom_attach">4</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkComboBoxEntry" id="ConfigDriverDevList">
+ <widget class="GtkHScale" id="ConfigJitterWindow">
<property name="visible">True</property>
- <child internal-child="entry">
- <widget class="GtkEntry" id="comboboxentry-entry2">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- </widget>
- </child>
+ <property name="can_focus">True</property>
+ <property name="adjustment">1 0 1 0.01 0.10000000000000001 0</property>
+ <property name="draw_value">False</property>
+ <property name="value_pos">GTK_POS_RIGHT</property>
+ <signal name="value_changed" handler="ConfigJitterWindow_value_changed_cb"/>
</widget>
<packing>
<property name="left_attach">1</property>
- <property name="right_attach">3</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="x_options">GTK_FILL</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkCheckButton" id="ConfigMinOutBufferHint">
+ <widget class="GtkLabel" id="label7">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="label" translatable="yes">Output Buffer</property>
<property name="xalign">0</property>
- <property name="response_id">0</property>
- <property name="draw_indicator">True</property>
- <signal name="toggled" handler="ConfigMinOutBufferHint_toggled_cb"/>
+ <property name="label" translatable="yes">Driver</property>
</widget>
<packing>
- <property name="top_attach">4</property>
- <property name="bottom_attach">5</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkCheckButton" id="ConfigPacketIntervalHint">
+ <widget class="GtkLabel" id="ConfigPacketIntervalLabel">
+ <property name="width_request">64</property>
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="label" translatable="yes">Packet Interval</property>
<property name="xalign">0</property>
- <property name="response_id">0</property>
- <property name="draw_indicator">True</property>
- <signal name="toggled" handler="ConfigPacketIntervalHint_toggled_cb"/>
</widget>
<packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
@@ -2442,14 +2589,16 @@ The contact you wish to call has multiple telephone numbers. Select the number
</packing>
</child>
<child>
- <widget class="GtkLabel" id="ConfigPacketIntervalLabel">
- <property name="width_request">64</property>
+ <widget class="GtkCheckButton" id="ConfigPacketIntervalHint">
<property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Packet Interval</property>
<property name="xalign">0</property>
+ <property name="response_id">0</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="ConfigPacketIntervalHint_toggled_cb"/>
</widget>
<packing>
- <property name="left_attach">2</property>
- <property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
@@ -2457,172 +2606,99 @@ The contact you wish to call has multiple telephone numbers. Select the number
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label7">
+ <widget class="GtkCheckButton" id="ConfigMinOutBufferHint">
<property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Output Buffer</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">Driver</property>
+ <property name="response_id">0</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="ConfigMinOutBufferHint_toggled_cb"/>
</widget>
<packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkHScale" id="ConfigJitterWindow">
+ <widget class="GtkComboBoxEntry" id="ConfigDriverDevList">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="adjustment">1 0 1 0.01 0.10000000000000001 0</property>
- <property name="draw_value">False</property>
- <property name="value_pos">GTK_POS_RIGHT</property>
- <signal name="value_changed" handler="ConfigJitterWindow_value_changed_cb"/>
+ <child internal-child="entry">
+ <widget class="GtkEntry" id="comboboxentry-entry2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </widget>
+ </child>
</widget>
<packing>
<property name="left_attach">1</property>
- <property name="right_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkCheckButton" id="ConfigJitterWindowHint">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Jitter Window</property>
+ <property name="xalign">0</property>
+ <property name="response_id">0</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="ConfigJitterWindowHint_toggled_cb"/>
+ </widget>
+ <packing>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label10">
+ <widget class="GtkLabel" id="ConfigJitterWindowLabel">
<property name="visible">True</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">Device / Options</property>
</widget>
<packing>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkComboBox" id="ConfigDriver">
+ <widget class="GtkHScale" id="ConfigMinOutBuffer">
<property name="visible">True</property>
- <property name="items" translatable="yes"></property>
- <signal name="changed" handler="ConfigDriver_changed_cb"/>
+ <property name="can_focus">True</property>
+ <property name="adjustment">1 0 1 0.01 0.10000000000000001 0</property>
+ <property name="draw_value">False</property>
+ <property name="value_pos">GTK_POS_RIGHT</property>
+ <signal name="value_changed" handler="ConfigMinOutBuffer_value_changed_cb"/>
</widget>
<packing>
<property name="left_attach">1</property>
- <property name="right_attach">3</property>
- <property name="x_options">GTK_FILL</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkFrame" id="frame2">
+ <widget class="GtkLabel" id="ConfigMinOutBufferLabel">
<property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label_xalign">0</property>
- <child>
- <widget class="GtkAlignment" id="alignment2">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="bottom_padding">8</property>
- <property name="left_padding">12</property>
- <child>
- <widget class="GtkVBox" id="vbox10">
- <property name="visible">True</property>
- <property name="spacing">8</property>
- <child>
- <widget class="GtkHBox" id="hbox8">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="border_width">10</property>
- <property name="spacing">10</property>
- <child>
- <widget class="GtkToggleButton" id="ConfigFeedbackTest">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">Feedback Test</property>
- <property name="response_id">0</property>
- <signal name="toggled" handler="ConfigFeedbackTest_toggled_cb"/>
- </widget>
- </child>
- <child>
- <widget class="GtkLabel" id="label21">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">Real Packet Size:</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="ConfigRealPacketSizeLabel">
- <property name="width_request">64</property>
- <property name="visible">True</property>
- <property name="xalign">0</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="position">2</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- </packing>
- </child>
- <child>
- <widget class="GtkHBox" id="hbox111">
- <property name="visible">True</property>
- <property name="spacing">8</property>
- <child>
- <widget class="GtkImage" id="ConfigSkewNotice">
- <property name="no_show_all">True</property>
- <property name="stock">gtk-dialog-warning</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="ConfigSkewNoticeLabel">
- <property name="no_show_all">True</property>
- <property name="xalign">0</property>
- <property name="wrap">True</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="padding">4</property>
- <property name="position">1</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </widget>
- </child>
- </widget>
- </child>
- <child>
- <widget class="GtkLabel" id="label20">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">&lt;b&gt;Test&lt;/b&gt;</property>
- <property name="use_markup">True</property>
- </widget>
- <packing>
- <property name="type">label_item</property>
- </packing>
- </child>
+ <property name="xalign">0</property>
</widget>
<packing>
+ <property name="left_attach">2</property>
<property name="right_attach">3</property>
- <property name="top_attach">5</property>
- <property name="bottom_attach">6</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
@@ -2661,87 +2737,91 @@ The contact you wish to call has multiple telephone numbers. Select the number
<property name="column_spacing">10</property>
<property name="row_spacing">10</property>
<child>
- <widget class="GtkLabel" id="ConfigDereverbDecayLabel">
+ <widget class="GtkLabel" id="ConfigEchoCancelTailLabel">
+ <property name="width_request">64</property>
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="xalign">0</property>
- <property name="label">0</property>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
- <property name="top_attach">3</property>
- <property name="bottom_attach">4</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="ConfigDereverbValueLabel">
+ <widget class="GtkHScale" id="ConfigEchoCancelTail">
<property name="visible">True</property>
+ <property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="xalign">0</property>
- <property name="label">0</property>
+ <property name="adjustment">0 0 1 0.01 0.10000000000000001 0</property>
+ <property name="draw_value">False</property>
+ <signal name="value_changed" handler="ConfigEchoCancelTail_value_changed_cb"/>
</widget>
<packing>
- <property name="left_attach">2</property>
- <property name="right_attach">3</property>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_FILL</property>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
</packing>
</child>
<child>
- <widget class="GtkCheckButton" id="ConfigDenoise">
+ <widget class="GtkCheckButton" id="ConfigEchoCancel">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">Noise Reduction</property>
- <property name="xalign">0</property>
+ <property name="label" translatable="yes">Echo Cancelation</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
- <signal name="toggled" handler="ConfigDenoise_toggled_cb"/>
+ <signal name="toggled" handler="ConfigEchoCancel_toggled_cb"/>
</widget>
<packing>
- <property name="right_attach">3</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label34">
+ <widget class="GtkLabel" id="ConfigAutoGainLabel">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">Auto Gain</property>
+ <property name="label">0</property>
</widget>
<packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
- <property name="y_options"></property>
+ <property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkCheckButton" id="ConfigDereverb">
+ <widget class="GtkHScale" id="ConfigAutoGain">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">Dereverb</property>
- <property name="response_id">0</property>
- <property name="draw_indicator">True</property>
- <signal name="toggled" handler="ConfigDereverb_toggled_cb"/>
+ <property name="adjustment">0 0 20 1 10 0</property>
+ <property name="digits">0</property>
+ <property name="draw_value">False</property>
+ <property name="value_pos">GTK_POS_RIGHT</property>
+ <signal name="value_changed" handler="ConfigAutoGain_value_changed_cb"/>
</widget>
<packing>
- <property name="top_attach">2</property>
- <property name="bottom_attach">4</property>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options"></property>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkHScale" id="ConfigDereverbValue">
+ <widget class="GtkHScale" id="ConfigDereverbDecay">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
@@ -2749,18 +2829,18 @@ The contact you wish to call has multiple telephone numbers. Select the number
<property name="digits">2</property>
<property name="draw_value">False</property>
<property name="value_pos">GTK_POS_RIGHT</property>
- <signal name="value_changed" handler="ConfigDereverbValue_value_changed_cb"/>
+ <signal name="value_changed" handler="ConfigDereverbDecay_value_changed_cb"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkHScale" id="ConfigDereverbDecay">
+ <widget class="GtkHScale" id="ConfigDereverbValue">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
@@ -2768,96 +2848,92 @@ The contact you wish to call has multiple telephone numbers. Select the number
<property name="digits">2</property>
<property name="draw_value">False</property>
<property name="value_pos">GTK_POS_RIGHT</property>
- <signal name="value_changed" handler="ConfigDereverbDecay_value_changed_cb"/>
+ <signal name="value_changed" handler="ConfigDereverbValue_value_changed_cb"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">3</property>
- <property name="bottom_attach">4</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkHScale" id="ConfigAutoGain">
+ <widget class="GtkCheckButton" id="ConfigDereverb">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="adjustment">0 0 20 1 10 0</property>
- <property name="digits">0</property>
- <property name="draw_value">False</property>
- <property name="value_pos">GTK_POS_RIGHT</property>
- <signal name="value_changed" handler="ConfigAutoGain_value_changed_cb"/>
+ <property name="label" translatable="yes">Dereverb</property>
+ <property name="response_id">0</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="ConfigDereverb_toggled_cb"/>
</widget>
<packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="y_options">GTK_FILL</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="ConfigAutoGainLabel">
+ <widget class="GtkLabel" id="label34">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="xalign">0</property>
- <property name="label">0</property>
+ <property name="label" translatable="yes">Auto Gain</property>
</widget>
<packing>
- <property name="left_attach">2</property>
- <property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_FILL</property>
+ <property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkCheckButton" id="ConfigEchoCancel">
+ <widget class="GtkCheckButton" id="ConfigDenoise">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">Echo Cancelation</property>
+ <property name="label" translatable="yes">Noise Reduction</property>
+ <property name="xalign">0</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
- <signal name="toggled" handler="ConfigEchoCancel_toggled_cb"/>
+ <signal name="toggled" handler="ConfigDenoise_toggled_cb"/>
</widget>
<packing>
- <property name="top_attach">4</property>
- <property name="bottom_attach">5</property>
+ <property name="right_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkHScale" id="ConfigEchoCancelTail">
+ <widget class="GtkLabel" id="ConfigDereverbValueLabel">
<property name="visible">True</property>
- <property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="adjustment">0 0 1 0.01 0.10000000000000001 0</property>
- <property name="draw_value">False</property>
- <signal name="value_changed" handler="ConfigEchoCancelTail_value_changed_cb"/>
+ <property name="xalign">0</property>
+ <property name="label">0</property>
</widget>
<packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">4</property>
- <property name="bottom_attach">5</property>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="ConfigEchoCancelTailLabel">
- <property name="width_request">64</property>
+ <widget class="GtkLabel" id="ConfigDereverbDecayLabel">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="xalign">0</property>
+ <property name="label">0</property>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
- <property name="top_attach">4</property>
- <property name="bottom_attach">5</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@@ -2914,40 +2990,25 @@ The contact you wish to call has multiple telephone numbers. Select the number
<property name="column_spacing">5</property>
<property name="row_spacing">5</property>
<child>
- <widget class="GtkEntry" id="ConfigAudioDetachCommand">
+ <widget class="GtkFileChooserButton" id="ConfigRingerFile">
<property name="visible">True</property>
- <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <signal name="file_set" handler="ConfigRingerFile_file_set_cb"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkEntry" id="ConfigAudioAttachCommand">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="y_options">GTK_FILL</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="label38">
+ <widget class="GtkLabel" id="label39">
<property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">Audio Detach Command:</property>
+ <property name="label" translatable="yes">Ringer Sound:</property>
</widget>
<packing>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
@@ -2966,26 +3027,41 @@ The contact you wish to call has multiple telephone numbers. Select the number
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label39">
+ <widget class="GtkLabel" id="label38">
<property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">Ringer Sound:</property>
+ <property name="label" translatable="yes">Audio Detach Command:</property>
</widget>
<packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkFileChooserButton" id="ConfigRingerFile">
+ <widget class="GtkEntry" id="ConfigAudioAttachCommand">
<property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <signal name="file_set" handler="ConfigRingerFile_file_set_cb"/>
+ <property name="can_focus">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="ConfigAudioDetachCommand">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
@@ -3105,52 +3181,57 @@ The contact you wish to call has multiple telephone numbers. Select the number
<property name="column_spacing">8</property>
<property name="row_spacing">8</property>
<child>
- <widget class="GtkLabel" id="label24">
+ <widget class="GtkLabel" id="label14">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">The first step will begin when you click &lt;i&gt;Forward&lt;/i&gt;.</property>
+ <property name="label" translatable="yes">&lt;b&gt;1. Record a sound clip of spoken language
+ &lt;i&gt;Try to leave pauses and quiet moments during
+ this clip.&lt;/i&gt;&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">4</property>
- <property name="bottom_attach">5</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="emptylabel2">
- <property name="visible">True</property>
- </widget>
- <packing>
- <property name="top_attach">2</property>
- <property name="bottom_attach">5</property>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_FILL</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="emptylabel">
+ <widget class="GtkLabel" id="label11">
<property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">To test your signal processing settings, we will perform
+three steps:</property>
+ <property name="use_markup">True</property>
</widget>
<packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
<property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_FILL</property>
+ <property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkArrow" id="arrow1">
+ <widget class="GtkLabel" id="label17">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;b&gt;2.&lt;/b&gt; Play back the sound clip while recording a second clip
+ &lt;i&gt;This will simulate a full duplex conversation, with the clip
+ recorded in step one as coming from the remote party.&lt;/i&gt;</property>
+ <property name="use_markup">True</property>
</widget>
<packing>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_FILL</property>
+ <property name="y_options"></property>
</packing>
</child>
<child>
@@ -3173,55 +3254,50 @@ The contact you wish to call has multiple telephone numbers. Select the number
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label17">
+ <widget class="GtkArrow" id="arrow1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">&lt;b&gt;2.&lt;/b&gt; Play back the sound clip while recording a second clip
- &lt;i&gt;This will simulate a full duplex conversation, with the clip
- recorded in step one as coming from the remote party.&lt;/i&gt;</property>
- <property name="use_markup">True</property>
</widget>
<packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
- <property name="y_options"></property>
+ <property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label11">
+ <widget class="GtkLabel" id="emptylabel">
<property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">To test your signal processing settings, we will perform
-three steps:</property>
- <property name="use_markup">True</property>
</widget>
<packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
<property name="x_options">GTK_FILL</property>
- <property name="y_options"></property>
+ <property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label14">
+ <widget class="GtkLabel" id="emptylabel2">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label24">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">&lt;b&gt;1. Record a sound clip of spoken language
- &lt;i&gt;Try to leave pauses and quiet moments during
- this clip.&lt;/i&gt;&lt;/b&gt;</property>
+ <property name="label" translatable="yes">The first step will begin when you click &lt;i&gt;Forward&lt;/i&gt;.</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
@@ -3248,51 +3324,68 @@ three steps:</property>
<property name="column_spacing">8</property>
<property name="row_spacing">8</property>
<child>
- <widget class="GtkLabel" id="label8">
+ <widget class="GtkToggleButton" id="DspTestRecStart1">
<property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes"> Click &lt;i&gt;Record&lt;/i&gt; to begin</property>
- <property name="use_markup">True</property>
- <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label">gtk-media-record</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">0</property>
+ <signal name="toggled" handler="DspTestRecStart1_toggled_cb"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="x_options"></property>
+ <property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkProgressBar" id="DspTestRecPosition1">
+ <widget class="GtkLabel" id="label12">
<property name="visible">True</property>
- <property name="show_text">True</property>
- <property name="text" translatable="yes">Record Progress</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;b&gt;1. Record a sound clip of spoken language
+ &lt;i&gt;Try to leave pauses and quiet moments during
+ this clip.&lt;/i&gt;&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">3</property>
- <property name="bottom_attach">4</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_FILL</property>
+ <property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="emptylabel4">
+ <widget class="GtkLabel" id="label22">
<property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">To test your signal processing settings, we will perform
+three steps:</property>
+ <property name="use_markup">True</property>
</widget>
<packing>
- <property name="top_attach">2</property>
- <property name="bottom_attach">6</property>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
<property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_FILL</property>
+ <property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="emptylabel1">
+ <widget class="GtkArrow" id="arrow2">
<property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
</widget>
<packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@@ -3312,70 +3405,53 @@ three steps:</property>
</packing>
</child>
<child>
- <widget class="GtkArrow" id="arrow2">
+ <widget class="GtkLabel" id="emptylabel1">
<property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
</widget>
<packing>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label22">
+ <widget class="GtkLabel" id="emptylabel4">
<property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">To test your signal processing settings, we will perform
-three steps:</property>
- <property name="use_markup">True</property>
</widget>
<packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">6</property>
<property name="x_options">GTK_FILL</property>
- <property name="y_options"></property>
+ <property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label12">
+ <widget class="GtkProgressBar" id="DspTestRecPosition1">
<property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">&lt;b&gt;1. Record a sound clip of spoken language
- &lt;i&gt;Try to leave pauses and quiet moments during
- this clip.&lt;/i&gt;&lt;/b&gt;</property>
- <property name="use_markup">True</property>
+ <property name="show_text">True</property>
+ <property name="text" translatable="yes">Record Progress</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
- <property name="y_options"></property>
+ <property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkToggleButton" id="DspTestRecStart1">
+ <widget class="GtkLabel" id="label8">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label">gtk-media-record</property>
- <property name="use_stock">True</property>
- <property name="response_id">0</property>
- <signal name="toggled" handler="DspTestRecStart1_toggled_cb"/>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes"> Click &lt;i&gt;Record&lt;/i&gt; to begin</property>
+ <property name="use_markup">True</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">5</property>
- <property name="bottom_attach">6</property>
- <property name="x_options"></property>
- <property name="y_options"></property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
</packing>
</child>
</widget>
@@ -3404,68 +3480,51 @@ three steps:</property>
<property name="column_spacing">8</property>
<property name="row_spacing">8</property>
<child>
- <widget class="GtkLabel" id="label9">
+ <widget class="GtkLabel" id="label26">
<property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">&lt;b&gt;2. Play back the sound clip while recording a second clip
- &lt;i&gt;This will simulate a full duplex conversation, with
- the clip recorded in step one as coming from the
- remote party.&lt;/i&gt;&lt;/b&gt;</property>
+ <property name="label" translatable="yes"> Click &lt;i&gt;Record&lt;/i&gt; to begin</property>
<property name="use_markup">True</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options"></property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
</packing>
</child>
<child>
- <widget class="GtkToggleButton" id="DspTestRecStart2">
+ <widget class="GtkProgressBar" id="DspTestRecPosition2">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label">gtk-media-record</property>
- <property name="use_stock">True</property>
- <property name="response_id">0</property>
- <signal name="toggled" handler="DspTestRecStart2_toggled_cb"/>
+ <property name="show_text">True</property>
+ <property name="text" translatable="yes">Record Progress</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">5</property>
- <property name="bottom_attach">6</property>
- <property name="x_options"></property>
- <property name="y_options"></property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label25">
+ <widget class="GtkLabel" id="emptylabel8">
<property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">You are currently on Step 2 of the digital signal processing test:</property>
- <property name="use_markup">True</property>
</widget>
<packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">6</property>
<property name="x_options">GTK_FILL</property>
- <property name="y_options"></property>
+ <property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkArrow" id="arrow4">
+ <widget class="GtkLabel" id="emptylabel7">
<property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
</widget>
<packing>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
@@ -3485,53 +3544,70 @@ three steps:</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="emptylabel7">
+ <widget class="GtkArrow" id="arrow4">
<property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
</widget>
<packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="emptylabel8">
+ <widget class="GtkLabel" id="label25">
<property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">You are currently on Step 2 of the digital signal processing test:</property>
+ <property name="use_markup">True</property>
</widget>
<packing>
- <property name="top_attach">2</property>
- <property name="bottom_attach">6</property>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
<property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_FILL</property>
+ <property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkProgressBar" id="DspTestRecPosition2">
+ <widget class="GtkToggleButton" id="DspTestRecStart2">
<property name="visible">True</property>
- <property name="show_text">True</property>
- <property name="text" translatable="yes">Record Progress</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label">gtk-media-record</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">0</property>
+ <signal name="toggled" handler="DspTestRecStart2_toggled_cb"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">3</property>
- <property name="bottom_attach">4</property>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_FILL</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="x_options"></property>
+ <property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label26">
+ <widget class="GtkLabel" id="label9">
<property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes"> Click &lt;i&gt;Record&lt;/i&gt; to begin</property>
+ <property name="label" translatable="yes">&lt;b&gt;2. Play back the sound clip while recording a second clip
+ &lt;i&gt;This will simulate a full duplex conversation, with
+ the clip recorded in step one as coming from the
+ remote party.&lt;/i&gt;&lt;/b&gt;</property>
<property name="use_markup">True</property>
- <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
</packing>
</child>
</widget>
@@ -3560,119 +3636,119 @@ three steps:</property>
<property name="column_spacing">8</property>
<property name="row_spacing">8</property>
<child>
- <widget class="GtkToggleButton" id="DspTestPlayStart">
+ <widget class="GtkLabel" id="label23">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label">gtk-media-play</property>
- <property name="use_stock">True</property>
- <property name="response_id">0</property>
- <signal name="toggled" handler="DspTestPlayStart_toggled_cb"/>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;b&gt;3. Play back the second clip
+ &lt;i&gt;This is what the remote party would hear for the
+ conversation as simulated in step two.&lt;/i&gt;&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">4</property>
- <property name="bottom_attach">5</property>
- <property name="x_options"></property>
- <property name="y_options">GTK_EXPAND</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label16">
+ <widget class="GtkLabel" id="label13">
<property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">You are currently on Step 3 of the digital signal processing test:</property>
+ <property name="label" translatable="yes"> Click &lt;i&gt;Play&lt;/i&gt; to begin</property>
<property name="use_markup">True</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options"></property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
</packing>
</child>
<child>
- <widget class="GtkArrow" id="arrow3">
+ <widget class="GtkProgressBar" id="DspTestPlayPosition">
<property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="show_text">True</property>
+ <property name="text" translatable="yes">Playback Progress</property>
</widget>
<packing>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="emptylabel6">
+ <widget class="GtkLabel" id="emptylabel5">
<property name="visible">True</property>
</widget>
<packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">5</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="emptylabel5">
+ <widget class="GtkLabel" id="emptylabel6">
<property name="visible">True</property>
</widget>
<packing>
- <property name="top_attach">2</property>
- <property name="bottom_attach">5</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkProgressBar" id="DspTestPlayPosition">
+ <widget class="GtkArrow" id="arrow3">
<property name="visible">True</property>
- <property name="show_text">True</property>
- <property name="text" translatable="yes">Playback Progress</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
</widget>
<packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">3</property>
- <property name="bottom_attach">4</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label13">
+ <widget class="GtkLabel" id="label16">
<property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes"> Click &lt;i&gt;Play&lt;/i&gt; to begin</property>
+ <property name="label" translatable="yes">You are currently on Step 3 of the digital signal processing test:</property>
<property name="use_markup">True</property>
- <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label23">
+ <widget class="GtkToggleButton" id="DspTestPlayStart">
<property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">&lt;b&gt;3. Play back the second clip
- &lt;i&gt;This is what the remote party would hear for the
- conversation as simulated in step two.&lt;/i&gt;&lt;/b&gt;</property>
- <property name="use_markup">True</property>
+ <property name="label">gtk-media-play</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">0</property>
+ <signal name="toggled" handler="DspTestPlayStart_toggled_cb"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="x_options">GTK_FILL</property>
- <property name="y_options"></property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options"></property>
+ <property name="y_options">GTK_EXPAND</property>
</packing>
</child>
</widget>
@@ -3836,40 +3912,53 @@ three steps:</property>
<property name="column_spacing">8</property>
<property name="row_spacing">8</property>
<child>
- <widget class="GtkLabel" id="label52">
+ <widget class="GtkLabel" id="label56">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">The first step will begin when you click &lt;i&gt;Forward&lt;/i&gt;.</property>
+ <property name="label" translatable="yes">&lt;b&gt;1. Configure and test your local sound card&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">3</property>
- <property name="bottom_attach">4</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="emptylabel3">
+ <widget class="GtkLabel" id="label55">
<property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">To set up HFP for Linux for the first time, the following steps
+will be taken:</property>
+ <property name="use_markup">True</property>
</widget>
<packing>
- <property name="top_attach">2</property>
- <property name="bottom_attach">4</property>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
<property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_FILL</property>
+ <property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="emptylabel9">
+ <widget class="GtkLabel" id="label54">
<property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;b&gt;2.&lt;/b&gt; Configure your Bluetooth mobile phone</property>
+ <property name="use_markup">True</property>
</widget>
<packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_FILL</property>
+ <property name="y_options"></property>
</packing>
</child>
<child>
@@ -3885,51 +3974,38 @@ three steps:</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label54">
+ <widget class="GtkLabel" id="emptylabel9">
<property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">&lt;b&gt;2.&lt;/b&gt; Configure your Bluetooth mobile phone</property>
- <property name="use_markup">True</property>
</widget>
<packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
- <property name="y_options"></property>
+ <property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label55">
+ <widget class="GtkLabel" id="emptylabel3">
<property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">To set up HFP for Linux for the first time, the following steps
-will be taken:</property>
- <property name="use_markup">True</property>
</widget>
<packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
- <property name="y_options"></property>
+ <property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label56">
+ <widget class="GtkLabel" id="label52">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">&lt;b&gt;1. Configure and test your local sound card&lt;/b&gt;</property>
+ <property name="label" translatable="yes">The first step will begin when you click &lt;i&gt;Forward&lt;/i&gt;.</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
@@ -3956,40 +4032,53 @@ will be taken:</property>
<property name="column_spacing">8</property>
<property name="row_spacing">8</property>
<child>
- <widget class="GtkLabel" id="label60">
+ <widget class="GtkLabel" id="label63">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">The first step will begin when you click &lt;i&gt;Forward&lt;/i&gt;.</property>
+ <property name="label" translatable="yes">&lt;b&gt;1. Configure and test your local sound card&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">3</property>
- <property name="bottom_attach">4</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="emptylabel10">
+ <widget class="GtkLabel" id="label62">
<property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">To set up HFP for Linux for the first time, the following steps
+will be taken:</property>
+ <property name="use_markup">True</property>
</widget>
<packing>
- <property name="top_attach">2</property>
- <property name="bottom_attach">4</property>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
<property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_FILL</property>
+ <property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="emptylabel11">
+ <widget class="GtkLabel" id="label61">
<property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;b&gt;2.&lt;/b&gt; Configure your Bluetooth mobile phone</property>
+ <property name="use_markup">True</property>
</widget>
<packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
- <property name="y_options">GTK_FILL</property>
+ <property name="y_options"></property>
</packing>
</child>
<child>
@@ -4005,51 +4094,38 @@ will be taken:</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label61">
+ <widget class="GtkLabel" id="emptylabel11">
<property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">&lt;b&gt;2.&lt;/b&gt; Configure your Bluetooth mobile phone</property>
- <property name="use_markup">True</property>
</widget>
<packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
- <property name="y_options"></property>
+ <property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label62">
+ <widget class="GtkLabel" id="emptylabel10">
<property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">To set up HFP for Linux for the first time, the following steps
-will be taken:</property>
- <property name="use_markup">True</property>
</widget>
<packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
- <property name="y_options"></property>
+ <property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
- <widget class="GtkLabel" id="label63">
+ <widget class="GtkLabel" id="label60">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="xalign">0</property>
- <property name="label" translatable="yes">&lt;b&gt;1. Configure and test your local sound card&lt;/b&gt;</property>
+ <property name="label" translatable="yes">The first step will begin when you click &lt;i&gt;Forward&lt;/i&gt;.</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
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()
diff --git a/data/hfpd-dbus-doc.cs b/data/hfpd-dbus-doc.cs
index ff81138..d4f8457 100644
--- a/data/hfpd-dbus-doc.cs
+++ b/data/hfpd-dbus-doc.cs
@@ -152,7 +152,7 @@
* - Set the net.sf.nohands.hfpd.AudioGateway.AutoReconnect property
* to @c true.
* - Register to receive the
- * net.sf.nohands.hfpd.AudioGateway.AudiotateChanged signal, and use
+ * net.sf.nohands.hfpd.AudioGateway.AudioStateChanged signal, and use
* the signal handler to start streaming audio to and from the audio
* gateway device, using
* net.sf.nohands.hfpd.SoundIo.AudioGatewayStart(), or failing that,
@@ -395,7 +395,7 @@ namespace net.sf.nohands.hfpd {
* finds an unexpected value, it should fail and warn about
* a version mismatch.
*
- * This document describes version 2 of the HFPD D-Bus
+ * This document describes version 4 of the HFPD D-Bus
* interface.
*
* A version number value in this field covers all HFPD
@@ -1663,10 +1663,10 @@ namespace net.sf.nohands.hfpd {
*
* @note This method will not return until the audio gateway
* has responded to the command. An audio gateway that has
- * moved out of radio range may not be identified as being
- * inaccessible for several seconds, and a poorly designed
- * audio gateway may fail to respond altogether. D-Bus
- * clients should not expect this method to complete
+ * moved out of Bluetooth radio range may not be identified
+ * as being inaccessible for several seconds, and a poorly
+ * designed audio gateway may fail to respond altogether.
+ * D-Bus clients should not expect this method to complete
* quickly and should invoke it asynchronously.
*/
public Dial(in string phone_num);
@@ -1686,10 +1686,10 @@ namespace net.sf.nohands.hfpd {
*
* @note This method will not return until the audio gateway
* has responded to the command. An audio gateway that has
- * moved out of radio range may not be identified as being
- * inaccessible for several seconds, and a poorly designed
- * audio gateway may fail to respond altogether. D-Bus
- * clients should not expect this method to complete
+ * moved out of Bluetooth radio range may not be identified
+ * as being inaccessible for several seconds, and a poorly
+ * designed audio gateway may fail to respond altogether.
+ * D-Bus clients should not expect this method to complete
* quickly and should invoke it asynchronously.
*/
public Redial();
@@ -1712,10 +1712,10 @@ namespace net.sf.nohands.hfpd {
*
* @note This method will not return until the audio gateway
* has responded to the command. An audio gateway that has
- * moved out of radio range may not be identified as being
- * inaccessible for several seconds, and a poorly designed
- * audio gateway may fail to respond altogether. D-Bus
- * clients should not expect this method to complete
+ * moved out of Bluetooth radio range may not be identified
+ * as being inaccessible for several seconds, and a poorly
+ * designed audio gateway may fail to respond altogether.
+ * D-Bus clients should not expect this method to complete
* quickly and should invoke it asynchronously.
*/
public HangUp();
@@ -1738,36 +1738,200 @@ namespace net.sf.nohands.hfpd {
*
* @note This method will not return until the audio gateway
* has responded to the command. An audio gateway that has
- * moved out of radio range may not be identified as being
- * inaccessible for several seconds, and a poorly designed
- * audio gateway may fail to respond altogether. D-Bus
- * clients should not expect this method to complete
+ * moved out of Bluetooth radio range may not be identified
+ * as being inaccessible for several seconds, and a poorly
+ * designed audio gateway may fail to respond altogether.
+ * D-Bus clients should not expect this method to complete
* quickly and should invoke it asynchronously.
*/
public SendDtmf(in byte code);
/**
- * @brief Send an answer call command to the audio gateway
+ * @brief Request the audio gateway answer the incoming call
*
* This method causes an answer call command to be sent to
* the audio gateway. If the audio gateway has an
* incoming, incomplete, ringing call, the call will be
* answered and will become the active call.
- *
+
* @throw net.sf.nohands.hfpd.Error Thrown on any
* sort of error, unspecific of the reason of failure.
*
* @note This method will not return until the audio gateway
* has responded to the command. An audio gateway that has
- * moved out of radio range may not be identified as being
- * inaccessible for several seconds, and a poorly designed
- * audio gateway may fail to respond altogether. D-Bus
- * clients should not expect this method to complete
+ * moved out of Bluetooth radio range may not be identified
+ * as being inaccessible for several seconds, and a poorly
+ * designed audio gateway may fail to respond altogether.
+ * D-Bus clients should not expect this method to complete
* quickly and should invoke it asynchronously.
*/
public Answer();
/**
+ * @brief Query the telephone number of the audio gateway
+ *
+ * This method queries the telephone number of the
+ * audio gateway and returns the result. This request is
+ * described in GSM 07.07 7.1.
+ *
+ * @param[out] number The telephone number of the audio
+ * gateway on the public telephone network. The format
+ * of this number, e.g. organizational, local, or
+ * international, is described by the @em type parameter.
+ * @param[out] type The format of the @em number
+ * parameter. See GSM 04.10 section 10.5.4.7 for more
+ * information on the meaning. Some common values include:
+ * - 129: Unknown type of number in E.164 numbering plan
+ * - 145: International number in E.164 numbering plan
+ * @param[out] service Service related to the number.
+ * Values are described in GSM 07.07 section 7.1, and
+ * include:
+ * - 4: voice
+ * - 5: fax
+ *
+ * @throw net.sf.nohands.hfpd.Error Thrown on any
+ * sort of error, unspecific of the reason of failure.
+ *
+ * @note This method will not return until the audio gateway
+ * has responded to the command. An audio gateway that has
+ * moved out of Bluetooth radio range may not be identified
+ * as being inaccessible for several seconds, and a poorly
+ * designed audio gateway may fail to respond altogether.
+ * D-Bus clients should not expect this method to complete
+ * quickly and should invoke it asynchronously.
+ */
+ public QueryNumber(out string number, out int32 type,
+ out int32 service);
+
+ /** Structure used for AudioGateway.QueryCurrentCalls() */
+ struct CallInfo {
+ /** @brief Unique identifier of the call */
+ int32 idx;
+ /** @brief Direction: 0=outgoing, 1=incoming */
+ int32 dir;
+ /**
+ * @brief State of the call
+ * - 0: active
+ * - 1: held
+ * - 2: dialing outgoing call
+ * - 3: remotely ringing outgoing call
+ * - 4: incoming unanswered call
+ * - 5: waiting (on hold)
+ */
+ int32 stat;
+ /**
+ * @brief bearer/teleservice
+ * - 0: voice
+ * - 1: data
+ * - 2: fax
+ * - 3: voice followed by data, voice mode
+ * - 4: alternating voice/data, voice mode
+ * - 5: alternating voice/fax, voice mode
+ * - 6: voice followed by data, data mode
+ * - 7: alternating voice/data, data mode
+ * - 8: alternating voice/fax, fax mode
+ * - 9: unknown
+ */
+ int32 mode;
+ /** @brief Multiparty: 0=not linked, 1=linked */
+ int32 mpty;
+ /** @brief Telephone number of remote party */
+ string number;
+ /** @brief Telephone number representation type */
+ int32 type;
+ /** @brief Phone book formatted name of remote party */
+ string alpha;
+ };
+
+ /**
+ * @brief Query the current calls of the audio gateway
+ *
+ * This method queries the current calls on the audio
+ * gateway, and returns the result. This request is
+ * described in GSM 07.07 7.17. The audio gateway is not
+ * required to support this request, but is likely to
+ * support it if the @c "EnhancedCallStatus" feature in
+ * the AudioGateway.Features property is set to @c true.
+ *
+ * @param[out] calls An array of CallInfo structures, one
+ * per active call being handled by the audio gateway.
+ * If no calls are present, the array will be empty. The
+ * index members may be used
+ *
+ * @throw net.sf.nohands.hfpd.Error Thrown on any
+ * sort of error, unspecific of the reason of failure.
+ *
+ * @note This method will not return until the audio gateway
+ * has responded to the command. An audio gateway that has
+ * moved out of Bluetooth radio range may not be identified
+ * as being inaccessible for several seconds, and a poorly
+ * designed audio gateway may fail to respond altogether.
+ * D-Bus clients should not expect this method to complete
+ * quickly and should invoke it asynchronously.
+ */
+ public QueryCurrentCalls(out CallInfo[] calls);
+
+ /**
+ * @brief Query the operator of the audio gateway
+ *
+ * This method queries the current operator information
+ * from the audio gateway and returns the result. This
+ * request is described in GSM 07.07 7.3, but as per
+ * Bluetooth HFP 1.5, only the long alphanumeric form of
+ * the operator information is available, e.g. "T-Mobile"
+ * or "Verizon".
+ *
+ * @param[out] mode Operator selection mode. See GSM 07.07
+ * @param[out] format Operator display format, always 0.
+ * @param[out] oper Operator name as an alphanumeric
+ * string, e.g. "AT&T".
+ *
+ * @throw net.sf.nohands.hfpd.Error Thrown on any
+ * sort of error, unspecific of the reason of failure.
+ *
+ * @note This method will not return until the audio gateway
+ * has responded to the command. An audio gateway that has
+ * moved out of Bluetooth radio range may not be identified
+ * as being inaccessible for several seconds, and a poorly
+ * designed audio gateway may fail to respond altogether.
+ * D-Bus clients should not expect this method to complete
+ * quickly and should invoke it asynchronously.
+ */
+ public QueryOperator(out int32 mode, out int32 format,
+ out string oper);
+
+ /**
+ * @brief Request activation or deactivation of voice
+ * recognition on the audio gateway
+ *
+ * Some audio gateway devices support voice recognition,
+ * and can use the audio link and the microphone of the
+ * hands-free for voice recognition. Support for voice
+ * recognition is indicated by the @c "VoiceRecognition"
+ * feature in the AudioGateway.Features property being
+ * @c true.
+ *
+ * @param[in] activate Set to @c true to request
+ * activation of voice recognition, @c false to request
+ * deactivation.
+ *
+ * The current state of voice recognition can be retrieved
+ * from the AudioGateway.VoiceRecognitionActive property.
+ * Changes to the state, including changes induced by calls
+ * to this method, are reported by the
+ * VoiceRecognitionActiveChanged() signal.
+ *
+ * @note This method will not return until the audio gateway
+ * has responded to the command. An audio gateway that has
+ * moved out of Bluetooth radio range may not be identified
+ * as being inaccessible for several seconds, and a poorly
+ * designed audio gateway may fail to respond altogether.
+ * D-Bus clients should not expect this method to complete
+ * quickly and should invoke it asynchronously.
+ */
+ public SetVoiceRecognition(in bool activate);
+
+ /**
* @brief Send a drop held / UDUB command to the audio gateway
*
* This method causes an oddly designed command to be
@@ -1779,19 +1943,25 @@ namespace net.sf.nohands.hfpd {
* - Otherwise, if there is a call that is on hold, the call
* will be terminated.
*
- * Support for this command is optional, and is indicated
- * by the @c "DropHeldUdub" feature in the
- * AudioGateway.Features property being @c true.
+ * Support for this command by the audio gateway is
+ * optional, and is indicated by the @c "DropHeldUdub"
+ * feature in the AudioGateway.Features property being @c true.
+ *
+ * Many audio gateways do not perform a unique action when
+ * this request is issued, and will often reject or ignore
+ * the incoming call. In general, the UDUB request is
+ * implemented so inconsistently that it is not worth
+ * presenting to the user.
*
* @throw net.sf.nohands.hfpd.Error Thrown on any
* sort of error, unspecific of the reason of failure.
*
* @note This method will not return until the audio gateway
* has responded to the command. An audio gateway that has
- * moved out of radio range may not be identified as being
- * inaccessible for several seconds, and a poorly designed
- * audio gateway may fail to respond altogether. D-Bus
- * clients should not expect this method to complete
+ * moved out of Bluetooth radio range may not be identified
+ * as being inaccessible for several seconds, and a poorly
+ * designed audio gateway may fail to respond altogether.
+ * D-Bus clients should not expect this method to complete
* quickly and should invoke it asynchronously.
*/
public CallDropHeldUdub();
@@ -1807,24 +1977,53 @@ namespace net.sf.nohands.hfpd {
* incoming, unanswered call, it will be connected and
* will become the active call.
*
- * Support for this command is optional, and is indicated
- * by the @c "SwapDropActive" feature in the
- * AudioGateway.Features property being @c true.
+ * Support for this command by the audio gateway is
+ * optional, and is indicated by the @c "SwapDropActive"
+ * feature in the AudioGateway.Features property being @c true.
*
* @throw net.sf.nohands.hfpd.Error Thrown on any
* sort of error, unspecific of the reason of failure.
*
* @note This method will not return until the audio gateway
* has responded to the command. An audio gateway that has
- * moved out of radio range may not be identified as being
- * inaccessible for several seconds, and a poorly designed
- * audio gateway may fail to respond altogether. D-Bus
- * clients should not expect this method to complete
+ * moved out of Bluetooth radio range may not be identified
+ * as being inaccessible for several seconds, and a poorly
+ * designed audio gateway may fail to respond altogether.
+ * D-Bus clients should not expect this method to complete
* quickly and should invoke it asynchronously.
*/
public CallSwapDropActive();
/**
+ * @brief Send a drop specific call command to the audio
+ * gateway
+ *
+ * This method causes a command to be sent to the audio
+ * gateway requesting that it drop a specific call.
+ * This method is only useful when the audio gateway has the
+ * @c "EnhancedCallStatus" feature, in which case call index
+ * numbers can be retrieved via QueryCurrentCalls().
+ * Additionally, the audio gateway is only likely to support
+ * the command if it has the @c "EnhancedCallControl" and
+ * @c "DropIndex" features in the AudioGateway.Features
+ * property being @c true.
+ *
+ * @param[in] idx Index of the call to be dropped.
+ *
+ * @throw net.sf.nohands.hfpd.Error Thrown on any
+ * sort of error, unspecific of the reason of failure.
+ *
+ * @note This method will not return until the audio gateway
+ * has responded to the command. An audio gateway that has
+ * moved out of Bluetooth radio range may not be identified
+ * as being inaccessible for several seconds, and a poorly
+ * designed audio gateway may fail to respond altogether.
+ * D-Bus clients should not expect this method to complete
+ * quickly and should invoke it asynchronously.
+ */
+ public CallDropIndex(int idx);
+
+ /**
* @brief Send a hold active call command to the audio gateway
*
* This method causes a command to be sent to the audio
@@ -1835,19 +2034,19 @@ namespace net.sf.nohands.hfpd {
* incoming, unanswered call, it will be connected and
* will become the active call.
*
- * Support for this command is optional, and is indicated
- * by the @c "SwapHoldActive" feature in the
- * AudioGateway.Features property being @c true.
+ * Support for this command by the audio gateway is
+ * optional, and is indicated by the @c "SwapHoldActive"
+ * feature in the AudioGateway.Features property being @c true.
*
* @throw net.sf.nohands.hfpd.Error Thrown on any
* sort of error, unspecific of the reason of failure.
*
* @note This method will not return until the audio gateway
* has responded to the command. An audio gateway that has
- * moved out of radio range may not be identified as being
- * inaccessible for several seconds, and a poorly designed
- * audio gateway may fail to respond altogether. D-Bus
- * clients should not expect this method to complete
+ * moved out of Bluetooth radio range may not be identified
+ * as being inaccessible for several seconds, and a poorly
+ * designed audio gateway may fail to respond altogether.
+ * D-Bus clients should not expect this method to complete
* quickly and should invoke it asynchronously.
*/
public CallSwapHoldActive();
@@ -1858,10 +2057,10 @@ namespace net.sf.nohands.hfpd {
* This method causes a link calls command to be sent to the
* audio gateway. If the audio gateway has an active call,
* and a call placed on hold, the calls will be linked
- * into a single three-way call.
+ * into a single three-way multiparty call.
*
- * Support for this command is optional, and is indicated
- * by the @c "Link" feature in the
+ * Support for this command by the audio gateway is
+ * optional, and is indicated by the @c "Link" feature in the
* AudioGateway.Features property being @c true.
*
* @throw net.sf.nohands.hfpd.Error Thrown on any
@@ -1869,15 +2068,47 @@ namespace net.sf.nohands.hfpd {
*
* @note This method will not return until the audio gateway
* has responded to the command. An audio gateway that has
- * moved out of radio range may not be identified as being
- * inaccessible for several seconds, and a poorly designed
- * audio gateway may fail to respond altogether. D-Bus
- * clients should not expect this method to complete
+ * moved out of Bluetooth radio range may not be identified
+ * as being inaccessible for several seconds, and a poorly
+ * designed audio gateway may fail to respond altogether.
+ * D-Bus clients should not expect this method to complete
* quickly and should invoke it asynchronously.
*/
public CallLink();
/**
+ * @brief Send a private consult command to the audio
+ * gateway
+ *
+ * This method causes a command to be sent to the audio
+ * gateway requesting that it remove all calls from the
+ * multiparty call except for a specific call. All calls
+ * removed from the multiparty will be placed on hold.
+ * This method is only useful when the audio gateway has the
+ * @c "EnhancedCallStatus" feature, in which case call index
+ * numbers can be retrieved via QueryCurrentCalls().
+ * Additionally, the audio gateway is only likely to support
+ * the command if it has the @c "EnhancedCallControl" and
+ * @c "PrivateConsult" features in the AudioGateway.Features
+ * property set to @c true.
+ *
+ * @param[in] idx Index of the call to go to private
+ * consult mode.
+ *
+ * @throw net.sf.nohands.hfpd.Error Thrown on any
+ * sort of error, unspecific of the reason of failure.
+ *
+ * @note This method will not return until the audio gateway
+ * has responded to the command. An audio gateway that has
+ * moved out of Bluetooth radio range may not be identified
+ * as being inaccessible for several seconds, and a poorly
+ * designed audio gateway may fail to respond altogether.
+ * D-Bus clients should not expect this method to complete
+ * quickly and should invoke it asynchronously.
+ */
+ public CallPrivateConsult(int idx);
+
+ /**
* @brief Send a transfer call command to the audio gateway
*
* This method causes a transfer call command to be sent to
@@ -1885,19 +2116,19 @@ namespace net.sf.nohands.hfpd {
* call, and a call placed on hold, the calls will be linked
* to each other, and disconnected from the audio gateway.
*
- * Support for this command is optional, and is indicated
- * by the @c "Transfer" feature in the
- * AudioGateway.Features property being @c true.
+ * Support for this command by the audio gateway is
+ * optional, and is indicated by the @c "Transfer" feature
+ * in the AudioGateway.Features property being @c true.
*
* @throw net.sf.nohands.hfpd.Error Thrown on any
* sort of error, unspecific of the reason of failure.
*
* @note This method will not return until the audio gateway
* has responded to the command. An audio gateway that has
- * moved out of radio range may not be identified as being
- * inaccessible for several seconds, and a poorly designed
- * audio gateway may fail to respond altogether. D-Bus
- * clients should not expect this method to complete
+ * moved out of Bluetooth radio range may not be identified
+ * as being inaccessible for several seconds, and a poorly
+ * designed audio gateway may fail to respond altogether.
+ * D-Bus clients should not expect this method to complete
* quickly and should invoke it asynchronously.
*/
public CallTransfer();
@@ -1975,6 +2206,61 @@ namespace net.sf.nohands.hfpd {
const byte AudioState;
/**
+ * @brief Voice recognition state of the device
+ *
+ * This property can be accessed using the
+ * @ref property "standard D-Bus property interface".
+ *
+ * This read-only property describes whether the audio
+ * gateway has its voice recognition feature activated.
+ * When activated, the audio gateway will use the
+ * audio link to feed its built-in voice recogintion
+ * mechanism, and recognize commands spoken into the
+ * microphone of the hands-free.
+ *
+ * This property is present on all AudioGateway objects,
+ * but is only useful with devices that support voice
+ * recogintion. Such devices will have the
+ * @c "VoiceRecognition" feature in AudioGateway.Features
+ * set to @c true.
+ *
+ * Changes to this value are reported by the
+ * VoiceRecognitionActiveChanged() signal.
+ *
+ * This property is read-only, but changes may be
+ * effected by SetVoiceRecognition().
+ */
+ const bool VoiceRecognitionActive;
+
+ /**
+ * @brief In-Band ring tone state of the device
+ *
+ * This property can be accessed using the
+ * @ref property "standard D-Bus property interface".
+ *
+ * This read-only property describes whether the audio
+ * gateway has its in-band ring tone feature enabled.
+ * When in-band ring tones are enabled, the audio gateway
+ * will use the audio link to play its normal alerting
+ * ring tone, which will be played over the speakers of
+ * the hands-free.
+ *
+ * Changes to this value are reported by the
+ * InBandRingToneEnableChanged() signal.
+ *
+ * This property is read-only. Changes may only be
+ * effected by changing the setting on the audio gateway
+ * itself, typically through an option in the user
+ * interface of the audio gateway.
+ *
+ * This setting can be used to inhibit playback of alert
+ * sounds in situations where alert sounds take priority
+ * over the audio gateway audio link, or coexist with
+ * the audio link.
+ */
+ const bool InBandRingToneEnable;
+
+ /**
* @brief D-Bus ownership state of the device
*
* This property can be accessed using the
@@ -2113,12 +2399,13 @@ namespace net.sf.nohands.hfpd {
* - EnhancedCallControl
* - DropHeldUdub
* - SwapDropActive
- * - DropActive
+ * - DropIndex
* - SwapHoldActive
* - PrivateConsult
* - Link
* - Transfer
* - CallSetupIndicator
+ * - CallHeldIndicator
* - SignalStrengthIndicator
* - RoamingIndicator
* - BatteryChargeIndicator
@@ -2239,15 +2526,28 @@ namespace net.sf.nohands.hfpd {
* AudioGateway should use this signal to start alerting
* the user and possibly start playing a ring tone.
*
- * @param[out] caller_id The caller line identification
- * reported by the audio gateway, typically a set of digits
- * occasionally prefixed by a "+". The D-Bus client may
- * use this value as a key for a phone book search.
+ * @param[out] number The caller line identification
+ * reported by the audio gateway. The format of this
+ * number, e.g. organizational, local, or international,
+ * is described by the @em number_type parameter.
+ * The D-Bus client may use this value as a key for a
+ * phone book search.
+ * @param[out] number_type The format of the @em number
+ * parameter. See GSM 04.10 section 10.5.4.7 for more
+ * information on the meaning. Some common values include:
+ * - 129: Unknown type of number in E.164 numbering plan
+ * - 145: International number in E.164 numbering plan
+ * @param[out] subaddr The ISDN subaddress of the caller.
+ * This is unused in most situations. For more information,
+ * see ITU-T recommendation I.330.
+ * @param[out] subaddr_type The format of the @em subaddr
+ * parameter, in the same format as the @em number_type
+ * parameter.
* @param[out] caller_name Some audio gateway devices
* have built-in phone books, which may have an entry for
* the phone number of the caller. In such cases, this
- * parameter contains the caller name reported by the
- * audio gateway.
+ * parameter contains the contact name associated with the
+ * phone number as reported by the audio gateway.
*
* The D-Bus client has a number of options for dealing
* with the incoming call, although some are optionally
@@ -2256,7 +2556,10 @@ namespace net.sf.nohands.hfpd {
* - Reject the call with HangUp().
* - Report User Determined User Busy with CallDropHeldUdub().
*/
- public signal Ring(out string caller_id,
+ public signal Ring(out string number,
+ out int32 number_type,
+ out string subaddr,
+ out int32 subaddr_type,
out string caller_name);
/**
@@ -2298,5 +2601,41 @@ namespace net.sf.nohands.hfpd {
* gateway device.
*/
public signal NameResolved(out string name);
+
+ /**
+ * @brief Notification of voice recognition activatation or
+ * deactivatation
+ *
+ * When the audio gateway activates or deactivates its
+ * built-in voice recognition feature, this signal will
+ * be emitted as a notification.
+ *
+ * @param[out] state Set to @em true if voice recognition
+ * is being activated, @em false if deactivated.
+ *
+ * The voice recognition state is also available from
+ * the AudioGateway.VoiceRecognitionActive property.
+ */
+ public signal VoiceRecognitionActiveChanged(out bool state);
+
+ /**
+ * @brief Notification of change to the in-band ring tone
+ * setting of the audio gateway
+ *
+ * When the audio gateway's in-band ring tone setting is
+ * changed, this signal will be emitted as a notification.
+ *
+ * @param[out] state The new value of the in-band ring tone
+ * setting. Set to @em true if the in-band ring tones are
+ * enabled, @em false if disabled.
+ *
+ * The in-band ring tone state is also available from
+ * the AudioGateway.InBandRingToneEnable property.
+ *
+ * The in-band ring tone setting may only be changed by
+ * the audio gateway itself, typically through an option
+ * in the user interface of the audio gateway.
+ */
+ public signal InBandRingToneEnableChanged(out bool state);
}
}
diff --git a/hfpd/objects.cpp b/hfpd/objects.cpp
index 30c98a7..294d042 100644
--- a/hfpd/objects.cpp
+++ b/hfpd/objects.cpp
@@ -197,13 +197,15 @@ SendReplyErrorInfo(DBusMessage *msgp, libhfp::ErrorInfo &error)
}
struct AgPendingCommand {
- ListItem m_links;
- DBusMessage *m_msg;
- HfpPendingCommand *m_pend;
- AudioGateway *m_ag;
+ ListItem m_links;
+ DBusMessage *m_msg;
+ HfpPendingCommand *m_pend;
+ AudioGateway *m_ag;
+
+ Callback<void, AgPendingCommand *, void *> cb_success;
void HfpCommandResult(HfpPendingCommand *pendp,
- ErrorInfo *error, const char *info) {
+ ErrorInfo *error, void *info) {
assert(pendp == m_pend);
assert(m_msg);
assert(m_ag);
@@ -213,7 +215,11 @@ struct AgPendingCommand {
* structure here.
*/
if (!error) {
- (void) m_ag->SendReplyArgs(m_msg, DBUS_TYPE_INVALID);
+ if (cb_success.Registered())
+ cb_success(this, info);
+ else
+ (void) m_ag->SendReplyArgs(m_msg,
+ DBUS_TYPE_INVALID);
} else {
(void) m_ag->SendReplyErrorInfo(m_msg, *error);
}
@@ -268,6 +274,11 @@ AudioGateway(HandsFree *hfp, HfpSession *sessp, char *name)
sessp->cb_NotifyCall.Register(this, &AudioGateway::NotifyCall);
sessp->cb_NotifyIndicator.Register(this,
&AudioGateway::NotifyIndicator);
+ sessp->cb_NotifyVoiceRecog.Register(this,
+ &AudioGateway::NotifyVoiceRecog);
+ sessp->cb_NotifyVolume.Register(this, &AudioGateway::NotifyVolume);
+ sessp->cb_NotifyInBandRingTone.Register(this,
+ &AudioGateway::NotifyInBandRingTone);
sessp->cb_NotifyDestroy.Register(this, &AudioGateway::NotifyDestroy);
}
@@ -525,22 +536,31 @@ NotifyCall(libhfp::HfpSession *sessp, bool act, bool waiting, bool ring)
UpdateCallState(CallState());
if (ring) {
- const char *num = 0, *alpha = 0;
- const GsmClipPhoneNumber *clip;
+ const char *num = 0, *sa = 0, *alpha = 0;
+ const GsmClipResult *clip;
+ int32_t numtype = 0, satype = 0;
clip = sessp->WaitingCallIdentity();
- if (clip) {
+ if (clip && (clip->cli_validity == 0)) {
num = clip->number;
+ numtype = clip->type;
+ sa = clip->subaddr;
+ satype = clip->satype;
alpha = clip->alpha;
}
if (!num)
num = "";
+ if (!sa)
+ sa = "";
if (!alpha)
alpha = "";
SendSignalArgs(HFPD_AUDIOGATEWAY_INTERFACE_NAME,
"Ring",
DBUS_TYPE_STRING, &num,
+ DBUS_TYPE_INT32, &numtype,
+ DBUS_TYPE_STRING, &sa,
+ DBUS_TYPE_INT32, &satype,
DBUS_TYPE_STRING, &alpha,
DBUS_TYPE_INVALID);
}
@@ -601,6 +621,31 @@ NotifyAudioConnection(libhfp::HfpSession *sessp, libhfp::ErrorInfo *error)
}
void AudioGateway::
+NotifyVoiceRecog(libhfp::HfpSession *sessp, bool active)
+{
+ dbus_bool_t st = active;
+ (void) SendSignalArgs(HFPD_AUDIOGATEWAY_INTERFACE_NAME,
+ "VoiceRecognitionActiveChanged",
+ DBUS_TYPE_BOOLEAN, &st,
+ DBUS_TYPE_INVALID);
+}
+
+void AudioGateway::
+NotifyVolume(libhfp::HfpSession *sessp, bool mic, bool speaker)
+{
+}
+
+void AudioGateway::
+NotifyInBandRingTone(libhfp::HfpSession *sessp, bool enabled)
+{
+ dbus_bool_t st = enabled;
+ (void) SendSignalArgs(HFPD_AUDIOGATEWAY_INTERFACE_NAME,
+ "InBandRingToneEnableChanged",
+ DBUS_TYPE_BOOLEAN, &st,
+ DBUS_TYPE_INVALID);
+}
+
+void AudioGateway::
NotifyDestroy(BtManaged *objp)
{
const char *path;
@@ -788,6 +833,216 @@ Answer(DBusMessage *msgp)
m_sess->CmdAnswer(&error)));
}
+void AudioGateway::
+QueryNumberComplete(AgPendingCommand *agpcp, void *result)
+{
+ GsmCnumResult *res = (GsmCnumResult *) result;
+ int32_t type, service;
+ const char *number = 0;
+
+ assert(res);
+
+ number = res->number;
+ type = res->type;
+ service = res->service;
+
+ if (!number)
+ number = "";
+ (void) SendReplyArgs(agpcp->m_msg,
+ DBUS_TYPE_STRING, &number,
+ DBUS_TYPE_INT32, &type,
+ DBUS_TYPE_INT32, &service,
+ DBUS_TYPE_INVALID);
+
+}
+
+bool AudioGateway::
+QueryNumber(DBusMessage *msgp)
+{
+ AgPendingCommand *agpcp = 0;
+ ErrorInfo error;
+
+ if (!CreatePendingCommand(msgp, agpcp))
+ return false;
+
+ agpcp->cb_success.Register(this, &AudioGateway::QueryNumberComplete);
+ return DoPendingCommand(agpcp, error,
+ m_sess->CmdQueryNumber(&error));
+}
+
+void AudioGateway::
+QueryOperatorComplete(AgPendingCommand *agpcp, void *result)
+{
+ GsmCopsResult *res = (GsmCopsResult *) result;
+ int32_t mode, format;
+ char ibuf[16];
+ const char *oper = 0;
+
+ assert(res);
+
+ mode = res->mode;
+ format = res->format;
+ if (format == 2) {
+ sprintf(ibuf, "%d", res->nonalpha);
+ oper = ibuf;
+ } else {
+ oper = res->alpha;
+ }
+
+ if (!oper)
+ oper = "";
+ (void) SendReplyArgs(agpcp->m_msg,
+ DBUS_TYPE_INT32, &mode,
+ DBUS_TYPE_INT32, &format,
+ DBUS_TYPE_STRING, &oper,
+ DBUS_TYPE_INVALID);
+
+}
+
+bool AudioGateway::
+QueryOperator(DBusMessage *msgp)
+{
+ AgPendingCommand *agpcp = 0;
+ ErrorInfo error;
+
+ if (!CreatePendingCommand(msgp, agpcp))
+ return false;
+
+ agpcp->cb_success.Register(this, &AudioGateway::QueryOperatorComplete);
+ return DoPendingCommand(agpcp, error,
+ m_sess->CmdQueryOperator(&error));
+}
+
+void AudioGateway::
+QueryCurrentCallsComplete(AgPendingCommand *agpcp, void *result)
+{
+ ListItem *headp, *listp;
+ GsmClccResult *resp;
+ DBusMessage *replyp;
+ ErrorInfo error;
+ DBusMessageIter mi, ami, smi;
+
+ int32_t idx, dir, stat, mode, mpty, type;
+ const char *number, *alpha;
+
+ headp = (ListItem *) result;
+ replyp = NewMethodReturn(agpcp->m_msg);
+ if (!replyp)
+ goto failed;
+
+ dbus_message_iter_init_append(replyp, &mi);
+ if (!dbus_message_iter_open_container(&mi,
+ DBUS_TYPE_ARRAY,
+ DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_INT32_AS_STRING
+ DBUS_TYPE_INT32_AS_STRING
+ DBUS_TYPE_INT32_AS_STRING
+ DBUS_TYPE_INT32_AS_STRING
+ DBUS_TYPE_INT32_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_INT32_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_STRUCT_END_CHAR_AS_STRING,
+ &ami)) {
+ error.SetNoMem();
+ goto failed;
+ }
+
+ ListForEach(listp, headp) {
+ resp = GetContainer(listp, GsmClccResult, m_links);
+
+ idx = resp->idx;
+ dir = resp->dir;
+ stat = resp->stat;
+ mode = resp->mode;
+ mpty = resp->mpty;
+ number = resp->number;
+ type = resp->type;
+ alpha = resp->alpha;
+
+ if (!number)
+ number = "";
+ if (!alpha)
+ alpha = "";
+
+ if (!dbus_message_iter_open_container(&ami,
+ DBUS_TYPE_STRUCT,
+ 0,
+ &smi) ||
+ !dbus_message_iter_append_basic(&smi,
+ DBUS_TYPE_INT32,
+ &idx) ||
+ !dbus_message_iter_append_basic(&smi,
+ DBUS_TYPE_INT32,
+ &dir) ||
+ !dbus_message_iter_append_basic(&smi,
+ DBUS_TYPE_INT32,
+ &stat) ||
+ !dbus_message_iter_append_basic(&smi,
+ DBUS_TYPE_INT32,
+ &mode) ||
+ !dbus_message_iter_append_basic(&smi,
+ DBUS_TYPE_INT32,
+ &mpty) ||
+ !dbus_message_iter_append_basic(&smi,
+ DBUS_TYPE_STRING,
+ &number) ||
+ !dbus_message_iter_append_basic(&smi,
+ DBUS_TYPE_INT32,
+ &type) ||
+ !dbus_message_iter_append_basic(&smi,
+ DBUS_TYPE_STRING,
+ &alpha) ||
+ !dbus_message_iter_close_container(&ami, &smi)) {
+ error.SetNoMem();
+ goto failed;
+ }
+ }
+
+ if (!dbus_message_iter_close_container(&mi, &ami) ||
+ !SendMessage(replyp))
+ goto failed;
+
+ return;
+
+failed:
+ SendReplyErrorInfo(agpcp->m_msg, error);
+}
+
+bool AudioGateway::
+QueryCurrentCalls(DBusMessage *msgp)
+{
+ AgPendingCommand *agpcp = 0;
+ ErrorInfo error;
+
+ if (!CreatePendingCommand(msgp, agpcp))
+ return false;
+
+ agpcp->cb_success.Register(this,
+ &AudioGateway::QueryCurrentCallsComplete);
+ return DoPendingCommand(agpcp, error,
+ m_sess->CmdQueryCurrentCalls(&error));
+}
+
+bool AudioGateway::
+SetVoiceRecognition(DBusMessage *msgp)
+{
+ DBusMessageIter mi;
+ bool res;
+ dbus_bool_t st;
+ AgPendingCommand *agpcp = 0;
+ ErrorInfo error;
+
+ res = dbus_message_iter_init(msgp, &mi);
+ assert(res);
+ assert(dbus_message_iter_get_arg_type(&mi) == DBUS_TYPE_BOOLEAN);
+ dbus_message_iter_get_basic(&mi, &st);
+
+ return (CreatePendingCommand(msgp, agpcp) &&
+ DoPendingCommand(agpcp, error,
+ m_sess->CmdSetVoiceRecog(st, &error)));
+}
+
bool AudioGateway::
CallDropHeldUdub(DBusMessage *msgp)
{
@@ -811,6 +1066,25 @@ CallSwapDropActive(DBusMessage *msgp)
}
bool AudioGateway::
+CallDropIndex(DBusMessage *msgp)
+{
+ DBusMessageIter mi;
+ int32_t index;
+ bool res;
+ AgPendingCommand *agpcp = 0;
+ ErrorInfo error;
+
+ res = dbus_message_iter_init(msgp, &mi);
+ assert(res);
+ assert(dbus_message_iter_get_arg_type(&mi) == DBUS_TYPE_INT32);
+ dbus_message_iter_get_basic(&mi, &index);
+
+ return (CreatePendingCommand(msgp, agpcp) &&
+ DoPendingCommand(agpcp, error,
+ m_sess->CmdCallDropIndex(index, &error)));
+}
+
+bool AudioGateway::
CallSwapHoldActive(DBusMessage *msgp)
{
AgPendingCommand *agpcp = 0;
@@ -833,6 +1107,25 @@ CallLink(DBusMessage *msgp)
}
bool AudioGateway::
+CallPrivateConsult(DBusMessage *msgp)
+{
+ DBusMessageIter mi;
+ int32_t index;
+ bool res;
+ AgPendingCommand *agpcp = 0;
+ ErrorInfo error;
+
+ res = dbus_message_iter_init(msgp, &mi);
+ assert(res);
+ assert(dbus_message_iter_get_arg_type(&mi) == DBUS_TYPE_INT32);
+ dbus_message_iter_get_basic(&mi, &index);
+
+ return (CreatePendingCommand(msgp, agpcp) &&
+ DoPendingCommand(agpcp, error,
+ m_sess->CmdCallPrivateConsult(index, &error)));
+}
+
+bool AudioGateway::
CallTransfer(DBusMessage *msgp)
{
AgPendingCommand *agpcp = 0;
@@ -866,6 +1159,20 @@ GetAudioState(DBusMessage *msgp, uint8_t &val)
}
bool AudioGateway::
+GetVoiceRecognitionActive(DBusMessage *msgp, bool &val)
+{
+ val = m_sess->GetVoiceRecogActive();
+ return true;
+}
+
+bool AudioGateway::
+GetInBandRingToneEnable(DBusMessage *msgp, bool &val)
+{
+ val = m_sess->GetInBandRingToneEnable();
+ return true;
+}
+
+bool AudioGateway::
GetClaimed(DBusMessage *msgp, bool &val)
{
val = (m_owner != 0);
@@ -1003,8 +1310,8 @@ GetFeatures(DBusMessage *msgp, const DbusProperty *propp,
m_sess->FeatureDropHeldUdub()) ||
!AddStringBool(ami, "SwapDropActive",
m_sess->FeatureSwapDropActive()) ||
- !AddStringBool(ami, "DropActive",
- m_sess->FeatureDropActive()) ||
+ !AddStringBool(ami, "DropIndex",
+ m_sess->FeatureDropIndex()) ||
!AddStringBool(ami, "SwapHoldActive",
m_sess->FeatureSwapHoldActive()) ||
!AddStringBool(ami, "PrivateConsult",
@@ -1015,6 +1322,8 @@ GetFeatures(DBusMessage *msgp, const DbusProperty *propp,
m_sess->FeatureTransfer()) ||
!AddStringBool(ami, "CallSetupIndicator",
m_sess->FeatureIndCallSetup()) ||
+ !AddStringBool(ami, "CallHeldIndicator",
+ m_sess->FeatureIndCallHeld()) ||
!AddStringBool(ami, "SignalStrengthIndicator",
m_sess->FeatureIndSignalStrength()) ||
!AddStringBool(ami, "RoamingIndicator",
@@ -1814,7 +2123,7 @@ done:
bool HandsFree::
GetVersion(DBusMessage *msgp, dbus_uint32_t &val)
{
- val = 3;
+ val = 4;
return true;
}
@@ -2070,7 +2379,12 @@ bool HandsFree::
SetReportCapabilities(DBusMessage *msgp, const dbus_uint32_t &val,
bool &doreply)
{
- m_hfp->SetCaps(val);
+ ErrorInfo error;
+
+ if (!m_hfp->SetCaps(val, &error)) {
+ doreply = false;
+ return SendReplyErrorInfo(msgp, error);
+ }
return true;
}
diff --git a/hfpd/objects.h b/hfpd/objects.h
index d17536e..037af82 100644
--- a/hfpd/objects.h
+++ b/hfpd/objects.h
@@ -85,6 +85,12 @@ class AudioGateway : public HfpdExportObject {
bool DoSetAutoReconnect(bool value, libhfp::ErrorInfo *error = 0);
+ void QueryNumberComplete(class AgPendingCommand *agpcp, void *result);
+ void QueryOperatorComplete(class AgPendingCommand *agpcp,
+ void *result);
+ void QueryCurrentCallsComplete(class AgPendingCommand *agpcp,
+ void *result);
+
public:
AudioGateway(HandsFree *hfp, libhfp::HfpSession *sessp, char *name);
virtual ~AudioGateway();
@@ -114,6 +120,9 @@ public:
int val);
void NotifyAudioConnection(libhfp::HfpSession *sessp,
libhfp::ErrorInfo *reason);
+ void NotifyVoiceRecog(libhfp::HfpSession *sessp, bool active);
+ void NotifyVolume(libhfp::HfpSession *sessp, bool mic, bool speaker);
+ void NotifyInBandRingTone(libhfp::HfpSession *sessp, bool enabled);
void NotifyDestroy(libhfp::BtManaged *sessp);
void NameResolved(void);
@@ -127,16 +136,24 @@ public:
bool HangUp(DBusMessage *msgp);
bool SendDtmf(DBusMessage *msgp);
bool Answer(DBusMessage *msgp);
+ bool QueryNumber(DBusMessage *msgp);
+ bool QueryOperator(DBusMessage *msgp);
+ bool QueryCurrentCalls(DBusMessage *msgp);
+ bool SetVoiceRecognition(DBusMessage *msgp);
bool CallDropHeldUdub(DBusMessage *msgp);
bool CallSwapDropActive(DBusMessage *msgp);
+ bool CallDropIndex(DBusMessage *msgp);
bool CallSwapHoldActive(DBusMessage *msgp);
bool CallLink(DBusMessage *msgp);
+ bool CallPrivateConsult(DBusMessage *msgp);
bool CallTransfer(DBusMessage *msgp);
/* D-Bus Property related methods */
bool GetState(DBusMessage *msgp, uint8_t &val);
bool GetCallState(DBusMessage *msgp, uint8_t &val);
bool GetAudioState(DBusMessage *msgp, uint8_t &val);
+ bool GetVoiceRecognitionActive(DBusMessage *msgp, bool &val);
+ bool GetInBandRingToneEnable(DBusMessage *msgp, bool &val);
bool GetClaimed(DBusMessage *msgp, bool &val);
bool GetVoluntaryDisconnect(DBusMessage *msgp, bool &val);
bool GetAddress(DBusMessage *msgp, const DbusProperty *propp,
@@ -163,10 +180,16 @@ static const DbusMethod g_AudioGateway_methods[] = {
DbusMethodEntry(AudioGateway, HangUp, "", ""),
DbusMethodEntry(AudioGateway, SendDtmf, "y", ""),
DbusMethodEntry(AudioGateway, Answer, "", ""),
+ DbusMethodEntry(AudioGateway, QueryNumber, "", "sii"),
+ DbusMethodEntry(AudioGateway, QueryOperator, "", "iis"),
+ DbusMethodEntry(AudioGateway, QueryCurrentCalls, "", "a(iiiiisis)"),
+ DbusMethodEntry(AudioGateway, SetVoiceRecognition, "b", ""),
DbusMethodEntry(AudioGateway, CallDropHeldUdub, "", ""),
DbusMethodEntry(AudioGateway, CallSwapDropActive, "", ""),
+ DbusMethodEntry(AudioGateway, CallDropIndex, "i", ""),
DbusMethodEntry(AudioGateway, CallSwapHoldActive, "", ""),
DbusMethodEntry(AudioGateway, CallLink, "", ""),
+ DbusMethodEntry(AudioGateway, CallPrivateConsult, "i", ""),
DbusMethodEntry(AudioGateway, CallTransfer, "", ""),
{ 0, }
};
@@ -177,9 +200,12 @@ static const DbusMethod g_AudioGateway_signals[] = {
DbusSignalEntry(AudioStateChanged, "y"),
DbusSignalEntry(ClaimStateChanged, "b"),
DbusSignalEntry(AutoReconnectChanged, "b"),
- DbusSignalEntry(Ring, "ss"),
+ DbusSignalEntry(Ring, "sisis"),
DbusSignalEntry(IndicatorChanged, "si"),
DbusSignalEntry(NameResolved, "s"),
+ DbusSignalEntry(VoiceRecognitionActiveChanged, "b"),
+ DbusSignalEntry(InBandRingToneEnableChanged, "b"),
+ DbusSignalEntry(VolumeChanged, "yy"),
{ 0, }
};
@@ -194,6 +220,12 @@ static const DbusProperty g_AudioGateway_properties[] = {
GetCallState),
DbusPropertyMarshallImmutable(uint8_t, AudioState, AudioGateway,
GetAudioState),
+ DbusPropertyMarshallImmutable(bool, VoiceRecognitionActive,
+ AudioGateway,
+ GetVoiceRecognitionActive),
+ DbusPropertyMarshallImmutable(bool, InBandRingToneEnable,
+ AudioGateway,
+ GetInBandRingToneEnable),
DbusPropertyMarshallImmutable(bool, Claimed, AudioGateway,
GetClaimed),
DbusPropertyMarshallImmutable(bool, VoluntaryDisconnect, AudioGateway,
diff --git a/include/libhfp/bt.h b/include/libhfp/bt.h
index 9065f15..8c0a2a2 100644
--- a/include/libhfp/bt.h
+++ b/include/libhfp/bt.h
@@ -172,6 +172,8 @@ enum {
LIBHFP_ERROR_BT_NOT_CONNECTED_SCO,
/** System or service has already been started */
LIBHFP_ERROR_BT_ALREADY_STARTED,
+ /** Operation timed out */
+ LIBHFP_ERROR_BT_TIMEOUT,
/** Protocol has been violated */
LIBHFP_ERROR_BT_PROTOCOL_VIOLATION,
/** User-initiated disconnection of device */
diff --git a/include/libhfp/hfp.h b/include/libhfp/hfp.h
index 5d67006..64167df 100644
--- a/include/libhfp/hfp.h
+++ b/include/libhfp/hfp.h
@@ -63,7 +63,7 @@ class HfpSession;
* callbacks, e.g. HfpService::cb_HfpSessionFactory, read known devices
* from a configuration file, and instantiate an HfpSession object for
* each with GetSession(). The HfpSession objects can be marked
- * auto-reconnect, via HfpSession::SetAutoReconnect(), so that they may
+ * auto-reconnect, via RfcommSession::SetAutoReconnect(), so that they may
* be automatically connected when they enter radio range.
*
* Whenever Hands-Free Profile service is active, HfpService maintains
@@ -95,26 +95,12 @@ private:
char *m_svc_desc;
sdp_record_t *m_sdp_rec;
- void AutoReconnectTimeout(TimerNotifier*);
-
bool m_sco_enable;
- ListItem m_autoreconnect_list;
- int m_autoreconnect_timeout;
- bool m_autoreconnect_set;
- TimerNotifier *m_autoreconnect_timer;
-
- ListItem m_autoreconnect_now_list;
- bool m_autoreconnect_now_set;
- TimerNotifier *m_autoreconnect_now_timer;
-
bool m_complaint_sco_mtu;
bool m_complaint_sco_vs;
bool m_complaint_sco_listen;
- void AddAutoReconnect(HfpSession *sessp, bool now = false);
- void RemoveAutoReconnect(HfpSession *sessp);
-
bool ScoListen(ErrorInfo *error);
void ScoCleanup(void);
void ScoListenNotify(SocketNotifier *, int fh);
@@ -132,7 +118,7 @@ private:
public:
- HfpService(int caps = 15);
+ HfpService(int caps = 63);
virtual ~HfpService();
/**
@@ -435,15 +421,24 @@ public:
*/
bool SetScoEnabled(bool sco_enable, ErrorInfo *error = 0);
+ /** Query advertised hands-free capabilities */
int GetCaps(void) const { return m_brsf_my_caps; }
- void SetCaps(int caps) { m_brsf_my_caps = caps; }
+ /** Set advertised hands-free capabilities */
+ bool SetCaps(int caps, ErrorInfo *error = 0);
+
+ /** Query service name advertised in SDP record */
const char *GetServiceName(void) const
{ return m_svc_name ? m_svc_name : "Handsfree"; }
+
+ /** Set service name advertised in SDP record */
bool SetServiceName(const char *desc, ErrorInfo *error = 0);
+ /** Query service description advertised in SDP record */
const char *GetServiceDesc(void) const
{ return m_svc_desc ? m_svc_desc : ""; }
+
+ /** Set service description advertised in SDP record */
bool SetServiceDesc(const char *desc, ErrorInfo *error = 0);
static bool IsDeviceClassHf(uint32_t devclass) {
@@ -454,12 +449,16 @@ public:
}
};
-class GsmClipPhoneNumber {
- void *operator new(size_t nb, size_t extra);
+class GsmResult {
+protected:
+ char *src_string;
size_t extra;
+ void *operator new(size_t nb, const char *src_string, size_t len);
+public:
+ void operator delete(void *mem);
+};
- static GsmClipPhoneNumber *Create(const char *src);
-
+class GsmClipResult : public GsmResult {
public:
const char *number;
int type;
@@ -468,11 +467,47 @@ public:
const char *alpha;
int cli_validity;
- void operator delete(void *mem);
- static GsmClipPhoneNumber *Parse(const char *buffer);
- static GsmClipPhoneNumber *ParseCcwa(const char *buffer);
- bool Compare(const GsmClipPhoneNumber *clip) const;
- GsmClipPhoneNumber *Duplicate(void) const;
+ static GsmClipResult *Parse(const char *buffer);
+ static GsmClipResult *ParseCcwa(const char *buffer);
+ bool Compare(const GsmClipResult *clip) const;
+ GsmClipResult *Duplicate(void) const;
+};
+
+class GsmCnumResult : public GsmResult {
+public:
+ const char *alpha;
+ const char *number;
+ int type;
+ int speed;
+ int service;
+
+ static GsmCnumResult *Parse(const char *buffer);
+};
+
+class GsmCopsResult : public GsmResult {
+public:
+ int mode;
+ int format;
+ const char *alpha;
+ int nonalpha;
+
+ static GsmCopsResult *Parse(const char *buffer);
+};
+
+class GsmClccResult : public GsmResult {
+public:
+ ListItem m_links;
+
+ int idx;
+ int dir;
+ int stat;
+ int mode;
+ int mpty;
+ const char *number;
+ int type;
+ const char *alpha;
+
+ static GsmClccResult *Parse(const char *buffer);
};
class AtCommand;
@@ -508,7 +543,7 @@ class AtCommand;
* successful call to the Cancel() method.
*/
class HfpPendingCommand
- : public Callback<void, HfpPendingCommand*, ErrorInfo*, const char *> {
+ : public Callback<void, HfpPendingCommand*, ErrorInfo*, void *> {
public:
/**
* @brief Request that the command be canceled and not sent
@@ -558,7 +593,7 @@ public:
* - Client reference count drops to zero. Put().
* - The HfpSession is in the Disconnected state. See IsConnected(),
* IsConnecting().
- * - Auto-reconnect is not enabled. See SetAutoReconnect().
+ * - Auto-reconnect is not enabled. See RfcommSession::SetAutoReconnect().
* - As with all @ref managed "managed objects," the actual destruction
* is performed in the context of a timer event.
*
@@ -608,38 +643,44 @@ private:
BTS_Handshaking,
BTS_Connected
} m_conn_state;
- bool m_conn_autoreconnect;
- ListItem m_autoreconnect_links;
ListItem m_commands;
+ /* Timeout for completion of the topmost command */
+ TimerNotifier *m_command_timer;
+ void CommandTimeout(TimerNotifier *notp);
+
int m_brsf;
/* Methods reimplemented from RfcommSession */
virtual void __Disconnect(ErrorInfo *reason, bool voluntary = false);
virtual void NotifyConnectionState(ErrorInfo *async_error);
virtual void SdpSupportedFeatures(uint16_t features);
+ virtual void AutoReconnect(void);
/* New methods for HFP */
- void AutoReconnect(void);
bool HfpHandshake(ErrorInfo *error);
void HfpHandshakeDone(void);
void HfpDataReady(SocketNotifier *notp, int fh);
size_t HfpConsume(char *buf, size_t len);
void DeleteFirstCommand(bool do_start = true);
- bool AppendCommand(AtCommand *cmdp, ErrorInfo *error);
+ bool AddCommand(AtCommand *cmdp, bool top, ErrorInfo *error);
+ bool SendCommand(const char *cmd, ErrorInfo *error);
bool StartCommand(ErrorInfo *error);
bool CancelCommand(AtCommand *cmdp);
void ResponseDefault(char *buf);
HfpPendingCommand *PendingCommand(AtCommand *cmdp, ErrorInfo *error);
+ friend class CopsCommand;
friend class CindRCommand;
+ friend class CmerCommand;
friend class AtdCommand;
friend class AtCommandClearCallSetup;
void UpdateIndicator(int inum, const char *ival);
void UpdateCallSetup(int val, int ring = 0,
- GsmClipPhoneNumber *clip = 0,
+ GsmClipResult *clip = 0,
int timeout_ms = 0);
+ void ClearClip(void);
friend class ChldTCommand;
void SetSupportedHoldRange(int start, int end);
@@ -653,7 +694,7 @@ private:
m_chld_4: 1;
friend class BrsfCommand;
- void SetSupportedFeatures(int ag_features) { m_brsf = ag_features; };
+ void SetSupportedFeatures(int ag_features);
friend class CindTCommand;
void SetIndicatorNum(int inum, const char *name, int namelen);
@@ -667,6 +708,7 @@ private:
int m_inum_service;
int m_inum_call;
int m_inum_callsetup;
+ int m_inum_callheld;
int m_inum_signal;
int m_inum_roam;
int m_inum_battchg;
@@ -679,7 +721,11 @@ private:
void CleanupIndicators(void);
void ExpandIndicators(int min_size);
+ bool VerifyIndicators(void);
+ friend class NrecCommand;
+ friend class BvraCommand;
+ void SetBvra(bool active);
/* Call state trackers */
bool m_state_service;
@@ -688,9 +734,13 @@ private:
int m_state_signal;
int m_state_roam;
int m_state_battchg;
+ bool m_state_bvra;
+ bool m_state_bsir;
+ bool m_state_ecnr;
+ int m_state_vgm;
+ int m_state_vgs;
enum { PHONENUM_MAX_LEN = 31, };
- GsmClipPhoneNumber *m_state_incomplete_clip;
static bool ValidPhoneNumChar(char c, ErrorInfo *error);
static bool ValidPhoneNum(const char *ph, ErrorInfo *error);
@@ -724,15 +774,32 @@ private:
bool ScoSocketExists(void) const { return (m_sco_sock >= 0); }
/* Event handling stuff */
+ bool m_callsetup_presumed;
TimerNotifier *m_timer;
void Timeout(TimerNotifier *notp);
+ /*
+ * Caller-ID timeout for RING events, to avoid reporting empty
+ * CLIP information.
+ */
+ TimerNotifier *m_clip_timer;
+ enum {
+ CLIP_UNKNOWN,
+ CLIP_WAITING,
+ CLIP_FINAL,
+ } m_clip_state;
+ GsmClipResult *m_clip_value;
+
+ void ClipTimeout(TimerNotifier *notp);
+
protected:
/* Timeouts for dealing with devices without the callsetup indicator */
int m_timeout_ring;
int m_timeout_ring_ccwa;
int m_timeout_dial;
+ int m_timeout_clip;
+ int m_timeout_command;
public:
HfpService *GetService(void) const
@@ -882,6 +949,51 @@ public:
*/
Callback<void, HfpSession *, const char *, int> cb_NotifyIndicator;
+ /**
+ * @brief Notification of a change to the audio gateway's
+ * voice recognition state
+ *
+ * @param bool Set to @em true if the audio gateway has activated
+ * voice recognition mode, @em false otherwise
+ *
+ * The last known voice recognition state can also be accessed
+ * via GetVoiceRecogActive(). Activation or deactivation can
+ * be requested via CmdSetVoiceRecog().
+ *
+ * @sa Bluetooth HFP v1.5 section 4.25
+ */
+ Callback<void, HfpSession *, bool> cb_NotifyVoiceRecog;
+
+ /**
+ * @brief Notification of a change to the negotiated speaker
+ * or microphone gain level
+ *
+ * @param bool1 Set to @em true if the microphone gain level has
+ * been changed by audio gateway
+ * @param bool2 Set to @em true if the speaker gain level has
+ * been changed by audio gateway
+ *
+ * The gain values can be accessed via GetVolumeMic() and
+ * GetVolumeSpeaker().
+ *
+ * @sa Bluetooth HFP v1.5 section 4.28
+ */
+ Callback<void, HfpSession *, bool, bool> cb_NotifyVolume;
+
+ /**
+ * @brief Notification of a change to the audio gateway's
+ * in-band ring tone setting
+ *
+ * @param bool Set to @em true if the audio gateway has enabled
+ * in-band ring tones, @em false otherwise
+ *
+ * The in-band ring tone setting can also be accessed via
+ * GetInBandRingToneEnable()
+ *
+ * @sa Bluetooth HFP v1.5 section 4.13.4
+ */
+ Callback<void, HfpSession *, bool> cb_NotifyInBandRingTone;
+
private:
/* Response buffer */
enum { RFCOMM_MAX_LINELEN = 512 };
@@ -979,37 +1091,11 @@ public:
* - There are no available Bluetooth HCIs.
* - The RFCOMM socket could not be created, e.g. because part or
* all of the bluetooth stack could not be loaded.
- * @sa Disconnect(), SetAutoReconnect()
+ * @sa Disconnect(), RfcommSession::SetAutoReconnect()
* @sa HfpSession::cb_NotifyConnection
*/
bool Connect(ErrorInfo *error = 0);
- /**
- * @brief Query whether the autoreconnect mechanism is enabled for
- * this device
- *
- * @retval true Autoreconnect is enabled
- * @retval false Autoreconnect is disabled.
- * @sa SetAutoReconnect()
- */
- bool IsAutoReconnect(void) const { return m_conn_autoreconnect; }
-
- /**
- * @brief Enable or disable the autoreconnect mechanism for this
- * device
- *
- * If enabled, whenever the device is disconnected, a reconnection
- * attempt will be made periodically through a timer.
- * Auto-reconnection is useful for phones that regularly move in
- * and out of range.
- *
- * This function can affect the @ref aglifecycle "life cycle management"
- * of the object it is called on.
- * @param enable Set to true to enable, false to disable.
- * @sa IsAutoReconnect(), Connect()
- */
- void SetAutoReconnect(bool enable);
-
/*
* Call state queries
@@ -1027,7 +1113,7 @@ public:
/**
* @brief Query whether the device has an established call
*
- * @retval true Established call exists
+ * @retval true Established call, either active or on hold, exists
* @retval false No established call exists
*/
bool HasEstablishedCall(void) const {
@@ -1047,14 +1133,15 @@ public:
* @brief Retrieve the caller ID value of the last incomplete call,
* either incoming or outgoing
*
- * The string value may be used
- * until the global event handler is invoked again or
- * CmdDial() / CmdRedial() is invoked.
- * @return String pointer to the remote identity of the last
- * incomplete call, or NULL if the identity was not known.
+ * @return a GsmClipResult object containing the calling line
+ * identity, or NULL if the identity is not known, or there is no
+ * waiting or unanswered call. This object's contents are described
+ * in GSM 07.07 7.6. This object may be used until the global
+ * event handler is invoked again or CmdDial() / CmdRedial() is
+ * invoked.
*/
- const GsmClipPhoneNumber *WaitingCallIdentity(void) const {
- return m_state_incomplete_clip;
+ const GsmClipResult *WaitingCallIdentity(void) const {
+ return m_clip_value;
}
@@ -1133,6 +1220,71 @@ public:
*/
int GetBatteryCharge(void) const { return m_state_battchg; }
+ /**
+ * @brief Query the voice recognition state
+ *
+ * If the audio gateway supports voice recognition, and is
+ * connected, it will use the microphone of the hands-free to
+ * listen for commands. Voice recognition can be activated
+ * directly on the audio gateway, or via CmdSetVoiceRecog().
+ * Voice recognition can be deactivated when the audio gateway
+ * has recognized a voice command, when the audio gateway times
+ * out listening for a command, or via CmdSetVoiceRecog().
+ *
+ * Audio gateway support for voice recognition can be tested
+ * via FeatureVoiceRecog().
+ *
+ * @retval true Voice recognition activated
+ * @retval false Voice recognition deactivated or unsupported
+ *
+ * @note This function will only return meaningful values
+ * when the device is in the connected state.
+ */
+ bool GetVoiceRecogActive(void) const { return m_state_bvra; }
+
+ /**
+ * @brief Query the microphone gain level
+ *
+ * If the audio gateway supports remote volume control, this
+ * method can be used to retrieve the microphone volume level
+ * synchronized with the audio gateway.
+ *
+ * @return The synchronized microphone volume level, 0-15,
+ * or -1 if the microphone volume level is unsynchronized.
+ */
+ int GetVolumeMic(void) const { return m_state_vgm; }
+
+ /**
+ * @brief Query the speaker gain level
+ *
+ * If the audio gateway supports remote volume control, this
+ * method can be used to retrieve the speaker volume level
+ * synchronized with the audio gateway.
+ *
+ * @return The synchronized speaker volume level, 0-15,
+ * or -1 if the speaker volume level is unsynchronized.
+ */
+ int GetVolumeSpeaker(void) const { return m_state_vgs; }
+
+ /**
+ * @brief Query the in-band ring tone setting
+ *
+ * If the audio gateway supports in-band ring tones, it will
+ * send its in-band ring tone state on connection, and when it
+ * is changed. This accessor method can be used to query the
+ * last known value.
+ *
+ * The in-band ring tone setting cannot be changed from the
+ * hands-free; it can only be set on the audio gateway.
+ *
+ * @retval 0 In-band ring tones disabled or unsupported
+ * @retval 1 In-band ring tones enabled
+ *
+ * @note This function will only return meaningful values
+ * when the device is in the connected state.
+ */
+ bool GetInBandRingToneEnable(void) const { return m_state_bsir; }
+
/*
* Reported feature presence queries
@@ -1181,7 +1333,6 @@ public:
*
* @note This information is only valid when the device is in the
* connected state.
- * @todo Voice recognition is not yet supported by libhfp.
*
* @retval true Voice recognition supported
* @retval false Voice recognition not supported
@@ -1225,10 +1376,12 @@ public:
{ return (m_brsf & 256) ? true : false; }
bool FeatureIndCallSetup(void) const
{ return (m_inum_callsetup != 0); }
+ bool FeatureIndCallHeld(void) const
+ { return (m_inum_callheld != 0); }
bool FeatureDropHeldUdub(void) const { return m_chld_0; }
bool FeatureSwapDropActive(void) const { return m_chld_1; }
- bool FeatureDropActive(void) const { return m_chld_1x; }
+ bool FeatureDropIndex(void) const { return m_chld_1x; }
bool FeatureSwapHoldActive(void) const { return m_chld_2; }
bool FeaturePrivateConsult(void) const { return m_chld_2x; }
bool FeatureLink(void) const { return m_chld_3; }
@@ -1293,8 +1446,93 @@ public:
*/
bool IsCommandPending(void) const { return !m_commands.Empty(); }
- HfpPendingCommand *CmdSetVoiceRecog(bool enabled,
+ /**
+ * @brief Query the telephone number of the audio gateway
+ *
+ * @param[out] error Error information structure. If this method
+ * fails and returns @c 0, and @em error is not 0, @em error
+ * will be filled out with information on the cause of the failure.
+ * @return An HfpPendingCommand to receive a notification when
+ * the command completes, with the command status, or @c 0 if
+ * the command could not be queued, e.g. because the device
+ * connection was lost
+ *
+ * On success, the last parameter of the callback method to the
+ * resulting HfpPendingCommand object will be passed a
+ * GsmCnumResult structure. The object will be deleted after
+ * the callback method returns, so the callback method must
+ * duplicate the object if it wishes to reference it later.
+ */
+ HfpPendingCommand *CmdQueryNumber(ErrorInfo *error = 0);
+
+ /**
+ * @brief Query operator information from the audio gateway
+ *
+ * @param[out] error Error information structure. If this method
+ * fails and returns @c 0, and @em error is not 0, @em error
+ * will be filled out with information on the cause of the failure.
+ * @return An HfpPendingCommand to receive a notification when
+ * the command completes, with the command status, or @c 0 if
+ * the command could not be queued, e.g. because the device
+ * connection was lost
+ *
+ * On success, the last parameter of the callback method to the
+ * resulting HfpPendingCommand object will be passed a
+ * GsmCopsResult structure. The object will be deleted after
+ * the callback method returns, so the callback method must
+ * duplicate the object if it wishes to reference it later.
+ */
+ HfpPendingCommand *CmdQueryOperator(ErrorInfo *error = 0);
+
+ /**
+ * @brief Query current calls from the audio gateway
+ *
+ * @param[out] error Error information structure. If this method
+ * fails and returns @c 0, and @em error is not 0, @em error
+ * will be filled out with information on the cause of the failure.
+ * @return An HfpPendingCommand to receive a notification when
+ * the command completes, with the command status, or @c 0 if
+ * the command could not be queued, e.g. because the device
+ * connection was lost
+ *
+ * On success, the last parameter of the callback method to the
+ * resulting HfpPendingCommand object will be passed a pointer to
+ * a ListItem, which is the head of a list of GsmClccResult
+ * objects. The list head and all objects on the list will be
+ * deleted after the callback method returns, so the callback
+ * method must duplicate the objects that it wishes to reference
+ * later.
+ */
+ HfpPendingCommand *CmdQueryCurrentCalls(ErrorInfo *error = 0);
+
+ /**
+ * @brief Request that the audio gateway activate or deactivate
+ * voice recognition
+ *
+ * @note This command may only be expected to succeed if the device
+ * claims support for voice recognition,
+ * i.e. FeatureVoiceRecog() returns true.
+ *
+ * @param[in] active Set to @em true to request activation of
+ * voice recognition mode, @em false to request deactivation.
+ * @param[out] error Error information structure. If this method
+ * fails and returns @c 0, and @em error is not 0, @em error
+ * will be filled out with information on the cause of the failure.
+ * @return An HfpPendingCommand to receive a notification when
+ * the command completes, with the command status, or @c 0 if
+ * the command could not be queued, e.g. because the device
+ * connection was lost
+ *
+ * If successful, the cb_NotifyVoiceRecog callback will be
+ * invoked with the new voice recognition state.
+ */
+ HfpPendingCommand *CmdSetVoiceRecog(bool active,
ErrorInfo *error = 0);
+
+ HfpPendingCommand *CmdSetVolumeMic(uint8_t vol, ErrorInfo *error = 0);
+ HfpPendingCommand *CmdSetVolumeSpeaker(uint8_t vol,
+ ErrorInfo *error = 0);
+
HfpPendingCommand *CmdSetEcnr(bool enabled, ErrorInfo *error = 0);
/**
@@ -1408,11 +1646,11 @@ public:
HfpPendingCommand *CmdCallSwapDropActive(ErrorInfo *error = 0);
/**
- * @brief Drop a specific active call
+ * @brief Drop a specific call
*
* @note This command may only be expected to succeed if the
- * device supports dropping of specific active calls, i.e.
- * FeatureDropActive() returns true.
+ * device supports dropping of specific calls, i.e.
+ * FeatureDropIndex() returns true.
* @param[in] actnum Call number to be dropped
* @param[out] error Error information structure. If this method
* fails and returns @c 0, and @em error is not 0, @em error
@@ -1422,8 +1660,8 @@ public:
* the command could not be queued, e.g. because the device
* connection was lost
*/
- HfpPendingCommand *CmdCallDropActive(unsigned int actnum,
- ErrorInfo *error = 0);
+ HfpPendingCommand *CmdCallDropIndex(unsigned int actnum,
+ ErrorInfo *error = 0);
/**
* @brief Request that the audio gateway hold the active call
diff --git a/include/libhfp/rfcomm.h b/include/libhfp/rfcomm.h
index 22b0150..c3a5218 100644
--- a/include/libhfp/rfcomm.h
+++ b/include/libhfp/rfcomm.h
@@ -105,9 +105,25 @@ protected:
/* Call me from Stop() */
void RfcommCleanup(void);
+ ListItem m_autoreconnect_list;
+ int m_autoreconnect_timeout;
+ bool m_autoreconnect_set;
+ TimerNotifier *m_autoreconnect_timer;
+
+ ListItem m_autoreconnect_now_list;
+ bool m_autoreconnect_now_set;
+ TimerNotifier *m_autoreconnect_now_timer;
+
+ void AddAutoReconnect(RfcommSession *sessp, bool now = false);
+ void RemoveAutoReconnect(RfcommSession *sessp);
+ void AutoReconnectTimeout(TimerNotifier*);
+
RfcommService(uint16_t search_svclass_id = 0);
virtual ~RfcommService();
+ bool Start(ErrorInfo *error);
+ void Stop(void);
+
public:
RfcommSession *GetSession(BtDevice *devp, bool create = true);
RfcommSession *GetSession(bdaddr_t const &addr, bool create = true);
@@ -178,11 +194,12 @@ protected:
bool RfcommSdpLookupChannel(ErrorInfo *error);
void RfcommSdpLookupChannelComplete(SdpTask *taskp);
- bool RfcommConnect(uint8_t channel, ErrorInfo *error);
+ bool RfcommConnect(uint8_t channel, ErrorInfo *error,
+ int timeout = 15000);
void RfcommConnectNotify(SocketNotifier *notp, int fh);
/* This is the primary overload and handles SDP channel lookups */
- bool RfcommConnect(ErrorInfo *error);
+ bool RfcommConnect(ErrorInfo *error, int timeout = 30000);
virtual bool RfcommAccept(int sock);
@@ -190,6 +207,18 @@ protected:
SocketNotifier *m_rfcomm_not;
rfcomm_secmode_t m_rfcomm_secmode;
+ bool m_conn_autoreconnect;
+ ListItem m_autoreconnect_links;
+
+ virtual void AutoReconnect(void) = 0;
+
+ TimerNotifier *m_operation_timeout;
+
+ bool RfcommSetOperationTimeout(int ms, ErrorInfo *error);
+ void RfcommOperationTimeout(TimerNotifier *);
+
+ bool RfcommSend(const uint8_t *buf, size_t len, ErrorInfo *error);
+
RfcommSession(RfcommService *svcp, BtDevice *devp);
virtual ~RfcommSession();
@@ -222,7 +251,6 @@ protected:
return (err == ECONNRESET);
}
-
public:
RfcommService *GetService(void) const
{ return (RfcommService*) BtSession::GetService(); }
@@ -256,6 +284,32 @@ public:
rfcomm_secmode_t GetSecMode(void) const { return m_rfcomm_secmode; }
/**
+ * @brief Query whether the autoreconnect mechanism is enabled for
+ * this device
+ *
+ * @retval true Autoreconnect is enabled
+ * @retval false Autoreconnect is disabled.
+ * @sa SetAutoReconnect()
+ */
+ bool IsAutoReconnect(void) const { return m_conn_autoreconnect; }
+
+ /**
+ * @brief Enable or disable the autoreconnect mechanism for this
+ * device
+ *
+ * If enabled, whenever the device is disconnected, a reconnection
+ * attempt will be made periodically through a timer.
+ * Auto-reconnection is useful for devices such as phones that
+ * regularly move in and out of range.
+ *
+ * This function can affect the @ref aglifecycle "life cycle management"
+ * of the object it is called on.
+ * @param enable Set to true to enable, false to disable.
+ * @sa IsAutoReconnect(), Connect()
+ */
+ void SetAutoReconnect(bool enable);
+
+ /**
* @brief Query whether an in-progress or complete connection to the
* device was initiated by the device
*
diff --git a/libhfp/hfp.cpp b/libhfp/hfp.cpp
index f74cd98..11f0145 100644
--- a/libhfp/hfp.cpp
+++ b/libhfp/hfp.cpp
@@ -55,9 +55,6 @@ HfpService(int caps)
m_sco_listen(-1), m_sco_listen_not(0),
m_brsf_my_caps(caps), m_svc_name(0), m_svc_desc(0),
m_sdp_rec(0), m_sco_enable(true),
- m_autoreconnect_timeout(15000), m_autoreconnect_set(false),
- m_autoreconnect_timer(0),
- m_autoreconnect_now_set(false), m_autoreconnect_now_timer(0),
m_complaint_sco_mtu(false), m_complaint_sco_vs(false),
m_complaint_sco_listen(false)
{
@@ -74,85 +71,6 @@ HfpService::
free(m_svc_desc);
m_svc_desc = 0;
}
- assert(!m_autoreconnect_timer);
- assert(!m_autoreconnect_now_timer);
-}
-
-void HfpService::
-AutoReconnectTimeout(TimerNotifier *timerp)
-{
- ListItem retrylist;
-
- if (timerp == m_autoreconnect_timer) {
- assert(m_autoreconnect_set);
- m_autoreconnect_set = false;
- retrylist.AppendItemsFrom(m_autoreconnect_list);
-
- } else {
- assert(timerp == m_autoreconnect_now_timer);
- assert(m_autoreconnect_now_set);
- m_autoreconnect_now_set = false;
- retrylist.AppendItemsFrom(m_autoreconnect_now_list);
- }
-
- while (!retrylist.Empty()) {
- HfpSession *sessp = GetContainer(retrylist.next, HfpSession,
- m_autoreconnect_links);
- sessp->m_autoreconnect_links.UnlinkOnly();
-
- /* Always append to the delayed list */
- m_autoreconnect_list.AppendItem(sessp->m_autoreconnect_links);
-
- sessp->AutoReconnect();
- }
-
- if (!m_autoreconnect_list.Empty() && !m_autoreconnect_set) {
- m_autoreconnect_set = true;
- m_autoreconnect_timer->Set(m_autoreconnect_timeout);
- }
-}
-
-void HfpService::
-AddAutoReconnect(HfpSession *sessp, bool now)
-{
- assert(sessp->m_autoreconnect_links.Empty());
- assert(!sessp->IsConnected() && !sessp->IsConnecting());
-
- if (now) {
- if (m_autoreconnect_now_timer && !m_autoreconnect_now_set) {
- m_autoreconnect_now_set = true;
- m_autoreconnect_now_timer->Set(0);
- }
- m_autoreconnect_now_list.AppendItem(sessp->
- m_autoreconnect_links);
- } else {
- if (m_autoreconnect_timer && !m_autoreconnect_set) {
- m_autoreconnect_set = true;
- m_autoreconnect_timer->Set(m_autoreconnect_timeout);
- }
- m_autoreconnect_list.AppendItem(sessp->m_autoreconnect_links);
- }
-}
-
-void HfpService::
-RemoveAutoReconnect(HfpSession *sessp)
-{
- assert(!sessp->m_autoreconnect_links.Empty());
- sessp->m_autoreconnect_links.Unlink();
-
- if (m_autoreconnect_timer &&
- m_autoreconnect_list.Empty() &&
- m_autoreconnect_set) {
- m_autoreconnect_set = false;
- m_autoreconnect_timer->Cancel();
- }
-
- if (m_autoreconnect_now_timer &&
- m_autoreconnect_now_list.Empty() &&
- m_autoreconnect_now_set) {
- m_autoreconnect_now_set = false;
- m_autoreconnect_now_timer->Cancel();
- }
}
bool HfpService::
@@ -454,7 +372,7 @@ SdpRegister(ErrorInfo *error)
root_list = 0;
/* Add one last required attribute */
- caps = m_brsf_my_caps;
+ caps = m_brsf_my_caps & 0x1f;
if (sdp_attr_add_new(svcrec, SDP_ATTR_SUPPORTED_FEATURES,
SDP_UINT16, &caps) < 0)
goto nomem;
@@ -491,26 +409,9 @@ bool HfpService::
Start(ErrorInfo *error)
{
assert(GetHub());
- assert(!m_autoreconnect_timer);
- assert(!m_autoreconnect_now_timer);
-
- m_autoreconnect_timer = GetDi()->NewTimer();
- if (!m_autoreconnect_timer) {
- if (error)
- error->SetNoMem();
- return false;
- }
- m_autoreconnect_timer->Register(this,
- &HfpService::AutoReconnectTimeout);
- m_autoreconnect_now_timer = GetDi()->NewTimer();
- if (!m_autoreconnect_now_timer) {
- if (error)
- error->SetNoMem();
+ if (!RfcommService::Start(error))
goto failed;
- }
- m_autoreconnect_now_timer->Register(this,
- &HfpService::AutoReconnectTimeout);
if (!RfcommListen(error))
goto failed;
@@ -521,16 +422,6 @@ Start(ErrorInfo *error)
if (!SdpRegister(error))
goto failed;
- if (!m_autoreconnect_list.Empty() && !m_autoreconnect_set) {
- m_autoreconnect_set = true;
- m_autoreconnect_timer->Set(0);
- }
-
- if (!m_autoreconnect_now_list.Empty() && !m_autoreconnect_now_set) {
- m_autoreconnect_now_set = true;
- m_autoreconnect_now_timer->Set(0);
- }
-
return true;
failed:
@@ -546,16 +437,7 @@ Stop(void)
SdpUnregister();
RfcommCleanup();
ScoCleanup();
- if (m_autoreconnect_timer) {
- m_autoreconnect_set = false;
- delete m_autoreconnect_timer;
- m_autoreconnect_timer = 0;
- }
- if (m_autoreconnect_now_timer) {
- m_autoreconnect_now_set = false;
- delete m_autoreconnect_now_timer;
- m_autoreconnect_now_timer = 0;
- }
+ RfcommService::Stop();
}
RfcommSession * HfpService::
@@ -649,6 +531,26 @@ SetScoEnabled(bool sco_enable, ErrorInfo *error)
}
bool HfpService::
+SetCaps(int caps, ErrorInfo *error)
+{
+ int old_caps = m_brsf_my_caps;
+
+ m_brsf_my_caps = caps;
+
+ if ((m_sdp_rec != 0) && ((old_caps ^ caps) & 0x1f)) {
+ SdpUnregister();
+ if (!SdpRegister(error)) {
+ /* Now we're just screwed! */
+ m_brsf_my_caps = old_caps;
+ (void) SdpRegister(0);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool HfpService::
SetServiceName(const char *val, ErrorInfo *error)
{
char *oldval, *newval = 0;
@@ -667,14 +569,16 @@ SetServiceName(const char *val, ErrorInfo *error)
}
}
- SdpUnregister();
- if ((m_sco_listen >= 0) && !SdpRegister(error)) {
- /* Now we're just screwed! */
- m_svc_name = oldval;
- if (newval)
- free(newval);
- (void) SdpRegister(0);
- return false;
+ if (m_sdp_rec != 0) {
+ SdpUnregister();
+ if (!SdpRegister(error)) {
+ /* Now we're just screwed! */
+ m_svc_name = oldval;
+ if (newval)
+ free(newval);
+ (void) SdpRegister(0);
+ return false;
+ }
}
if (oldval)
@@ -701,13 +605,15 @@ SetServiceDesc(const char *val, ErrorInfo *error)
}
}
- SdpUnregister();
- if ((m_sco_listen >= 0) && !SdpRegister(error)) {
- m_svc_desc = oldval;
- if (newval)
- free(newval);
- (void) SdpRegister(0);
- return false;
+ if (m_sdp_rec != 0) {
+ SdpUnregister();
+ if (!SdpRegister(error)) {
+ m_svc_desc = oldval;
+ if (newval)
+ free(newval);
+ (void) SdpRegister(0);
+ return false;
+ }
}
if (oldval)
@@ -718,20 +624,23 @@ SetServiceDesc(const char *val, ErrorInfo *error)
HfpSession::
HfpSession(HfpService *svcp, BtDevice *devp)
: RfcommSession(svcp, devp), m_conn_state(BTS_Disconnected),
- m_conn_autoreconnect(false),
+ m_command_timer(0),
m_chld_0(false), m_chld_1(false), m_chld_1x(false),
m_chld_2(false), m_chld_2x(false),m_chld_3(false), m_chld_4(false),
m_clip_enabled(false), m_ccwa_enabled(false),
m_inum_service(0), m_inum_call(0), m_inum_callsetup(0),
- m_inum_signal(0), m_inum_roam(0), m_inum_battchg(0),
+ m_inum_callheld(0), m_inum_signal(0), m_inum_roam(0),
+ m_inum_battchg(0),
m_inum_names(NULL), m_inum_names_len(0),
m_state_service(false), m_state_call(false), m_state_callsetup(0),
m_state_signal(-1), m_state_roam(-1), m_state_battchg(-1),
- m_state_incomplete_clip(0),
+ m_state_bvra(false), m_state_bsir(false), m_state_ecnr(false),
+ m_state_vgm(-1), m_state_vgs(-1),
m_sco_state(BVS_Invalid), m_sco_sock(-1), m_sco_nonblock(false),
- m_sco_not(0), m_timer(0),
+ m_sco_not(0), m_callsetup_presumed(false), m_timer(0),
+ m_clip_timer(0), m_clip_state(CLIP_UNKNOWN), m_clip_value(0),
m_timeout_ring(5000), m_timeout_ring_ccwa(20000),
- m_timeout_dial(20000),
+ m_timeout_dial(20000), m_timeout_clip(250), m_timeout_command(30000),
m_rsp_start(0), m_rsp_len(0)
{
}
@@ -739,7 +648,18 @@ HfpSession(HfpService *svcp, BtDevice *devp)
HfpSession::
~HfpSession()
{
- assert(!m_timer);
+ if (m_timer) {
+ delete m_timer;
+ m_timer = 0;
+ }
+ if (m_clip_timer) {
+ delete m_clip_timer;
+ m_clip_timer = 0;
+ }
+ if (m_command_timer) {
+ delete m_command_timer;
+ m_command_timer = 0;
+ }
assert(m_conn_state == BTS_Disconnected);
assert(m_commands.Empty());
assert(!m_inum_names);
@@ -771,21 +691,36 @@ NotifyConnectionState(ErrorInfo *async_error)
(m_conn_state == BTS_Disconnected));
m_conn_state = BTS_Handshaking;
- if (!HfpHandshake(&local_error))
+ if (!m_timer) {
+ m_timer = GetDi()->NewTimer();
+ if (m_timer)
+ m_timer->Register(this, &HfpSession::Timeout);
+ }
+ if (!m_clip_timer) {
+ m_clip_timer = GetDi()->NewTimer();
+ if (m_clip_timer)
+ m_clip_timer->Register(this,
+ &HfpSession::ClipTimeout);
+ }
+ if (!m_command_timer) {
+ m_command_timer = GetDi()->NewTimer();
+ if (m_command_timer)
+ m_command_timer->Register(this,
+ &HfpSession::CommandTimeout);
+ }
+
+ if (!m_timer || !m_clip_timer || !m_command_timer) {
+ local_error.SetNoMem();
__Disconnect(&local_error, false);
+ }
- /*
- * We might need to disable autoreconnect for this
- * device if the state change is the result of a
- * remotely initiated connection.
- */
- if (!m_autoreconnect_links.Empty()) {
- assert(m_conn_autoreconnect);
- GetService()->RemoveAutoReconnect(this);
+ if (IsRfcommConnected() && !HfpHandshake(&local_error)) {
+ __Disconnect(&local_error, false);
}
- else if (IsConnectionRemoteInitiated() &&
- cb_NotifyConnection.Registered())
+ if (IsRfcommConnected() &&
+ IsConnectionRemoteInitiated() &&
+ cb_NotifyConnection.Registered())
cb_NotifyConnection(this, 0);
}
@@ -794,6 +729,7 @@ NotifyConnectionState(ErrorInfo *async_error)
assert(m_conn_state == BTS_Disconnected);
assert(!IsConnectingAudio());
assert(!IsConnectedAudio());
+ UpdateCallSetup(0);
}
if (async_error && cb_NotifyConnection.Registered())
@@ -825,25 +761,19 @@ __Disconnect(ErrorInfo *reason, bool voluntary)
if (m_conn_state != BTS_Disconnected) {
m_conn_state = BTS_Disconnected;
- if (m_conn_autoreconnect)
- GetService()->AddAutoReconnect(this);
}
m_rsp_start = m_rsp_len = 0;
m_chld_0 = m_chld_1 = m_chld_1x = m_chld_2 = m_chld_2x = m_chld_3 =
m_chld_4 = false;
m_clip_enabled = false;
m_ccwa_enabled = false;
- m_inum_service = m_inum_call = m_inum_callsetup = 0;
+ m_inum_service = m_inum_call = m_inum_callsetup = m_inum_callheld = 0;
m_inum_signal = m_inum_roam = m_inum_battchg = 0;
m_state_service = false;
m_state_call = false;
- m_state_callsetup = 0;
m_state_signal = m_state_roam = m_state_battchg = -1;
-
- if (m_state_incomplete_clip) {
- delete m_state_incomplete_clip;
- m_state_incomplete_clip = 0;
- }
+ m_state_bvra = m_state_bsir = m_state_ecnr = false;
+ m_state_vgm = m_state_vgs = -1;
RfcommSession::__Disconnect(reason, voluntary);
}
@@ -855,21 +785,16 @@ Connect(ErrorInfo *error)
return true;
m_conn_state = BTS_RfcommConnecting;
- if (m_conn_autoreconnect)
- GetService()->RemoveAutoReconnect(this);
if (RfcommConnect(error))
return true;
m_conn_state = BTS_Disconnected;
- if (m_conn_autoreconnect)
- GetService()->AddAutoReconnect(this);
return false;
}
void HfpSession::
AutoReconnect(void)
{
- assert(m_conn_autoreconnect);
assert(m_conn_state == BTS_Disconnected);
if (Connect() && cb_NotifyConnection.Registered())
@@ -885,23 +810,6 @@ AutoReconnect(void)
}
void HfpSession::
-SetAutoReconnect(bool enable)
-{
- if (enable && !m_conn_autoreconnect) {
- Get();
- m_conn_autoreconnect = true;
- if (m_conn_state == BTS_Disconnected)
- GetService()->AddAutoReconnect(this, true);
- }
- else if (!enable && m_conn_autoreconnect) {
- m_conn_autoreconnect = false;
- if (m_conn_state == BTS_Disconnected)
- GetService()->RemoveAutoReconnect(this);
- Put();
- }
-}
-
-void HfpSession::
SdpSupportedFeatures(uint16_t features)
{
assert(!IsConnected());
@@ -1505,8 +1413,7 @@ class AtCommand {
bool iResponse(const char *buf) {
if (!strcmp(buf, "OK")) {
- OK();
- return true;
+ return OK();
}
if (!strcmp(buf, "ERROR")) {
ERROR();
@@ -1522,7 +1429,7 @@ class AtCommand {
protected:
HfpSession *GetSession(void) const { return m_sess; }
DispatchInterface *GetDi(void) const { return m_sess->GetDi(); }
- void CompletePending(ErrorInfo *error, const char *info) {
+ void CompletePending(ErrorInfo *error, void *info) {
HfpPendingCommand *cmdp = m_pend;
if (cmdp) {
m_pend = 0;
@@ -1533,8 +1440,9 @@ public:
const char *m_command_text;
virtual bool Response(const char *buf)
{ return false; };
- virtual void OK(void) {
+ virtual bool OK() {
CompletePending(0, 0);
+ return true;
}
virtual void ERROR(void) {
ErrorInfo simple_error;
@@ -1808,7 +1716,8 @@ ParseGsmStringField(char *&buf, const char *&result)
static bool
ParseGsmIntField(char *&buf, int &result)
{
- char *xbuf, *endp;
+ char *xbuf;
+ char *endp;
int value;
xbuf = buf;
@@ -1816,7 +1725,6 @@ ParseGsmIntField(char *&buf, int &result)
if (!*xbuf)
return false;
-
value = strtol(xbuf, &endp, 10);
if (endp != xbuf) {
result = value;
@@ -1825,7 +1733,6 @@ ParseGsmIntField(char *&buf, int &result)
while (*xbuf && IsWS(*xbuf)) { xbuf++; }
if (*xbuf == ',') {
- *xbuf = '\0';
xbuf++;
}
else if (*xbuf) {
@@ -1836,45 +1743,43 @@ ParseGsmIntField(char *&buf, int &result)
return true;
}
-void *GsmClipPhoneNumber::
-operator new(size_t nb, size_t extra)
+void *GsmResult::
+operator new(size_t nb, const char *src_string, size_t extra)
{
- return malloc(nb + extra);
+ void *mem;
+ char *cbuf;
+
+ if (!src_string)
+ src_string = "";
+ if (!extra)
+ extra = strlen(src_string) + 1;
+ mem = malloc(nb + extra);
+ if (mem) {
+ memset(mem, 0, nb);
+ cbuf = ((char *) mem) + nb;
+ memcpy(cbuf, src_string, extra);
+ ((GsmResult *) mem)->src_string = cbuf;
+ ((GsmResult *) mem)->extra = extra;
+ }
+ return mem;
}
-void GsmClipPhoneNumber::
+void GsmResult::
operator delete(void *mem)
{
free(mem);
}
-GsmClipPhoneNumber *GsmClipPhoneNumber::
-Create(const char *src)
-{
- GsmClipPhoneNumber *res;
- size_t extra;
-
- extra = strlen(src) + 1;
- res = new (extra) GsmClipPhoneNumber;
- if (!res)
- return 0;
-
- strcpy((char *)(res + 1), src);
- memset(res, 0, sizeof(*res));
- res->extra = extra;
- return res;
-}
-
-GsmClipPhoneNumber *GsmClipPhoneNumber::
+GsmClipResult *GsmClipResult::
Parse(const char *clip)
{
- GsmClipPhoneNumber *res;
+ GsmClipResult *res;
char *buf;
- res = Create(clip);
+ res = new (clip, 0) GsmClipResult;
if (!res)
return 0;
- buf = (char *) (res + 1);
+ buf = res->src_string;
if (!ParseGsmStringField(buf, res->number))
goto bad;
@@ -1905,17 +1810,17 @@ bad:
return 0;
}
-GsmClipPhoneNumber *GsmClipPhoneNumber::
+GsmClipResult *GsmClipResult::
ParseCcwa(const char *clip)
{
- GsmClipPhoneNumber *res;
+ GsmClipResult *res;
char *buf;
int class_drop;
- res = Create(clip);
+ res = new (clip, 0) GsmClipResult;
if (!res)
return 0;
- buf = (char *) (res + 1);
+ buf = res->src_string;
if (!ParseGsmStringField(buf, res->number))
goto bad;
@@ -1942,10 +1847,10 @@ bad:
return 0;
}
-bool GsmClipPhoneNumber::
-Compare(const GsmClipPhoneNumber *clip) const
+bool GsmClipResult::
+Compare(const GsmClipResult *clip) const
{
- const GsmClipPhoneNumber *src = this;
+ const GsmClipResult *src = this;
if (src->number || clip->number) {
if (!src->number || !clip->number)
return false;
@@ -1971,20 +1876,21 @@ Compare(const GsmClipPhoneNumber *clip) const
return true;
}
-GsmClipPhoneNumber *GsmClipPhoneNumber::
+GsmClipResult *GsmClipResult::
Duplicate(void) const
{
- const GsmClipPhoneNumber *src = this;
- GsmClipPhoneNumber *res;
+ const GsmClipResult *src = this;
+ GsmClipResult *res;
const char *from;
char *buf;
- res = new (src->extra) GsmClipPhoneNumber;
+ res = new (src->src_string, src->extra) GsmClipResult;
if (!res)
return 0;
+ buf = res->src_string;
+ from = src->src_string;
*res = *src;
- buf = (char *) (res + 1);
- from = (const char *) (src + 1);
+ res->src_string = buf;
memcpy(buf, from, src->extra);
if (src->number)
res->number = buf + (src->number - from);
@@ -1995,10 +1901,145 @@ Duplicate(void) const
return res;
}
+GsmCnumResult *GsmCnumResult::
+Parse(const char *cops)
+{
+ GsmCnumResult *res;
+ char *buf;
+
+ res = new (cops, 0) GsmCnumResult;
+ if (!res)
+ return 0;
+ buf = res->src_string;
+
+ if (!ParseGsmStringField(buf, res->alpha))
+ goto bad;
+ if (!*buf)
+ goto bad;
+ if (!ParseGsmStringField(buf, res->number))
+ goto bad;
+ if (!*buf)
+ goto bad;
+ if (!ParseGsmIntField(buf, res->type))
+ goto bad;
+ if (!*buf)
+ return res;
+ if (!ParseGsmIntField(buf, res->speed))
+ goto bad;
+ if (!*buf)
+ return res;
+ if (!ParseGsmIntField(buf, res->service))
+ goto bad;
+ return res;
+
+bad:
+ delete res;
+ return 0;
+}
+
+GsmCopsResult *GsmCopsResult::
+Parse(const char *cops)
+{
+ GsmCopsResult *res;
+ char *buf;
+
+ res = new (cops, 0) GsmCopsResult;
+ if (!res)
+ return 0;
+ buf = res->src_string;
+
+ if (!ParseGsmIntField(buf, res->mode))
+ goto bad;
+ if (!*buf)
+ return res;
+ if (!ParseGsmIntField(buf, res->format))
+ goto bad;
+ if (!*buf)
+ goto bad;
+ switch (res->format) {
+ case 0:
+ case 1:
+ if (!ParseGsmStringField(buf, res->alpha))
+ goto bad;
+ break;
+ case 2:
+ if (!ParseGsmIntField(buf, res->nonalpha))
+ goto bad;
+ break;
+ default:
+ goto bad;
+ }
+ return res;
+
+bad:
+ delete res;
+ return 0;
+}
+
+GsmClccResult *GsmClccResult::
+Parse(const char *clcc)
+{
+ GsmClccResult *res;
+ char *buf;
+
+ res = new (clcc, 0) GsmClccResult;
+ if (!res)
+ return 0;
+ buf = res->src_string;
+
+ if (!ParseGsmIntField(buf, res->idx))
+ goto bad;
+ if (!*buf)
+ goto bad;
+ if (!ParseGsmIntField(buf, res->dir))
+ goto bad;
+ if (!*buf)
+ goto bad;
+ if (!ParseGsmIntField(buf, res->stat))
+ goto bad;
+ if (!*buf)
+ goto bad;
+ if (!ParseGsmIntField(buf, res->mode))
+ goto bad;
+ if (!*buf)
+ goto bad;
+ if (!ParseGsmIntField(buf, res->mpty))
+ goto bad;
+ if (!*buf)
+ return res;
+ if (!ParseGsmStringField(buf, res->number))
+ goto bad;
+ if (!*buf)
+ goto bad;
+ if (!ParseGsmIntField(buf, res->type))
+ goto bad;
+ if (!*buf)
+ return res;
+ if (!ParseGsmStringField(buf, res->alpha))
+ goto bad;
+ return res;
+
+bad:
+ delete res;
+ return 0;
+}
+
+
+void HfpSession::
+SetBvra(bool active)
+{
+ if (m_state_bvra != active) {
+ m_state_bvra = active;
+ if (cb_NotifyVoiceRecog.Registered())
+ cb_NotifyVoiceRecog(this, active);
+ }
+}
+
void HfpSession::
ResponseDefault(char *buf)
{
- GsmClipPhoneNumber *phnum;
+ char *end;
+ GsmClipResult *phnum;
int indnum;
if (!strncmp(buf, "+CIEV:", 6)) {
@@ -2030,20 +2071,72 @@ ResponseDefault(char *buf)
else if (!strncmp(buf, "+CLIP:", 6) && IsConnected()) {
/* Line identification for incoming call */
- phnum = GsmClipPhoneNumber::Parse(buf + 6);
+ phnum = GsmClipResult::Parse(buf + 6);
if (!phnum)
GetDi()->LogWarn("Parse error on CLIP");
- UpdateCallSetup(1, 1, phnum, m_timeout_ring);
+ UpdateCallSetup(1, 0, phnum, m_timeout_ring);
delete phnum;
}
else if (!strncmp(buf, "+CCWA:", 6) && IsConnected()) {
/* Call waiting + line identification for call waiting */
- phnum = GsmClipPhoneNumber::ParseCcwa(buf + 6);
+ phnum = GsmClipResult::ParseCcwa(buf + 6);
if (!phnum)
GetDi()->LogWarn("Parse error on CCWA");
UpdateCallSetup(1, 2, phnum, m_timeout_ring_ccwa);
}
+
+ else if (!strncmp(buf, "+BVRA:", 6) && IsConnected()) {
+ buf += 6;
+ while (buf[0] && IsWS(buf[0])) { buf++; }
+ indnum = strtol(buf, &end, 0);
+ if ((end == buf) || (indnum < 0) || (indnum > 1)) {
+ GetDi()->LogWarn("Parse error on BVRA");
+ } else {
+ SetBvra(indnum == 1);
+ }
+ }
+
+ else if ((!strncmp(buf, "+VGM:", 5) || !strncmp(buf, "+VGM=", 5)) &&
+ IsConnected()) {
+ buf += 5;
+ while (buf[0] && IsWS(buf[0])) { buf++; }
+ indnum = strtol(buf, &end, 0);
+ if ((end == buf) || (indnum < 0) || (indnum > 15)) {
+ GetDi()->LogWarn("Parse error on VGM");
+ } else if (m_state_vgm != indnum) {
+ m_state_vgm = indnum;
+ if (cb_NotifyVolume.Registered())
+ cb_NotifyVolume(this, true, false);
+ }
+ }
+
+ else if ((!strncmp(buf, "+VGS:", 5) || !strncmp(buf, "+VGS=", 5)) &&
+ IsConnected()) {
+ buf += 5;
+ while (buf[0] && IsWS(buf[0])) { buf++; }
+ indnum = strtol(buf, &end, 0);
+ if ((end == buf) || (indnum < 0) || (indnum > 15)) {
+ GetDi()->LogWarn("Parse error on VGS");
+ } else if (m_state_vgs != indnum) {
+ m_state_vgs = indnum;
+ if (cb_NotifyVolume.Registered())
+ cb_NotifyVolume(this, false, true);
+ }
+ }
+
+ else if (!strncmp(buf, "+BSIR:", 6) && IsConnected()) {
+ buf += 6;
+ while (buf[0] && IsWS(buf[0])) { buf++; }
+ indnum = strtol(buf, &end, 0);
+ if ((end == buf) || (indnum < 0) || (indnum > 1)) {
+ GetDi()->LogWarn("Parse error on BSIR");
+ } else if (m_state_bsir != indnum) {
+ m_state_bsir = (indnum == 1);
+ if (cb_NotifyInBandRingTone.Registered())
+ cb_NotifyInBandRingTone(this, indnum == 1);
+ }
+ }
}
@@ -2056,6 +2149,19 @@ ResponseDefault(char *buf)
*/
void HfpSession::
+CommandTimeout(TimerNotifier *timerp)
+{
+ ErrorInfo error;
+
+ assert(timerp == m_command_timer);
+ GetDi()->LogWarn(&error,
+ LIBHFP_ERROR_SUBSYS_BT,
+ LIBHFP_ERROR_BT_TIMEOUT,
+ "Audio Gateway Command Timeout");
+ __Disconnect(&error, false);
+}
+
+void HfpSession::
DeleteFirstCommand(bool do_start)
{
AtCommand *cmd;
@@ -2065,12 +2171,13 @@ DeleteFirstCommand(bool do_start)
cmd->m_links.Unlink();
delete cmd;
+ m_command_timer->Cancel();
if (do_start && !m_commands.Empty())
(void) StartCommand(0);
}
bool HfpSession::
-AppendCommand(AtCommand *cmdp, ErrorInfo *error)
+AddCommand(AtCommand *cmdp, bool top, ErrorInfo *error)
{
bool was_empty;
@@ -2091,7 +2198,14 @@ AppendCommand(AtCommand *cmdp, ErrorInfo *error)
}
was_empty = m_commands.Empty();
- m_commands.AppendItem(cmdp->m_links);
+
+ if (top && !was_empty) {
+ AtCommand *topcmd;
+ topcmd = GetContainer(m_commands.next, AtCommand, m_links);
+ topcmd->m_links.PrependItem(cmdp->m_links);
+ } else {
+ m_commands.AppendItem(cmdp->m_links);
+ }
if (!was_empty)
return true;
@@ -2126,7 +2240,7 @@ PendingCommand(AtCommand *cmdp, ErrorInfo *error)
return 0;
}
- if (!AppendCommand(cmdp, error)) {
+ if (!AddCommand(cmdp, false, error)) {
delete pendp;
return 0;
}
@@ -2135,70 +2249,37 @@ PendingCommand(AtCommand *cmdp, ErrorInfo *error)
}
bool HfpSession::
-StartCommand(ErrorInfo *error)
+SendCommand(const char *cmd, ErrorInfo *error)
{
- AtCommand *cmdp;
- int cl, rl;
- int err;
- ErrorInfo local_error;
+ size_t cl;
char stackbuf[64];
StringBuffer sb(stackbuf, sizeof(stackbuf));
- if (!IsRfcommConnected()) {
- if (error)
- error->Set(LIBHFP_ERROR_SUBSYS_BT,
- LIBHFP_ERROR_BT_NOT_CONNECTED,
- "Device is not connected");
- return false;
- }
-
- if (m_commands.Empty())
- return true;
-
- cmdp = GetContainer(m_commands.next, AtCommand, m_links);
-
- GetDi()->LogDebug("<< %s", cmdp->m_command_text);
-
- if (!sb.AppendFmt("%s\r", cmdp->m_command_text)) {
+ if (!sb.AppendFmt("%s\r", cmd)) {
if (error)
error->SetNoMem();
return false;
}
-
cl = strlen(sb.Contents());
- rl = send(m_rfcomm_sock, sb.Contents(), cl, MSG_NOSIGNAL);
- if (rl < 0) {
- err = errno;
- /* Problems!! */
- GetDi()->LogDebug(&local_error,
- LIBHFP_ERROR_SUBSYS_BT,
- LIBHFP_ERROR_BT_SYSCALL,
- "Write to RFCOMM socket: %s",
- strerror(err));
- if (error)
- *error = local_error;
+ GetDi()->LogDebug("<< %s", cmd);
+ return RfcommSend((const uint8_t *) sb.Contents(), cl, error);
+}
- __Disconnect(&local_error, ReadErrorVoluntary(err));
+bool HfpSession::
+StartCommand(ErrorInfo *error)
+{
+ AtCommand *cmdp;
- return false;
- }
+ if (m_commands.Empty())
+ return true;
- if (rl != cl) {
- /* Short write!? */
- GetDi()->LogWarn(&local_error,
- LIBHFP_ERROR_SUBSYS_BT,
- LIBHFP_ERROR_BT_SYSCALL,
- "Short write: expected:%d sent:%d, "
- "command \"%s\"",
- cl, rl, cmdp->m_command_text);
- if (error)
- *error = local_error;
+ cmdp = GetContainer(m_commands.next, AtCommand, m_links);
- __Disconnect(&local_error);
+ if (!SendCommand(cmdp->m_command_text, error))
return false;
- }
+ m_command_timer->Set(m_timeout_command);
return true;
}
@@ -2350,13 +2431,13 @@ public:
return false;
}
- void OK(void) {
+ bool OK(void) {
GetSession()->SetSupportedFeatures(m_brsf);
if (GetSession()->FeatureThreeWayCalling()) {
- (void) GetSession()->AppendCommand(
- new ChldTCommand(GetSession()), 0);
+ (void) GetSession()->AddCommand(
+ new ChldTCommand(GetSession()), false, 0);
}
- AtCommand::OK();
+ return AtCommand::OK();
}
void ERROR(void) {
/*
@@ -2369,6 +2450,21 @@ public:
}
};
+void HfpSession::
+SetSupportedFeatures(int ag_features)
+{
+ assert(IsConnecting());
+ m_brsf = ag_features;
+
+ /* Default: in-band ring tones enabled */
+ if (FeatureInBandRingTone())
+ m_state_bsir = true;
+
+ /* Default: echo cancelation/noise reduction enabled on AG */
+ if (FeatureEcnr())
+ m_state_ecnr = true;
+}
+
/* Cellular Indicator Test */
class CindTCommand : public AtCommand {
public:
@@ -2423,6 +2519,7 @@ public:
buf++;
}
+ GetSession()->VerifyIndicators();
}
return false;
}
@@ -2533,12 +2630,20 @@ SetIndicatorNum(int indnum, const char *buf, int len)
}
else if (StrMatch("call", buf, len)) {
m_inum_call = indnum;
+
+ /* Don't propagate to clients */
+ len = 0;
}
else if (StrMatch("callsetup", buf, len) ||
StrMatch("call_setup", buf, len)) {
- m_inum_callsetup = indnum;
- buf = "callsetup";
- len = strlen(buf);
+ if (m_inum_callsetup == 0)
+ m_inum_callsetup = indnum;
+
+ /* Don't propagate to clients */
+ len = 0;
+ }
+ else if (StrMatch("callheld", buf, len)) {
+ m_inum_callheld = indnum;
}
else if (StrMatch("signal", buf, len)) {
m_inum_signal = indnum;
@@ -2554,7 +2659,7 @@ SetIndicatorNum(int indnum, const char *buf, int len)
if (indnum >= m_inum_names_len) {
ExpandIndicators(indnum + 1);
}
- if (m_inum_names[indnum] == NULL) {
+ if (len && (m_inum_names[indnum] == NULL)) {
char *ndup = (char*) malloc(len + 1);
m_inum_names[indnum] = ndup;
/* Convert to lower case while copying */
@@ -2565,6 +2670,23 @@ SetIndicatorNum(int indnum, const char *buf, int len)
}
}
+bool HfpSession::
+VerifyIndicators(void)
+{
+ bool res = true;
+
+ if (m_inum_service < 0) {
+ GetDi()->LogWarn("AG did not report indicator \"service\"");
+ res = false;
+ }
+ if (m_inum_call < 0) {
+ GetDi()->LogWarn("AG did not report indicator \"call\"");
+ res = false;
+ }
+
+ return res;
+}
+
void HfpSession::
UpdateIndicator(int indnum, const char *buf)
{
@@ -2589,18 +2711,25 @@ UpdateIndicator(int indnum, const char *buf)
cb_NotifyCall(this, true, false, false);
/*
- * If the call state has changed, and we
- * are emulating callsetup, we presume that
- * the callsetup state has changed as well.
+ * If the call state has changed, and the
+ * callsetup state is presumed, we reset it.
*/
- if (IsConnected() && !FeatureIndCallSetup()) {
+ if (IsConnected() &&
+ (newstate == 1) &&
+ m_callsetup_presumed)
UpdateCallSetup(0);
- }
}
return;
}
if (indnum == m_inum_callsetup) {
- UpdateCallSetup(val);
+ /*
+ * Certain Motorola RAZR V3 devices will send a
+ * callsetup=1 indicator, but will fail to send
+ * callsetup=0 if the call is remotely terminated
+ * before it is answered or rejected.
+ */
+ if (val != 1)
+ UpdateCallSetup(val);
return;
}
@@ -2610,11 +2739,7 @@ UpdateIndicator(int indnum, const char *buf)
if (m_timer)
m_timer->Cancel();
m_state_call = false;
- m_state_callsetup = 0;
- if (m_state_incomplete_clip) {
- delete m_state_incomplete_clip;
- m_state_incomplete_clip = 0;
- }
+ UpdateCallSetup(0);
}
}
else if (indnum == m_inum_signal) {
@@ -2627,40 +2752,84 @@ UpdateIndicator(int indnum, const char *buf)
m_state_battchg = val;
}
- if ((indnum >= m_inum_names_len) ||
- (m_inum_names[indnum] == NULL)) {
+ if ((indnum < 0) || (indnum >= m_inum_names_len)) {
GetDi()->LogWarn("Undefined indicator %d", indnum);
return;
}
+
+ if (!m_inum_names[indnum])
+ /* Indicator not to be propagated to libhfp clients */
+ return;
+
+ /* Some AGs have both "callsetup" and "call_setup" indicators */
if (cb_NotifyIndicator.Registered())
cb_NotifyIndicator(this, m_inum_names[indnum], val);
}
-/*
- * UpdateCallSetup supports emulation of the callsetup indicator for
- * HFP 1.0 devices that don't support it.
- */
void HfpSession::
Timeout(TimerNotifier *notp)
{
- assert(notp == m_timer);
-
/*
- * Hack: deal with devices that don't provide call setup
- * information.
+ * Presumptive call setup timeout has elapsed,
+ * reset the callsetup value to 0.
*/
- if (IsConnected() && !FeatureIndCallSetup() &&
- (m_state_callsetup == 1)) {
- UpdateCallSetup(0);
+
+ assert(notp == m_timer);
+ assert(IsConnected());
+ assert(m_callsetup_presumed);
+ assert(m_state_callsetup != 0);
+ UpdateCallSetup(0);
+}
+
+void HfpSession::
+ClipTimeout(TimerNotifier *notp)
+{
+ assert(IsConnected());
+ assert(m_clip_state == CLIP_WAITING);
+ m_clip_state = CLIP_FINAL;
+ UpdateCallSetup(1, 1, 0, m_timeout_ring);
+}
+
+void HfpSession::
+ClearClip(void)
+{
+ if (m_clip_state == CLIP_WAITING)
+ m_clip_timer->Cancel();
+ if (m_clip_value) {
+ assert(m_clip_state == CLIP_FINAL);
+ delete m_clip_value;
+ m_clip_value = 0;
}
+ m_clip_state = CLIP_UNKNOWN;
}
+/*
+ * UpdateCallSetup is a complicated function that handles specific
+ * state machine jobs related to the call, callsetup, and clip states.
+ *
+ * We track the callsetup state presumptively. When an event occurs that
+ * suggests the callsetup should be in a particular state, we presume
+ * it to be in that state regardless of receiving an explicit indicator
+ * update from the AG. For example, if we get a RING event, we know that
+ * there is an incoming call, even if the AG doesn't tell is so. If we
+ * presume an incomplete call, we associate a timeout with the presumption.
+ * The timeout will be canceled if the AG tells us explicitly what the
+ * callsetup state is, or we presume no incomplete call. This way, we can
+ * transparently support AGs that do not report callsetup state. This is
+ * important because callsetup reporting is not required per HFP 1.5.
+ *
+ * The AG informs us of ringing before it gives us the caller ID
+ * information, but it's much cleaner to present the ring notification to
+ * our client at the same time as the caller ID info. We use the CLIP
+ * timer to try to make this happen.
+ */
void HfpSession::
-UpdateCallSetup(int val, int ring, GsmClipPhoneNumber *clip, int timeout_ms)
+UpdateCallSetup(int val, int ring, GsmClipResult *clip, int timeout_ms)
{
- bool upd_wc = false, upd_ac = false;
- GsmClipPhoneNumber *cpy = 0;
+ bool upd_wc = false, upd_ac = false, do_ring = false;
+ GsmClipResult *cpy = 0;
+ ErrorInfo local_error;
/*
GetDi()->LogDebug("UpdateCallSetup: v:%d r:%d clip:\"%s\", to:%d",
@@ -2669,60 +2838,140 @@ UpdateCallSetup(int val, int ring, GsmClipPhoneNumber *clip, int timeout_ms)
: "[NONE]", timeout_ms);
*/
- if (!FeatureIndCallSetup() && m_timer) {
- /* Reset the timer */
+ if (m_callsetup_presumed && m_timer)
m_timer->Cancel();
- if (val && timeout_ms) {
- m_timer->Set(timeout_ms);
- }
+ if (IsConnected() && timeout_ms &&
+ (m_callsetup_presumed || (m_state_callsetup != val))) {
+ m_callsetup_presumed = true;
+ m_timer->Set(timeout_ms);
+ } else {
+ m_callsetup_presumed = false;
}
- if (!val) {
+
+ switch (val) {
+ case 0:
+ /* No incomplete call */
assert(!clip);
- if (m_state_incomplete_clip) {
- delete m_state_incomplete_clip;
- m_state_incomplete_clip = 0;
+ assert(!ring);
+ assert(!timeout_ms);
+ ClearClip();
+ break;
+ case 1:
+ /* Incomplete incoming call */
+ if ((m_state_callsetup != 0) &&
+ (m_state_callsetup != 1))
+ ClearClip();
+
+ /* Check the CLIP state, update it */
+ switch (m_clip_state) {
+ case CLIP_UNKNOWN:
+ /* Was one provided? */
+ assert(!m_clip_value);
+ if (clip) {
+ cpy = clip->Duplicate();
+ if (cpy) {
+ m_clip_value = cpy;
+ m_clip_state = CLIP_FINAL;
+ }
+ } else if (ring) {
+ if (m_clip_enabled) {
+ /*
+ * We are now waiting for a CLIP,
+ * start the timer.
+ */
+ m_clip_timer->Set(m_timeout_clip);
+ m_clip_state = CLIP_WAITING;
+ } else {
+ m_clip_state = CLIP_FINAL;
+ }
+ }
+ break;
+ case CLIP_WAITING:
+ assert(!m_clip_value);
+ if (clip) {
+ cpy = clip->Duplicate();
+ if (cpy) {
+ m_clip_value = cpy;
+ m_clip_state = CLIP_FINAL;
+ m_clip_timer->Cancel();
+ }
+ do_ring = true;
+ }
+ break;
+ case CLIP_FINAL:
+ if (clip && (!m_clip_value ||
+ !m_clip_value->Compare(clip))) {
+ /*
+ * Did we miss an event here?
+ * A different caller ID value is being
+ * reported,
+ */
+ cpy = clip->Duplicate();
+ if (cpy) {
+ m_clip_value = cpy;
+ upd_wc = true;
+ }
+ }
+ break;
+ default:
+ abort();
}
- }
- else if (clip) {
- assert(val == 1);
- assert(ring != 0);
- if (m_state_incomplete_clip &&
- !m_state_incomplete_clip->Compare(clip)) {
- /*
- * Did we miss an event here?
- * A different caller ID value is being reported
- */
- cpy = clip->Duplicate();
- if (cpy)
- upd_wc = true;
+ if (ring) {
+ if ((ring == 1) && m_state_call) {
+ m_state_call = false;
+ upd_ac = true;
+ }
+ else if ((ring == 2) && !m_state_call) {
+ m_state_call = true;
+ upd_ac = true;
+ }
+
+ if (!do_ring)
+ do_ring = (m_clip_state == CLIP_FINAL);
+ }
+ break;
+ case 2:
+ case 3:
+ /* Incomplete outgoing call */
+ assert(!ring);
+ if ((m_state_callsetup != 0) &&
+ (m_state_callsetup != 2) &&
+ (m_state_callsetup != 3))
+ ClearClip();
+
+ assert(m_clip_state != CLIP_WAITING);
+ if (m_clip_state != CLIP_FINAL) {
+ assert(!m_clip_value);
+ m_clip_state = CLIP_FINAL;
}
- else if (!m_state_incomplete_clip) {
+ if (clip && (!m_clip_value ||
+ !m_clip_value->Compare(clip))) {
cpy = clip->Duplicate();
+ if (cpy) {
+ m_clip_value = cpy;
+ upd_wc = true;
+ }
}
+ break;
- if (cpy) {
- if (m_state_incomplete_clip)
- delete m_state_incomplete_clip;
- m_state_incomplete_clip = cpy;
- }
+ default:
+ GetDi()->LogWarn(&local_error,
+ LIBHFP_ERROR_SUBSYS_BT,
+ LIBHFP_ERROR_BT_PROTOCOL_VIOLATION,
+ "Unrecognized callsetup value from device");
+ __Disconnect(&local_error, true);
+ return;
}
+
if (val != m_state_callsetup) {
m_state_callsetup = val;
upd_wc = true;
}
- else if (ring) {
- if ((ring == 1) && m_state_call) {
- m_state_call = false;
- upd_ac = true;
- }
- else if ((ring == 2) && !m_state_call) {
- m_state_call = true;
- upd_ac = true;
- }
- }
- if ((upd_ac || upd_wc || ring) && cb_NotifyCall.Registered())
- cb_NotifyCall(this, upd_ac, upd_wc, (ring != 0));
+ if (IsConnected() &&
+ (upd_ac || upd_wc || do_ring) &&
+ cb_NotifyCall.Registered())
+ cb_NotifyCall(this, upd_ac, upd_wc, do_ring);
}
@@ -2732,8 +2981,13 @@ public:
CmerCommand(HfpSession *sessp)
: AtCommand(sessp, "AT+CMER=3,0,0,1") {}
void ERROR(void) {
- GetDi()->LogWarn("Could not enable AG event reporting");
- AtCommand::ERROR();
+ ErrorInfo error;
+ /* This will render the HFP interface almost unusable */
+ GetDi()->LogWarn(&error,
+ LIBHFP_ERROR_SUBSYS_BT,
+ LIBHFP_ERROR_BT_PROTOCOL_VIOLATION,
+ "Could not enable AG event reporting");
+ GetSession()->__Disconnect(&error, true);
}
};
@@ -2741,19 +2995,20 @@ public:
class ClipCommand : public AtCommand {
public:
ClipCommand(HfpSession *sessp) : AtCommand(sessp, "AT+CLIP=1") {}
- void OK(void) {
+ bool OK(void) {
GetSession()->m_clip_enabled = true;
- AtCommand::OK();
+ return AtCommand::OK();
}
+ /* It is not severely disruptive if this command fails */
};
/* Cellular Call Waiting */
class CcwaCommand : public AtCommand {
public:
CcwaCommand(HfpSession *sessp) : AtCommand(sessp, "AT+CCWA=1") {}
- void OK(void) {
+ bool OK(void) {
GetSession()->m_ccwa_enabled = true;
- AtCommand::OK();
+ return AtCommand::OK();
}
};
@@ -2768,12 +3023,12 @@ HfpHandshake(ErrorInfo *error)
m_rfcomm_not = GetDi()->NewSocket(m_rfcomm_sock, false);
m_rfcomm_not->Register(this, &HfpSession::HfpDataReady);
- if (!AppendCommand(new BrsfCommand(this, GetService()->m_brsf_my_caps),
- error) ||
- !AppendCommand(new CindTCommand(this), error) ||
- !AppendCommand(new CmerCommand(this), error) ||
- !AppendCommand(new ClipCommand(this), error) ||
- !AppendCommand(new CcwaCommand(this), error))
+ if (!AddCommand(new BrsfCommand(this, GetService()->m_brsf_my_caps),
+ false, error) ||
+ !AddCommand(new CindTCommand(this), false, error) ||
+ !AddCommand(new CmerCommand(this), false, error) ||
+ !AddCommand(new ClipCommand(this), false, error) ||
+ !AddCommand(new CcwaCommand(this), false, error))
return false;
return true;
@@ -2790,7 +3045,7 @@ HfpHandshakeDone(void)
m_conn_state = BTS_Connected;
/* Query current state */
- if (!AppendCommand(new CindRCommand(this), &error)) {
+ if (!AddCommand(new CindRCommand(this), false, &error)) {
__Disconnect(&error, false);
return;
}
@@ -2806,22 +3061,206 @@ HfpHandshakeDone(void)
* Commands
*/
+/* Cellular Get Subscriber Number */
+class CnumCommand : public AtCommand {
+public:
+ GsmCnumResult *m_res;
+
+ CnumCommand(HfpSession *sessp)
+ : AtCommand(sessp, "AT+CNUM"), m_res(0) {}
+ ~CnumCommand() {
+ if (m_res) {
+ delete m_res;
+ m_res = 0;
+ }
+ }
+
+ bool Response(const char *buf) {
+ ErrorInfo local_error;
+ if (!strncmp(buf, "+CNUM:", 6)) {
+ m_res = GsmCnumResult::Parse(buf + 6);
+ /* We could save the error code but we don't */
+ }
+ return false;
+ }
+
+ bool OK(void) {
+ ErrorInfo local_error;
+ if (!m_res) {
+ GetDi()->LogWarn(&local_error,
+ LIBHFP_ERROR_SUBSYS_BT,
+ LIBHFP_ERROR_BT_PROTOCOL_VIOLATION,
+ "Unacceptable response from device");
+ CompletePending(&local_error, 0);
+ } else {
+ CompletePending(0, m_res);
+ }
+ return true;
+ }
+};
+
+HfpPendingCommand *HfpSession::
+CmdQueryNumber(ErrorInfo *error)
+{
+ return PendingCommand(new CnumCommand(this), error);
+}
+
+/* Cellular Operator Status (two-stage) */
+class CopsCommand : public AtCommand {
+public:
+ bool m_sent_fmt;
+ GsmCopsResult *m_res;
+ CopsCommand(HfpSession *sessp)
+ : AtCommand(sessp, "AT+COPS=3,0"),
+ m_sent_fmt(false), m_res(0) {}
+ ~CopsCommand() {
+ if (m_res) {
+ delete m_res;
+ m_res = 0;
+ }
+ }
+ bool Response(const char *buf) {
+ ErrorInfo local_error;
+ if (!m_sent_fmt)
+ return false;
+ if (!strncmp(buf, "+COPS:", 6)) {
+ m_res = GsmCopsResult::Parse(buf + 6);
+ /* We could save the error code but we don't */
+ }
+ return false;
+ }
+ bool OK(void) {
+ ErrorInfo local_error;
+ if (!m_sent_fmt) {
+ /* Send the next stage of the command */
+ if (!GetSession()->SendCommand("AT+COPS?",
+ &local_error)) {
+ CompletePending(&local_error, 0);
+ } else {
+ m_sent_fmt = true;
+ }
+ return false;
+ }
+
+ if (!m_res) {
+ GetDi()->LogWarn(&local_error,
+ LIBHFP_ERROR_SUBSYS_BT,
+ LIBHFP_ERROR_BT_PROTOCOL_VIOLATION,
+ "Unacceptable response from device");
+ CompletePending(&local_error, 0);
+
+ } else {
+ CompletePending(0, m_res);
+ }
+
+ return true;
+ }
+};
+
+HfpPendingCommand *HfpSession::
+CmdQueryOperator(ErrorInfo *error)
+{
+ return PendingCommand(new CopsCommand(this), error);
+}
+
+/* Cellular List Current Calls */
+class ClccCommand : public AtCommand {
+public:
+ ListItem m_results;
+
+ ClccCommand(HfpSession *sessp) : AtCommand(sessp, "AT+CLCC") {}
+ ~ClccCommand() {
+ while (!m_results.Empty()) {
+ GsmClccResult *resp;
+ resp = GetContainer(m_results.next,
+ GsmClccResult,
+ m_links);
+ resp->m_links.Unlink();
+ delete resp;
+ }
+ }
+
+ bool Response(const char *buf) {
+ ErrorInfo local_error;
+ if (!strncmp(buf, "+CLCC:", 6)) {
+ GsmClccResult *resp;
+ resp = GsmClccResult::Parse(buf + 6);
+ if (resp)
+ m_results.AppendItem(resp->m_links);
+ }
+ return false;
+ }
+
+ bool OK(void) {
+ CompletePending(0, &m_results);
+ return true;
+ }
+};
+
+HfpPendingCommand *HfpSession::
+CmdQueryCurrentCalls(ErrorInfo *error)
+{
+ return PendingCommand(new ClccCommand(this), error);
+}
+
+/* Bluetooth Voice Recognition Activation */
+class BvraCommand : public AtCommand {
+public:
+ bool m_enable;
+ BvraCommand(HfpSession *sessp, bool enable)
+ : AtCommand(sessp, enable ? "AT+BVRA=1" : "AT+BVRA=0"),
+ m_enable(enable) {}
+ ~BvraCommand() {}
+
+ bool OK(void) {
+ GetSession()->SetBvra(m_enable);
+ return AtCommand::OK();
+ }
+};
+
HfpPendingCommand *HfpSession::
CmdSetVoiceRecog(bool enabled, ErrorInfo *error)
{
+ return PendingCommand(new BvraCommand(this, enabled), error);
+}
+
+HfpPendingCommand *HfpSession::
+CmdSetVolumeMic(uint8_t vol, ErrorInfo *error)
+{
char buf[32];
- sprintf(buf, "AT+BVRA=%d", enabled ? 1 : 0);
+ sprintf(buf, "AT+VGM=%d", vol);
return PendingCommand(new AtCommand(this, buf), error);
}
HfpPendingCommand *HfpSession::
-CmdSetEcnr(bool enabled, ErrorInfo *error)
+CmdSetVolumeSpeaker(uint8_t vol, ErrorInfo *error)
{
char buf[32];
- sprintf(buf, "AT+NREC=%d", enabled ? 1 : 0);
+ sprintf(buf, "AT+VGM=%d", vol);
return PendingCommand(new AtCommand(this, buf), error);
}
+/* Noise Reduction and Echo Cancellation */
+class NrecCommand : public AtCommand {
+public:
+ bool m_enable;
+ NrecCommand(HfpSession *sessp, bool enable)
+ : AtCommand(sessp, enable ? "AT+NREC=1" : "AT+NREC=0"),
+ m_enable(enable) {}
+ ~NrecCommand() {}
+
+ bool OK(void) {
+ GetSession()->m_state_ecnr = m_enable;
+ return AtCommand::OK();
+ }
+};
+
+HfpPendingCommand *HfpSession::
+CmdSetEcnr(bool enabled, ErrorInfo *error)
+{
+ return PendingCommand(new NrecCommand(this, enabled), error);
+}
+
class AtdCommand : public AtCommand {
public:
AtdCommand(HfpSession *sessp, const char *phnum) : AtCommand(sessp) {
@@ -2832,13 +3271,12 @@ public:
/* Redial mode - Bluetooth Last Dialed Number */
AtdCommand(HfpSession *sessp) : AtCommand(sessp, "AT+BLDN") {
}
- void OK(void) {
- if (!GetSession()->FeatureIndCallSetup() &&
- !GetSession()->HasConnectingCall()) {
+ bool OK(void) {
+ if (!GetSession()->HasConnectingCall()) {
GetSession()->UpdateCallSetup(2, 0, NULL,
GetSession()->m_timeout_dial);
}
- AtCommand::OK();
+ return AtCommand::OK();
}
};
@@ -2848,12 +3286,10 @@ public:
: AtCommand(sessp, cmd) {}
AtCommandClearCallSetup(HfpSession *sessp, const char *cmd)
: AtCommand(sessp, cmd) {}
- void OK(void) {
+ bool OK(void) {
/* Emulate call setup change */
- if (!GetSession()->FeatureIndCallSetup()) {
- GetSession()->UpdateCallSetup(0);
- }
- AtCommand::OK();
+ GetSession()->UpdateCallSetup(0);
+ return AtCommand::OK();
}
};
@@ -2891,7 +3327,7 @@ ValidPhoneNum(const char *ph, ErrorInfo *error)
int len = 0;
if (!ph)
return false;
- if (*ph == '+') {
+ if ((*ph == '+') || (*ph == '>')) {
ph++;
len++;
}
@@ -2959,12 +3395,12 @@ CmdCallSwapDropActive(ErrorInfo *error)
}
HfpPendingCommand *HfpSession::
-CmdCallDropActive(unsigned int actnum, ErrorInfo *error)
+CmdCallDropIndex(unsigned int actnum, ErrorInfo *error)
{
char buf[32];
if (!m_chld_1x) {
- GetDi()->LogWarn("Requested CmdCallDropActive(%d), "
+ GetDi()->LogWarn("Requested CmdCallActive(%d), "
"but AG does not claim support", actnum);
}
sprintf(buf, "AT+CHLD=1%d", actnum);
diff --git a/libhfp/rfcomm.cpp b/libhfp/rfcomm.cpp
index b2ef2ad..0a6b5c8 100644
--- a/libhfp/rfcomm.cpp
+++ b/libhfp/rfcomm.cpp
@@ -97,7 +97,10 @@ RfcommService::
RfcommService(uint16_t search_svclass_id)
: BtService(), m_rfcomm_listen(-1), m_rfcomm_listen_channel(0),
m_rfcomm_listen_not(0), m_secmode(RFCOMM_SEC_NONE),
- m_search_svclass_id(search_svclass_id), m_bt_master(true)
+ m_search_svclass_id(search_svclass_id), m_bt_master(true),
+ m_autoreconnect_timeout(15000), m_autoreconnect_set(false),
+ m_autoreconnect_timer(0),
+ m_autoreconnect_now_set(false), m_autoreconnect_now_timer(0)
{
}
@@ -107,6 +110,9 @@ RfcommService::
/* Make sure that we have been cleaned up after */
assert(m_rfcomm_listen < 0);
assert(m_rfcomm_listen_not == 0);
+
+ assert(!m_autoreconnect_timer);
+ assert(!m_autoreconnect_now_timer);
}
bool RfcommService::
@@ -134,6 +140,140 @@ SetSecMode(rfcomm_secmode_t secmode, ErrorInfo *error)
}
void RfcommService::
+AutoReconnectTimeout(TimerNotifier *timerp)
+{
+ ListItem retrylist;
+
+ if (timerp == m_autoreconnect_timer) {
+ assert(m_autoreconnect_set);
+ m_autoreconnect_set = false;
+ retrylist.AppendItemsFrom(m_autoreconnect_list);
+
+ } else {
+ assert(timerp == m_autoreconnect_now_timer);
+ assert(m_autoreconnect_now_set);
+ m_autoreconnect_now_set = false;
+ retrylist.AppendItemsFrom(m_autoreconnect_now_list);
+ }
+
+ while (!retrylist.Empty()) {
+ RfcommSession *sessp = GetContainer(retrylist.next,
+ RfcommSession,
+ m_autoreconnect_links);
+ sessp->m_autoreconnect_links.UnlinkOnly();
+
+ /* Always append to the delayed list */
+ m_autoreconnect_list.AppendItem(sessp->m_autoreconnect_links);
+
+ sessp->AutoReconnect();
+ }
+
+ if (!m_autoreconnect_list.Empty() && !m_autoreconnect_set) {
+ m_autoreconnect_set = true;
+ m_autoreconnect_timer->Set(m_autoreconnect_timeout);
+ }
+}
+
+void RfcommService::
+AddAutoReconnect(RfcommSession *sessp, bool now)
+{
+ assert(sessp->m_autoreconnect_links.Empty());
+ assert(!sessp->IsRfcommConnected() && !sessp->IsRfcommConnecting());
+
+ if (now) {
+ if (m_autoreconnect_now_timer && !m_autoreconnect_now_set) {
+ m_autoreconnect_now_set = true;
+ m_autoreconnect_now_timer->Set(0);
+ }
+ m_autoreconnect_now_list.AppendItem(sessp->
+ m_autoreconnect_links);
+ } else {
+ if (m_autoreconnect_timer && !m_autoreconnect_set) {
+ m_autoreconnect_set = true;
+ m_autoreconnect_timer->Set(m_autoreconnect_timeout);
+ }
+ m_autoreconnect_list.AppendItem(sessp->m_autoreconnect_links);
+ }
+}
+
+void RfcommService::
+RemoveAutoReconnect(RfcommSession *sessp)
+{
+ assert(!sessp->m_autoreconnect_links.Empty());
+ sessp->m_autoreconnect_links.Unlink();
+
+ if (m_autoreconnect_timer &&
+ m_autoreconnect_list.Empty() &&
+ m_autoreconnect_set) {
+ m_autoreconnect_set = false;
+ m_autoreconnect_timer->Cancel();
+ }
+
+ if (m_autoreconnect_now_timer &&
+ m_autoreconnect_now_list.Empty() &&
+ m_autoreconnect_now_set) {
+ m_autoreconnect_now_set = false;
+ m_autoreconnect_now_timer->Cancel();
+ }
+}
+
+bool RfcommService::
+Start(ErrorInfo *error)
+{
+ assert(!m_autoreconnect_timer);
+ assert(!m_autoreconnect_now_timer);
+
+ m_autoreconnect_timer = GetDi()->NewTimer();
+ if (!m_autoreconnect_timer) {
+ if (error)
+ error->SetNoMem();
+ return false;
+ }
+ m_autoreconnect_timer->Register(this,
+ &RfcommService::AutoReconnectTimeout);
+
+ m_autoreconnect_now_timer = GetDi()->NewTimer();
+ if (!m_autoreconnect_now_timer) {
+ if (error)
+ error->SetNoMem();
+ goto failed;
+ }
+ m_autoreconnect_now_timer->Register(this,
+ &RfcommService::AutoReconnectTimeout);
+
+ if (!m_autoreconnect_list.Empty() && !m_autoreconnect_set) {
+ m_autoreconnect_set = true;
+ m_autoreconnect_timer->Set(0);
+ }
+
+ if (!m_autoreconnect_now_list.Empty() && !m_autoreconnect_now_set) {
+ m_autoreconnect_now_set = true;
+ m_autoreconnect_now_timer->Set(0);
+ }
+
+ return true;
+
+failed:
+ Stop();
+ return false;
+}
+
+void RfcommService::
+Stop(void)
+{
+ if (m_autoreconnect_timer) {
+ m_autoreconnect_set = false;
+ delete m_autoreconnect_timer;
+ m_autoreconnect_timer = 0;
+ }
+ if (m_autoreconnect_now_timer) {
+ m_autoreconnect_now_set = false;
+ delete m_autoreconnect_now_timer;
+ m_autoreconnect_now_timer = 0;
+ }
+}
+
+void RfcommService::
RfcommListenNotify(SocketNotifier *notp, int fh)
{
BtDevice *devp;
@@ -415,8 +555,8 @@ RfcommSession(RfcommService *svcp, BtDevice *devp)
: BtSession(svcp, devp), m_rfcomm_state(RFC_Disconnected),
m_rfcomm_sdp_task(0), m_rfcomm_inbound(false),
m_rfcomm_dcvoluntary(false), m_rfcomm_sock(-1),
- m_rfcomm_not(0), m_rfcomm_secmode(RFCOMM_SEC_NONE)
-
+ m_rfcomm_not(0), m_rfcomm_secmode(RFCOMM_SEC_NONE),
+ m_conn_autoreconnect(false), m_operation_timeout(0)
{
}
@@ -429,6 +569,22 @@ RfcommSession::
assert(!m_rfcomm_not);
}
+void RfcommSession::
+SetAutoReconnect(bool enable)
+{
+ if (enable && !m_conn_autoreconnect) {
+ Get();
+ m_conn_autoreconnect = true;
+ if (m_rfcomm_state == RFC_Disconnected)
+ GetService()->AddAutoReconnect(this, true);
+ }
+ else if (!enable && m_conn_autoreconnect) {
+ m_conn_autoreconnect = false;
+ if (m_rfcomm_state == RFC_Disconnected)
+ GetService()->RemoveAutoReconnect(this);
+ Put();
+ }
+}
bool RfcommSession::
RfcommSdpLookupChannel(ErrorInfo *error)
@@ -461,6 +617,8 @@ RfcommSdpLookupChannel(ErrorInfo *error)
Get();
m_rfcomm_state = RFC_SdpLookupChannel;
m_rfcomm_sdp_task = taskp;
+ if (m_conn_autoreconnect)
+ GetService()->RemoveAutoReconnect(this);
return true;
}
@@ -473,6 +631,8 @@ RfcommSdpLookupChannelComplete(SdpTask *taskp)
assert(taskp == m_rfcomm_sdp_task);
assert(m_rfcomm_state == RFC_SdpLookupChannel);
+ (void) RfcommSetOperationTimeout(0, 0);
+
m_rfcomm_inbound = false;
if (taskp->m_params.m_errno) {
@@ -513,7 +673,7 @@ RfcommSdpLookupChannelComplete(SdpTask *taskp)
bool RfcommSession::
-RfcommConnect(uint8_t channel, ErrorInfo *error)
+RfcommConnect(uint8_t channel, ErrorInfo *error, int timeout)
{
struct sockaddr_rc raddr;
BtHci *hcip;
@@ -523,7 +683,8 @@ RfcommConnect(uint8_t channel, ErrorInfo *error)
hcip = GetHub()->GetHci();
if (!hcip) {
- error->SetNoMem();
+ if (error)
+ error->SetNoMem();
return false;
}
@@ -583,6 +744,10 @@ RfcommConnect(uint8_t channel, ErrorInfo *error)
goto failure;
}
+ /* Set a connection attempt timeout */
+ if (!RfcommSetOperationTimeout(timeout, error))
+ goto failure;
+
/* We wait for the socket to become _writable_ */
m_rfcomm_not = GetDi()->NewSocket(rsock, true);
if (!m_rfcomm_not) {
@@ -601,12 +766,16 @@ RfcommConnect(uint8_t channel, ErrorInfo *error)
m_rfcomm_inbound = false;
m_rfcomm_sock = rsock;
+ if (!m_autoreconnect_links.Empty())
+ GetService()->RemoveAutoReconnect(this);
+
if (!m_rfcomm_not)
RfcommConnectNotify(NULL, rsock);
return true;
failure:
+ (void) RfcommSetOperationTimeout(0, 0);
if (rsock >= 0) { close(rsock); }
assert(!error || error->IsSet());
return false;
@@ -631,6 +800,10 @@ RfcommAccept(int sock)
m_rfcomm_state = RFC_Connected;
Get();
+
+ if (m_conn_autoreconnect)
+ GetService()->RemoveAutoReconnect(this);
+
NotifyConnectionState(0);
return true;
}
@@ -644,6 +817,8 @@ RfcommConnectNotify(SocketNotifier *notp, int fh)
assert(m_rfcomm_state == RFC_Connecting);
+ (void) RfcommSetOperationTimeout(0, 0);
+
if (notp) {
assert(notp == m_rfcomm_not);
delete m_rfcomm_not;
@@ -681,12 +856,120 @@ RfcommConnectNotify(SocketNotifier *notp, int fh)
}
bool RfcommSession::
-RfcommConnect(ErrorInfo *error)
+RfcommConnect(ErrorInfo *error, int timeout)
{
if (m_rfcomm_state != RFC_Disconnected)
return false;
- return RfcommSdpLookupChannel(error);
+ if (!RfcommSetOperationTimeout(timeout, error))
+ return false;
+
+ if (!RfcommSdpLookupChannel(error)) {
+ (void) RfcommSetOperationTimeout(0, 0);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * It seems sketchy to trust the Bluetooth stack to time out its
+ * operations correctly all the time.
+ */
+bool RfcommSession::
+RfcommSetOperationTimeout(int ms, ErrorInfo *error)
+{
+ if (!ms) {
+ if (m_operation_timeout) {
+ delete m_operation_timeout;
+ m_operation_timeout = 0;
+ }
+ return true;
+ }
+
+ if (!m_operation_timeout) {
+ m_operation_timeout = GetDi()->NewTimer();
+ if (!m_operation_timeout) {
+ if (error)
+ error->SetNoMem();
+ return false;
+ }
+
+ m_operation_timeout->Register(this,
+ &RfcommSession::RfcommOperationTimeout);
+
+ } else {
+ m_operation_timeout->Cancel();
+ }
+
+ m_operation_timeout->Set(ms);
+ return true;
+}
+
+void RfcommSession::
+RfcommOperationTimeout(TimerNotifier *timerp)
+{
+ ErrorInfo error;
+
+ assert(IsRfcommConnecting() || IsRfcommConnected());
+ assert(timerp == m_operation_timeout);
+
+ error.Set(LIBHFP_ERROR_SUBSYS_BT,
+ LIBHFP_ERROR_BT_TIMEOUT,
+ "RFCOMM operation timed out");
+
+ __Disconnect(&error);
+}
+
+
+bool RfcommSession::
+RfcommSend(const uint8_t *buf, size_t len, ErrorInfo *error)
+{
+ ssize_t rl;
+ int err;
+ ErrorInfo local_error;
+
+ if (!IsRfcommConnected()) {
+ if (error)
+ error->Set(LIBHFP_ERROR_SUBSYS_BT,
+ LIBHFP_ERROR_BT_NOT_CONNECTED,
+ "Device is not connected");
+ return false;
+ }
+
+ rl = send(m_rfcomm_sock, buf, len, MSG_NOSIGNAL);
+
+ if (rl < 0) {
+ err = errno;
+ /* Problems!! */
+ GetDi()->LogDebug(&local_error,
+ LIBHFP_ERROR_SUBSYS_BT,
+ LIBHFP_ERROR_BT_SYSCALL,
+ "Write to RFCOMM socket: %s",
+ strerror(err));
+ if (error)
+ *error = local_error;
+
+ __Disconnect(&local_error, ReadErrorVoluntary(err));
+
+ return false;
+ }
+
+ if ((size_t) rl != len) {
+ /* Short write!? */
+ GetDi()->LogWarn(&local_error,
+ LIBHFP_ERROR_SUBSYS_BT,
+ LIBHFP_ERROR_BT_SYSCALL,
+ "Short write: expected:%d sent:%d",
+ len, rl);
+ if (error)
+ *error = local_error;
+
+ __Disconnect(&local_error);
+ return false;
+ }
+
+ return true;
}
void RfcommSession::
@@ -699,6 +982,7 @@ void RfcommSession::
__Disconnect(ErrorInfo *reason, bool voluntary)
{
if (m_rfcomm_state != RFC_Disconnected) {
+ (void) RfcommSetOperationTimeout(0, 0);
if (m_rfcomm_not) {
assert(m_rfcomm_state > RFC_SdpLookupChannel);
assert(m_rfcomm_sock >= 0);
@@ -719,6 +1003,9 @@ __Disconnect(ErrorInfo *reason, bool voluntary)
m_rfcomm_dcvoluntary = voluntary;
m_rfcomm_state = RFC_Disconnected;
+ if (m_conn_autoreconnect)
+ GetService()->AddAutoReconnect(this);
+
NotifyConnectionState(reason);
Put();