From: Prajwal Mohan Date: Fri, 27 Apr 2012 22:50:12 +0000 (-0700) Subject: Initial Import X-Git-Tag: 20120504.0~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=6d0c2ca025d4af6741ce950fb1e77ed99dd9fd70;p=profile%2Fivi%2Fhfdialer.git Initial Import --- 6d0c2ca025d4af6741ce950fb1e77ed99dd9fd70 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README b/README new file mode 100644 index 0000000..e69de29 diff --git a/dialer.desktop b/dialer.desktop new file mode 100644 index 0000000..18e5bf8 --- /dev/null +++ b/dialer.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=Application +Name=Handsfree Dialer +Icon=hfdialer +Exec=dialer +Comment=Handsfree Dialer Application +X-Desktop-File-Install-Version=0.16 diff --git a/dialer.service b/dialer.service new file mode 100644 index 0000000..b9ea896 --- /dev/null +++ b/dialer.service @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=com.hfdialer +Exec=/usr/bin/dialer -prestart diff --git a/dialerassets/bluetooth-smartphone.png b/dialerassets/bluetooth-smartphone.png new file mode 100644 index 0000000..f54ae68 Binary files /dev/null and b/dialerassets/bluetooth-smartphone.png differ diff --git a/dialerassets/computer.png b/dialerassets/computer.png new file mode 100644 index 0000000..83498ea Binary files /dev/null and b/dialerassets/computer.png differ diff --git a/dialerassets/dialerassets.pro b/dialerassets/dialerassets.pro new file mode 100644 index 0000000..382a2a5 --- /dev/null +++ b/dialerassets/dialerassets.pro @@ -0,0 +1,7 @@ +TEMPLATE = subdirs +CONFIG += ordered + +assets.files += *.png *.jpg +assets.path += $${installPrefix}/usr/share/hfdialer/images + +INSTALLS += assets diff --git a/dialerassets/ivi-v1_ivi-Dialer-incall.jpg b/dialerassets/ivi-v1_ivi-Dialer-incall.jpg new file mode 100644 index 0000000..9350e59 Binary files /dev/null and b/dialerassets/ivi-v1_ivi-Dialer-incall.jpg differ diff --git a/dialerassets/ivi-v1_ivi-background.jpg b/dialerassets/ivi-v1_ivi-background.jpg new file mode 100644 index 0000000..6f1d57d Binary files /dev/null and b/dialerassets/ivi-v1_ivi-background.jpg differ diff --git a/dialerassets/ivi_btn-call-active.png b/dialerassets/ivi_btn-call-active.png new file mode 100644 index 0000000..3d0a058 Binary files /dev/null and b/dialerassets/ivi_btn-call-active.png differ diff --git a/dialerassets/ivi_btn-call.png b/dialerassets/ivi_btn-call.png new file mode 100644 index 0000000..2fd535c Binary files /dev/null and b/dialerassets/ivi_btn-call.png differ diff --git a/dialerassets/ivi_btn-close.png b/dialerassets/ivi_btn-close.png new file mode 100644 index 0000000..94d623d Binary files /dev/null and b/dialerassets/ivi_btn-close.png differ diff --git a/dialerassets/ivi_btn-delete-active.png b/dialerassets/ivi_btn-delete-active.png new file mode 100644 index 0000000..282618a Binary files /dev/null and b/dialerassets/ivi_btn-delete-active.png differ diff --git a/dialerassets/ivi_btn-delete.png b/dialerassets/ivi_btn-delete.png new file mode 100644 index 0000000..97a00da Binary files /dev/null and b/dialerassets/ivi_btn-delete.png differ diff --git a/dialerassets/ivi_btn-endcall-active.png b/dialerassets/ivi_btn-endcall-active.png new file mode 100644 index 0000000..d7bf6e7 Binary files /dev/null and b/dialerassets/ivi_btn-endcall-active.png differ diff --git a/dialerassets/ivi_btn-endcall.png b/dialerassets/ivi_btn-endcall.png new file mode 100644 index 0000000..658774a Binary files /dev/null and b/dialerassets/ivi_btn-endcall.png differ diff --git a/dialerassets/ivi_btn-incomingcall-accept-active.png b/dialerassets/ivi_btn-incomingcall-accept-active.png new file mode 100644 index 0000000..94c451a Binary files /dev/null and b/dialerassets/ivi_btn-incomingcall-accept-active.png differ diff --git a/dialerassets/ivi_btn-incomingcall-accept.png b/dialerassets/ivi_btn-incomingcall-accept.png new file mode 100644 index 0000000..4e69ced Binary files /dev/null and b/dialerassets/ivi_btn-incomingcall-accept.png differ diff --git a/dialerassets/ivi_btn-incomingcall-decline-active.png b/dialerassets/ivi_btn-incomingcall-decline-active.png new file mode 100644 index 0000000..f733398 Binary files /dev/null and b/dialerassets/ivi_btn-incomingcall-decline-active.png differ diff --git a/dialerassets/ivi_btn-incomingcall-decline.png b/dialerassets/ivi_btn-incomingcall-decline.png new file mode 100644 index 0000000..8c683f5 Binary files /dev/null and b/dialerassets/ivi_btn-incomingcall-decline.png differ diff --git a/dialerassets/ivi_btn-list-active.png b/dialerassets/ivi_btn-list-active.png new file mode 100644 index 0000000..65597d1 Binary files /dev/null and b/dialerassets/ivi_btn-list-active.png differ diff --git a/dialerassets/ivi_btn-list-inactive.png b/dialerassets/ivi_btn-list-inactive.png new file mode 100644 index 0000000..ac8f33f Binary files /dev/null and b/dialerassets/ivi_btn-list-inactive.png differ diff --git a/dialerassets/ivi_btn-list.png b/dialerassets/ivi_btn-list.png new file mode 100644 index 0000000..1675dda Binary files /dev/null and b/dialerassets/ivi_btn-list.png differ diff --git a/dialerassets/ivi_btn-numbers-active.png b/dialerassets/ivi_btn-numbers-active.png new file mode 100644 index 0000000..d36f01f Binary files /dev/null and b/dialerassets/ivi_btn-numbers-active.png differ diff --git a/dialerassets/ivi_btn-numbers.png b/dialerassets/ivi_btn-numbers.png new file mode 100644 index 0000000..edc2205 Binary files /dev/null and b/dialerassets/ivi_btn-numbers.png differ diff --git a/dialerassets/ivi_buttonarea.png b/dialerassets/ivi_buttonarea.png new file mode 100644 index 0000000..6761817 Binary files /dev/null and b/dialerassets/ivi_buttonarea.png differ diff --git a/dialerassets/ivi_icon-call.png b/dialerassets/ivi_icon-call.png new file mode 100644 index 0000000..4579c3f Binary files /dev/null and b/dialerassets/ivi_icon-call.png differ diff --git a/dialerassets/ivi_icon-delete.png b/dialerassets/ivi_icon-delete.png new file mode 100644 index 0000000..7844410 Binary files /dev/null and b/dialerassets/ivi_icon-delete.png differ diff --git a/dialerassets/ivi_icon-endcall.png b/dialerassets/ivi_icon-endcall.png new file mode 100644 index 0000000..bb15214 Binary files /dev/null and b/dialerassets/ivi_icon-endcall.png differ diff --git a/dialerassets/ivi_icon-list-delete-active.png b/dialerassets/ivi_icon-list-delete-active.png new file mode 100644 index 0000000..7003a32 Binary files /dev/null and b/dialerassets/ivi_icon-list-delete-active.png differ diff --git a/dialerassets/ivi_icon-list-delete.png b/dialerassets/ivi_icon-list-delete.png new file mode 100644 index 0000000..1f5523a Binary files /dev/null and b/dialerassets/ivi_icon-list-delete.png differ diff --git a/dialerassets/ivi_icon-list-inactive-delete-active.png b/dialerassets/ivi_icon-list-inactive-delete-active.png new file mode 100644 index 0000000..05c443f Binary files /dev/null and b/dialerassets/ivi_icon-list-inactive-delete-active.png differ diff --git a/dialerassets/ivi_icon-list-inactive-delete.png b/dialerassets/ivi_icon-list-inactive-delete.png new file mode 100644 index 0000000..5421d7e Binary files /dev/null and b/dialerassets/ivi_icon-list-inactive-delete.png differ diff --git a/dialerassets/ivi_icon-time.png b/dialerassets/ivi_icon-time.png new file mode 100644 index 0000000..6d205bb Binary files /dev/null and b/dialerassets/ivi_icon-time.png differ diff --git a/dialerassets/ivi_textarea.png b/dialerassets/ivi_textarea.png new file mode 100644 index 0000000..63f4e5f Binary files /dev/null and b/dialerassets/ivi_textarea.png differ diff --git a/hfdialer.conf b/hfdialer.conf new file mode 100644 index 0000000..9be7fa2 --- /dev/null +++ b/hfdialer.conf @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/packaging/hfdialer.spec b/packaging/hfdialer.spec new file mode 100644 index 0000000..b7fa64f --- /dev/null +++ b/packaging/hfdialer.spec @@ -0,0 +1,61 @@ +%define buildwayland 1 +%if %{buildwayland} +%define backend wayland +%else +%define backend xlib +%endif + +Name: hfdialer-%{backend} +Summary: QML based Voice Call Application +Version: 0.3.2 +Release: 11.1 +Group: System/GUI/Other +License: Apache License, Version 2.0 +URL: http://www.tizen.org +Source0: hfdialer-%{version}.tar.bz2 +Requires: ofono +Requires: pulseaudio +Requires: bluetooth-qt-%{backend} +BuildRequires: pkgconfig(QtCore-%{backend}) +BuildRequires: pkgconfig(QtOpenGL-%{backend}) +BuildRequires: pkgconfig(QtDeclarative-%{backend}) +BuildRequires: pkgconfig(libpulse) +BuildRequires: pkgconfig(libpulse-mainloop-glib) +BuildRequires: pkgconfig(ofono-qt-%{backend}) +BuildRequires: pkgconfig(mlite-%{backend}) +BuildRequires: desktop-file-utils + +%description +QML based Dialer Application + + +%prep +%setup -q -n hfdialer-%{version} + +%build +unset LD_AS_NEEDED + +%qmake CONFIG+=%{backend} + +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} + +%qmake_install + +desktop-file-install --delete-original \ + --dir %{buildroot}%{_datadir}/applications \ + %{buildroot}%{_datadir}/applications/*.desktop + + +%files +%defattr(-,root,root,-) +/etc/dbus-1/system.d/hfdialer.conf +%{_bindir}/dialer +%{_datadir}/dbus-1/services/dialer.service +%{_datadir}/applications/dialer.desktop +%{_datadir}/hfdialer +%{_datadir}/hfdialer/qml +%{_datadir}/hfdialer/LICENSE + diff --git a/projects.pro b/projects.pro new file mode 100644 index 0000000..c1a35c8 --- /dev/null +++ b/projects.pro @@ -0,0 +1,38 @@ +VERSION = 0.3.2 +CONFIG += link_pkgconfig network opengl + +TEMPLATE = subdirs +CONFIG += ordered +SUBDIRS = src qml dialerassets sounds + +OTHER_FILES += *.service *.desktop *.sh + +# Desktop +desktop_entry.files = dialer.desktop +desktop_entry.path += $$INSTALL_ROOT/usr/share/applications +desktop_entry.CONFIG += no_check_exist + +# DBus service +dbus_service.files = dialer.service +dbus_service.path += $$INSTALL_ROOT/usr/share/dbus-1/services + +dbus_conf.files = hfdialer.conf +dbus_conf.path += $$INSTALL_ROOT/etc/dbus-1/system.d + +# Documentation +documentation.files = AUTHORS ChangeLog LICENSE README TODO +documentation.path = $$INSTALL_ROOT/usr/share/hfdialer/ + +INSTALLS += \ + desktop_entry \ + dbus_service \ + dbus_conf \ + documentation \ + +PROJECT_NAME = hfdialer + +dist.commands += rm -fR $${PROJECT_NAME}-$${VERSION} && +dist.commands += git clone . $${PROJECT_NAME}-$${VERSION} && +dist.commands += rm -fR $${PROJECT_NAME}-$${VERSION}/.git && +dist.commands += tar jcpvf $${PROJECT_NAME}-$${VERSION}.tar.bz2 $${PROJECT_NAME}-$${VERSION} +QMAKE_EXTRA_TARGETS += dist diff --git a/qml/AbstractDialog.qml b/qml/AbstractDialog.qml new file mode 100644 index 0000000..55937a3 --- /dev/null +++ b/qml/AbstractDialog.qml @@ -0,0 +1,45 @@ +/* + * dialer - QML User Interface Component + * + * Copyright (c) 2011, Tom Swindell. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +import Qt 4.7 + +Item { + id: root + + anchors.fill: parent + + states: [ + State { + name: 'shown' + PropertyChanges {target: root; opacity: 1.0} + }, + State { + name: 'hidden' + PropertyChanges {target: root; opacity: 0.0} + } + ] + + Behavior on opacity {PropertyAnimation {duration: 250}} + + Rectangle { + id: blind + anchors.fill: parent + color: 'black' + opacity: 0.7 + } + + MouseArea { + anchors.fill: parent + onPressed: { + root.state = 'hidden' + } + } +} diff --git a/qml/CallItemView.qml b/qml/CallItemView.qml new file mode 100644 index 0000000..3117016 --- /dev/null +++ b/qml/CallItemView.qml @@ -0,0 +1,101 @@ +/* + * Copyright 2012 Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import Qt 4.7 + +Item +{ + id: root + anchors.fill: parent + property variant call: null + + state: 'disconnected' + onStateChanged: { + console.log("*** STATE IS now >> " + root.state); + } + Connections { + target: adapter + onCallCountChanged: { + if (callCount <= 0) + root.state = 'disconnected' + } + } + onCallChanged: { + if(call) { + largeView.call = call; + console.log("*** QML *** :: CALL ITEM CHANGED, STATE: " + call.state); + root.state = call.state; + + if (call.stateChanged) + { + call.stateChanged.connect(function(state) { + console.log("*** QML *** :: CALL ITEM STATE CHANGED: " + state); + console.log(""); + + root.state = state; + }); + } + } + } + + states { + State { + name: 'active' + PropertyChanges {target: root; visible: true} + } + + State { + name: 'held' + PropertyChanges {target: root; visible: true} + } + + State { + name: 'dialing' + PropertyChanges {target: root; visible: true} + } + + State { + name: 'alerting' + PropertyChanges {target: root; visible: true} + } + + State { + name: 'incoming' + PropertyChanges {target: root; visible: true} + } + + State { + name: 'waiting' + PropertyChanges {target: root; visible: true} + } + + State { + name: 'disconnected' + PropertyChanges {target: root; visible: false} + } + } + + Rectangle + { + id: background + anchors {top: parent.top; left: parent.left; right: parent.right; bottom: parent.bottom; topMargin: parent.height / 4; bottomMargin: parent.height / 5} + + gradient: Gradient { + GradientStop {position: 0.0; color: '#4f4f4f'} + GradientStop {position: 1.0; color: '#000000'} + } + + CallItemViewLarge + { + id: largeView + call: root.call //parent.call + state: root.state //parent.state + } + } +} + diff --git a/qml/CallItemViewLarge.qml b/qml/CallItemViewLarge.qml new file mode 100644 index 0000000..1ea7dd6 --- /dev/null +++ b/qml/CallItemViewLarge.qml @@ -0,0 +1,239 @@ +/* + * Copyright 2012 Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import Qt 4.7 + +import 'javascripts/framework.js' as Support + +Item +{ + id: root + + anchors.fill: parent + + property variant call + property string callDuration: "00:00:00" + property string callerLabelText: "Unknown Caller" + state : 'disconnected' + + onCallChanged: { + console.log("*** call changed in large, before if") + if(call && call.msisdn) { + console.log("*** in calllarge if >> " + call.state ); + + root.state = call.state + + if (call.name) { + root.callerLabelText = call.name; + + } else { + + if (call.numberLen <= 10) + { + root.callerLabelText = call.msisdn[0] + call.msisdn[1] + call.msisdn[2] + '-' + + call.msisdn[3] + call.msisdn[4] + call.msisdn[5] + '-' + + call.msisdn[6] + call.msisdn[7] + call.msisdn[8] + call.msisdn[9]; + } + else + root.callerLabelText = call.msisdn; + } + } + } + + Timer { + interval: 1000; repeat: true; running: true; + + onTriggered: { + if(call) { + if(call.duration && call.duration > 0) + { + callDuration = Support.friendlyInterval(call.duration); + } + } + } + } + + states { + State { + name: 'active' + PropertyChanges {target: root; callDuration: "00:00:00"} + PropertyChanges {target: answerButton; visible: false} + PropertyChanges {target: hangupButton; visible: true; width: parent.width} + PropertyChanges {target: stateInd; text: qsTr("Active")} + } + + State { + name: 'held' + PropertyChanges {target: answerButton; visible: false} + PropertyChanges {target: hangupButton; visible: true; width: parent.width} + PropertyChanges {target: stateInd; text: qsTr("Held")} + } + + State { + name: 'dialing' + PropertyChanges {target: root; callDuration: "00:00:00"} + PropertyChanges {target: answerButton; visible: false} + PropertyChanges {target: hangupButton; visible: true; width: parent.width} + PropertyChanges {target: stateInd; text: qsTr("Dialing...")} + } + + State { + name: 'alerting' + PropertyChanges {target: answerButton; visible: false} + PropertyChanges {target: hangupButton; visible: true; width: parent.width} + PropertyChanges {target: stateInd; text: qsTr("Alerting...")} + } + + State { + name: 'incoming' + PropertyChanges {target: root; callDuration: "00:00:00"} + PropertyChanges {target: answerButton; visible: true} + PropertyChanges {target: hangupButton; visible: true; width: parent.width * 0.45} + PropertyChanges {target: stateInd; text: qsTr("Incoming...")} + } + + State { + name: 'waiting' + PropertyChanges {target: answerButton; visible: false} + PropertyChanges {target: hangupButton; visible: true; width: parent.width} + PropertyChanges {target: stateInd; text: qsTr("Waiting...")} + } + + State { + name: 'disconnected' + PropertyChanges {target: callDurationInd; text: "00:00:00"} + PropertyChanges {target: answerButton; visible: false} + PropertyChanges {target: hangupButton; visible: false} + PropertyChanges {target: stateInd; text: qsTr("Disconnected")} + } + } + + Text + { + id: stateInd + anchors {top: parent.top; topMargin: 20; horizontalCenter: parent.horizontalCenter} + color: '#ffffff' + font {pixelSize: 38} + text: qsTr("Disconnected") + } + + Text + { + id: callerInd + anchors {top: stateInd.bottom; topMargin: 20; horizontalCenter: parent.horizontalCenter} + color: '#ffffff' + font {pixelSize: 75} + text: callerLabelText + } + + Image + { + id: clock + source: "/usr/share/hfdialer/images/ivi_icon-time.png" + anchors { right: callDurationInd.left; rightMargin: 2; bottom: callerInd.top} + width: 25 + height: 25 + smooth: true + } + + Text + { + id: callDurationInd + anchors {bottom: callerInd.top; right: parent.right; topMargin: 10; rightMargin: parent.width * 0.2} + font {pixelSize: 22} + color: '#dfdfdf' + text: callDuration + } + + Item + { + id: buttons + anchors {top: callerInd.bottom; topMargin: 15; left: parent.left; leftMargin: parent.width * 0.2; right: parent.right; rightMargin: parent.width * 0.2} + + width: parent.width * 0.75 + height: 72 + + Image + { + id: answerButton + height: 72 + width: parent.width *0.45 + anchors {left: parent.left;} + source: "/usr/share/hfdialer/images/ivi_btn-incomingcall-accept.png" + + Image + { + width: 40; height: width + anchors.centerIn: parent + smooth: true + source: "/usr/share/hfdialer/images/ivi_icon-call.png" + } + + MouseArea + { + anchors.fill: parent + + onPressed: { + answerButton.source = "/usr/share/hfdialer/images/ivi_btn-incomingcall-accept-active.png" + } + + onReleased: { + answerButton.source = "/usr/share/hfdialer/images/ivi_btn-incomingcall-accept.png" + } + + onClicked: { + console.log("*** QML *** :: Answering call"); + adapter.currentCall.answer(); + } + } + } + + Image + { + id: hangupButton + height: 72 + width: parent.width * 0.45 + anchors {right: parent.right;} + source: "/usr/share/hfdialer/images/ivi_btn-incomingcall-decline.png" + + Image + { + width: 40; height: width + anchors.centerIn: parent + smooth: true + source: "/usr/share/hfdialer/images/ivi_icon-endcall.png" + } + + MouseArea + { + anchors.fill: parent + + onPressed: { + hangupButton.source = "/usr/share/hfdialer/images/ivi_btn-incomingcall-decline-active.png" + } + + onReleased: { + hangupButton.source = "/usr/share/hfdialer/images/ivi_btn-incomingcall-decline.png" + } + + onClicked: { + console.log("*** QML *** :: Hanging up call"); + root.parent.state = 'disconnected' + adapter.hangupAll(); + root.state = 'disconnected' + root.parent.parent.state = 'disconnected' + if (root.parent.parent.call) + root.parent.parent.call = null + if (root.call) + root.call = null + } + } + } + } +} + diff --git a/qml/DeviceDelegate.qml b/qml/DeviceDelegate.qml new file mode 100644 index 0000000..a4cc51c --- /dev/null +++ b/qml/DeviceDelegate.qml @@ -0,0 +1,66 @@ +/* + * Copyright 2012 Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import Qt 4.7 +Item { + id: root + property string deviceName + property string address + property string icon: "" + property string alias: "" + property string dbuspath: "" + property variant uuids: [] + + property int containerHeight: 80 + height: containerHeight + width: parent.width + signal clicked() + signal close() + Image { + id: availableBluetoothItem + source: "/usr/share/hfdialer/images/ivi_btn-list-inactive.png" + anchors {fill: parent; leftMargin: 8; rightMargin: 8; topMargin: 8} + + Image { + id: iconImg + source: icon == "phone" ? "/usr/share/hfdialer/images/bluetooth-smartphone.png" : + "/usr/share/hfdialer/images/computer.png" + height: availableBluetoothItem.containerHeight * 0.75 + width: height + anchors {left: parent.left; verticalCenter: parent.verticalCenter; leftMargin: icon == "phone"? 10 : 10;} + } + + Text { + id: mainText + + anchors {left: iconImg.right; right: parent.right; top: parent.top; bottom: parent.bottom; rightMargin: 10; leftMargin: icon == "phone"? 18 : 18;} + verticalAlignment: Text.AlignVCenter + height: availableBluetoothItem.containerHeight + font.pixelSize: parent.height / 2 + style: Text.Outline + styleColor: "#3B3A39" + color: "white" + text: root.deviceName + elide: Text.ElideRight + } + + } + + MouseArea { + id: mArea + anchors.fill: parent + + onPressed: { + availableBluetoothItem.source = "/usr/share/hfdialer/images/ivi_btn-list.png" + } + onReleased: { + availableBluetoothItem.source = "/usr/share/hfdialer/images/ivi_btn-list-inactive.png" + root.clicked() + } + } +} diff --git a/qml/DeviceDelegateActive.qml b/qml/DeviceDelegateActive.qml new file mode 100644 index 0000000..663b9ce --- /dev/null +++ b/qml/DeviceDelegateActive.qml @@ -0,0 +1,106 @@ +/* + * Copyright 2012 Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import Qt 4.7 +Item { + id: root + property string deviceName + property string address + property string icon: "" + property string alias: "" + property string dbuspath: "" + property variant uuids: [] + + property int containerHeight: 80 + height: containerHeight + width: parent.width + signal clicked() + signal close() + + Connections { + target: adapter + onModemOnlineChanged: { + + //If the modem gets powered down for any reason, attempt to power it again to maintain connection + if (!adapter.modemOnline) + { + mainText.color = "grey" + availableBluetoothItem.source = "/usr/share/hfdialer/images/ivi_btn-list-inactive.png" + } + else + { + mainText.color = "white" + availableBluetoothItem.source = "/usr/share/hfdialer/images/ivi_btn-list.png" + } + } + } + + Image { + id: availableBluetoothItem + + source: !adapter.modemOnline? "/usr/share/hfdialer/images/ivi_btn-list-inactive.png" : availableBluetoothItem.source = "/usr/share/hfdialer/images/ivi_btn-list.png" + anchors {fill: parent; leftMargin: 8; rightMargin: 8; topMargin: 8} + + MouseArea { + id: clickArea + anchors.fill: parent + + onPressed: { + availableBluetoothItem.source = "/usr/share/hfdialer/images/ivi_btn-list.png" + } + + onReleased: { + availableBluetoothItem.source = "/usr/share/hfdialer/images/ivi_btn-list-inactive.png" + } + + onClicked: { + adapter.modemOnline = true + } + } + + Text { + id: mainText + + anchors {left: parent.left; top: parent.top; bottom: parent.bottom; leftMargin: 15} + width: parent.width * 0.75 + + verticalAlignment: Text.AlignVCenter + height: availableBluetoothItem.containerHeight + font.pixelSize: parent.height / 2 + style: Text.Outline + styleColor: "#3B3A39" + color: !adapter.modemOnline? "grey" : "white" + text: root.deviceName + elide: Text.ElideRight + } + + Image { + id: closeButton + source: "/usr/share/hfdialer/images/ivi_icon-list-delete.png" + anchors { left: mainText.right; right: parent.right; top: parent.top; bottom: parent.bottom} + + MouseArea { + id: closeArea + anchors.fill: parent + + onPressed: { + closeButton.source = "/usr/share/hfdialer/images/ivi_icon-list-delete-active.png" + } + + onReleased: { + closeButton.source = "/usr/share/hfdialer/images/ivi_icon-list-delete.png" + } + + onClicked: { + console.log("CLOSE BUTTON CLICKED") + root.close() + } + } + } + } +} diff --git a/qml/DialNumPad.qml b/qml/DialNumPad.qml new file mode 100644 index 0000000..a4d8b68 --- /dev/null +++ b/qml/DialNumPad.qml @@ -0,0 +1,256 @@ +/* + * Copyright 2012 Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import Qt 4.7 + +Item +{ + id: root + + property bool numPadShown: true + property DialNumberEntry entry + property TextInput pidEdit + property bool pidRequest: false + + height: parent.height + property real buttonHeight: (parent.height / 5) - 41; + + function insertText(text) + { + if (!pidRequest) + entry.appendChar(text) + else if (text != "*" && text != "#") + { + pidEdit.text += text + } + } + + Image + { + id: numpad + width: parent.width; height: childrenRect.height + 21; + source: "/usr/share/hfdialer/images/ivi_buttonarea.png" + Behavior on opacity {PropertyAnimation {duration: 500}} + + Column + { + id: columnBox + anchors {top: parent.top; right: parent.right; left: parent.left; margins: 11} + spacing: 5 + + Row + { + width: parent.width + anchors {bottomMargin: 5} + spacing: 5 + + DialNumPadButton { + id: dial1 + text: qsTr("1"); + height: buttonHeight; + onClicked: root.insertText(text); + onPressAndHold: main.dialMailbox(); + } + DialNumPadButton { + text: qsTr("2"); + height: buttonHeight; + detail: qsTr("abc"); + onClicked: root.insertText(text); + onPressAndHold: main.dialSpeedDial(1); + } + DialNumPadButton { + text: qsTr("3"); + height: buttonHeight; + detail: qsTr("def"); + onClicked: root.insertText(text); + onPressAndHold: main.dialSpeedDial(2); + } + } + Row + { + width: parent.width + anchors.horizontalCenter: parent.horizontalCenter + spacing: 5 + DialNumPadButton { + text: qsTr("4"); + height: buttonHeight; + detail: qsTr("ghi"); + onClicked: root.insertText(text); + onPressAndHold: main.dialSpeedDial(3); + } + DialNumPadButton { + text: qsTr("5"); + height: buttonHeight; + detail: qsTr("jkl"); + onClicked: root.insertText(text); + onPressAndHold: main.dialSpeedDial(4); + } + DialNumPadButton { + text: qsTr("6"); + height: buttonHeight; + detail: qsTr("mno"); + onClicked: root.insertText(text); + onPressAndHold: main.dialSpeedDial(5); + } + } + Row + { + width: parent.width + anchors.horizontalCenter: parent.horizontalCenter + spacing: 5 + DialNumPadButton { + text: qsTr("7"); + height: buttonHeight; + detail: qsTr("pqrs"); + onClicked: root.insertText(text); + onPressAndHold: main.dialSpeedDial(6); + } + DialNumPadButton { + text: qsTr("8"); + height: buttonHeight; + detail: qsTr("tuv"); + onClicked: root.insertText(text); + onPressAndHold: main.dialSpeedDial(7); + } + DialNumPadButton { + text: qsTr("9"); + height: buttonHeight; + detail: qsTr("wxyz"); + onClicked: root.insertText(text); + onPressAndHold: main.dialSpeedDial(8); + } + } + Row + { + width: parent.width + anchors.horizontalCenter: parent.horizontalCenter + spacing: 5 + DialNumPadButton { + text: qsTr("*"); + height: buttonHeight; + onClicked: root.insertText(text); + onPressAndHold: root.insertText("p"); + } + DialNumPadButton { + text: qsTr("0"); + height: buttonHeight; + detail: qsTr("+"); + onClicked: root.insertText(text); + onPressAndHold: root.insertText("+"); + } + DialNumPadButton { + text: qsTr("#"); + height: buttonHeight; + onClicked: root.insertText(text); + onPressAndHold: root.insertText("w"); + } + } + + + + Row + { + id: actions + width: parent.width; height: dial1.height + + spacing: 5 + Image { + id: bDelete; + width: parent.width / 4; height: parent.height + source: "/usr/share/hfdialer/images/ivi_btn-delete.png" + Image { + anchors{ left: bDelete.left} + height: parent.height + width: parent.width + source: "/usr/share/hfdialer/images/ivi_icon-delete.png" + fillMode: Image.PreserveAspectFit + } + MouseArea + { + anchors.fill: parent + onClicked: + { + if (!pidRequest){ + + if(entry.textInput.text == entry.placeHolderText) + return; + + entry.textInput.text = entry.textInput.text.substring(0, entry.textInput.text.length -1); + } + else + pidEdit.text = pidEdit.text.substring(0, pidEdit.text.length -1); + } + + onPressAndHold: + { + if (!pidRequest) + entry.clear(); + else + pidEdit.text = ""; + } + } + } + + Image { + id: bCall; + + height: parent.height + width: parent.width - bDelete.width - closeButton.width - 5 + source: "/usr/share/hfdialer/images/ivi_btn-call.png" + + Image { + anchors { centerIn: parent} + height: parent.height + width: parent.width + source: "/usr/share/hfdialer/images/ivi_icon-call.png" + fillMode: Image.PreserveAspectFit + } + + MouseArea{ + anchors.fill: parent + onClicked: { + if (!pidRequest) + { + if(entry.isBlank()) + { + console.log("*** QML *** :: You can not dial without a number!"); + main.showErrorMessage(qsTr("You can't dial without a number!")); + return; + } + + if(main.dial(entry.textInput.text)) + { + entry.clear(); + } + } + } + } + } + + Image + { + id: closeButton + source: "/usr/share/hfdialer/images/ivi_btn-close.png" + height: parent.height + width: (parent.width / 7) - 5 + + MouseArea { + id: closeArea + anchors.fill: parent + + onClicked: { + console.log("CLOSE BUTTON CLICKED") + + Qt.quit() + } + } + } + } + } + } +} diff --git a/qml/DialNumPadButton.qml b/qml/DialNumPadButton.qml new file mode 100644 index 0000000..0593768 --- /dev/null +++ b/qml/DialNumPadButton.qml @@ -0,0 +1,76 @@ +/* + * Copyright 2012 Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import Qt 4.7 + +Item +{ + id: root + + property Item numpad + + property string text: '' + property string detail: '' + + property int marginSize: 10 + + signal clicked + signal pressAndHold + + width: (parent.width - marginSize) / 3; height: 72 + + SystemPalette {id: palette; colorGroup: SystemPalette.Active} + + Image + { + id: buttonImg + anchors {fill: parent;} + source: "/usr/share/hfdialer/images/ivi_btn-numbers.png" + + } + + Text + { + width: parent.width + height: parent.height * 0.6 + anchors {centerIn: parent} + text: parent.text + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + font {pixelSize: parent.height / 2} + color: "white" + style: Text.Outline; + styleColor: "#3B3A39" + } + + Text + { + anchors {top: parent.top; right: parent.right; topMargin: 21; rightMargin: 17} + text: parent.detail + font {pixelSize: 15} + color: "#D8D8D8" + } + + MouseArea + { + anchors.fill: parent + + onPressed: { + buttonImg.source = "/usr/share/hfdialer/images/ivi_btn-numbers-active.png" + } + + onReleased: { + buttonImg.source = "/usr/share/hfdialer/images/ivi_btn-numbers.png" + } + + onClicked: { + root.clicked(); + } + onPressAndHold: root.pressAndHold(); + } +} diff --git a/qml/DialNumberEntry.qml b/qml/DialNumberEntry.qml new file mode 100644 index 0000000..926f96e --- /dev/null +++ b/qml/DialNumberEntry.qml @@ -0,0 +1,77 @@ +/* + * dialer - QML User Interface Component + * + * Copyright (c) 2011, Tom Swindell. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +import Qt 4.7 + +Image +{ + property string placeHolderText: qsTr("Enter Number") + property TextInput textInput: input + + id: root + source: "/usr/share/hfdialer/images/ivi_textarea.png" + function clear() + { + input.color = "#3B3A39"; + input.text = placeHolderText; + } + + function isBlank() + { + return (input.text == placeHolderText); + } + + function appendChar(character) + { + if(input.text == placeHolderText) {input.text = character} else {input.text += character}; + } + + TextInput + { + id: input + anchors.centerIn: parent + color: "#3B3A39" + cursorVisible: false + activeFocusOnPress: false + inputMethodHints: Qt.ImhDialableCharactersOnly + font {pixelSize: 42} + text: placeHolderText + + Component.onCompleted: forceActiveFocus(); + + onTextChanged: { + if(text.length == 0) root.clear(); + + if(text.length > placeHolderText.length && text.substr(0, placeHolderText.length) == placeHolderText) + { + text = text.substr(placeHolderText.length); + } + + if(text.length < placeHolderText.length && placeHolderText.substr(0, text.length) == text) + { + text = placeHolderText; + } + + if(text == placeHolderText) + { + color = "#3B3A39"; + } + else + { + color = "#3B3A39"; + } + } + + onAccepted: { + main.dial(text) + } + } +} diff --git a/qml/DialPage.qml b/qml/DialPage.qml new file mode 100644 index 0000000..272dee6 --- /dev/null +++ b/qml/DialPage.qml @@ -0,0 +1,410 @@ +/* + * Copyright 2012 Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import Qt 4.7 + +import Tizen.Bluetooth 0.0 + +Item +{ + id: root + width: parent.width; height: parent.height + + property alias activeCall: activeCallView.call + property alias callState: activeCallView.state + + Keys.onEscapePressed: { + console.log("Escape Pressed"); + Qt.quit() + } + + onCallStateChanged: { + if (activeCallView.state != 'disconnected') + dialPage.state = 'activeCall' + else + dialPage.state = 'noCall' + } + + Timer { + id: reconnectTimer + interval: 3000 + repeat: false + running: false + + onTriggered: + { + adapter.modemOnline = true + } + } + + Connections { + target: adapter + onModemOnlineChanged: { + + //If the modem gets powered down for any reason, attempt to power it again to maintain connection + if (!adapter.modemOnline) + { + reconnectTimer.running = true; + } + } + } + + Image + { + id: dialPage + anchors.fill: parent + source: "/usr/share/hfdialer/images/ivi-v1_ivi-background.jpg" + state: 'noCall' + + DialNumberEntry + { + id: numberEntry + anchors { + top: parent.top; //bottom: numPad.top + left: parent.left; margins: 15 //right: parent.right + //margins: 10 + } + width: parent.width * 0.60 + height: parent.height / 4.5 + } + + DialNumPad + { + id: numPad + width: numberEntry.width + anchors {top: numberEntry.bottom; left: parent.left; bottom: parent.bottom; margins: 15} + entry: numberEntry + } + states{ + State { + name: 'activeCall' + PropertyChanges {target: numPad; visible: false} + PropertyChanges {target: numberEntry; visible: false} + PropertyChanges {target: modemList; visible: false} + PropertyChanges {target: vertDivider; visible: false} + } + State { + name: 'noCall' + PropertyChanges {target: numPad; visible: true} + PropertyChanges {target: numberEntry; visible: true} + PropertyChanges {target: modemList; visible: true} + PropertyChanges {target: vertDivider; visible: true} + } + + + } + + CallItemView + { + id: activeCallView + } + + Rectangle { + + id: vertDivider + anchors {left: numberEntry.right; margins: 8} + width: 4 + height: parent.height + color: "#262626" + + } + + Rectangle { + id: modemList + anchors {left: vertDivider.right; right: parent.right; top: parent.top; bottom: parent.bottom} + color: "#51504F" + + Text { + id: yourDevicesTxt + text: "Your Devices" + font.pixelSize: 42 + color: "white" + anchors {top: parent.top; right: parent.right; left: parent.left; bottom: horizDivider1.top; leftMargin: 15} + + } + + Rectangle { + id: horizDivider1 + anchors {left: parent.left; right: parent.right; topMargin: 8; bottomMargin: 8;} + y: 62 + height: 3 + color: "#B3BF3C" + } + + Text { + id: moreDevicesTxt + text: "More Devices" + font.pixelSize: 42 + color: "white" + anchors {right: parent.right; left: parent.left; bottom: horizDivider2.top; leftMargin: 15} + height: yourDevicesTxt.height + } + + Rectangle { + id: horizDivider2 + anchors {left: parent.left; right: parent.right; topMargin: 8; bottomMargin: 8;} + y: parent.height / 2 + height: 3 + color: "#B3BF3C" + } + + Component.onCompleted: { + nearbyDevicesModel.discover(true); + console.log("Devices qml has been created, checking for BT: " + btDevicesModel.adapterPresent) + + } + + Component.onDestruction: { + nearbyDevicesModel.discover(false); + } + + BluetoothDevicesModel { + id: btDevicesModel + property bool successFullPair: false + onDevicePaired: { + + } + } + + NearbyDevicesModel { + id: nearbyDevicesModel + + property bool discovering: false + + Component.onCompleted: { + + } + + onRequestConfirmation: { + console.log("onRequestConfirm called") + } + + onRequestPasskey: { + console.log("onRequestPasskey called") + + } + + onRequestPidCode: { + console.log("onRequestPidCode called") + } + + onAdapterPropertiesChanged: { + + if(name == "Discovering") { + discovering = value; + } + + } + + onNearbyDeviceFound: { + //console.log("new device: " + nearbyDevicesModel.alias(index)) + } + + } + + Flickable{ + id: activeFlickable + + anchors {top: horizDivider1.bottom; bottom: moreDevicesTxt.top; left: parent.left; right: parent.right} + clip: true + contentWidth: parent.width + contentHeight: parent.height + flickableDirection: Flickable.VerticalFlick + + Column { + id: deviceList + width: parent.width + spacing: 2 + Repeater { + model: btDevicesModel + + Component.onCompleted: { + + //Once model is completed, check if the modem is powered. If not, power it + if (!adapter.modemOnline) + { + adapter.modemOnline = true + } + } + + delegate: DeviceDelegateActive { + id: deligateItem + deviceName: model.name + address: model.address + dbuspath: model.path + uuids: model.profiles + property BluetoothDevice device: btDevicesModel.device(dbuspath) + + Connections { + target: btDevicesModel + onDevicePaired: { + console.log("new paired device address:" + device.address + "==" + model.address) + if(device.address === model.address){ + device.trusted = true + } + } + + onConnectedChanged: { + } + } + + onClose: { + console.log("unparing ..."); + device.unpair(); + btDevicesModel.deviceRemoved(device.path); + } + } + } + } + } + + Flickable{ + id: modelFlickable + anchors {top: horizDivider2.bottom; bottom: parent.bottom; left: parent.left; right: parent.right} + clip: true + contentWidth: parent.width + contentHeight: parent.height + flickableDirection: Flickable.VerticalFlick + + Column { + id: nearbyDevicesList + width: parent.width + height: parent.height / 2 + + Repeater { + id: modelRepeater + model: nearbyDevicesModel + + onCountChanged: { + modelFlickable.contentHeight = (count * 80) + } + + delegate: DeviceDelegate { + id: availableBluetoothItem + width: nearbyDevicesList.width + deviceName: name + icon: model.icon + alias: model.alias + anchors {margins: 8} + + onClicked: { + console.log("BUTTON CLICKED bubbled up") + nearbyDevicesModel.discover(false) + nearbyDevicesModel.pair(model.address) + } + + + Connections { + target: nearbyDevicesModel + onRequestConfirmation: { + console.log("spawning request confirm dialog, device = " + device + " deviceName = " + deviceName) + if(device != deviceName) return; + + dialogLoader.type = "confirmation" + dialogLoader.device = device + dialogLoader.code = code + dialogLoader.sourceComponent = requestConfirmDialogComponent + + } + + onRequestPasskey: { + console.log("spawning requestPasskeyDialog") + if(device != deviceName) return; + + dialogLoader.type = "passkey" + dialogLoader.device = device + dialogLoader.sourceComponent = requestPasskeyDialogComponent + + } + + onRequestPidCode: { + console.log("spawning requestPidCodeDialog") + if(device != deviceName) return; + + + dialogLoader.type = "pidcode" + dialogLoader.device = device + dialogLoader.legacyPairing = model.legacyPairing + dialogLoader.sourceComponent = requestPidCodeDialogComponent + + console.log(device + " model legacyPairing: " + model.legacyPairing) + } + } + } + } + } + } + + + Loader { + id: dialogLoader + anchors.fill: parent + property string type: "NULL" + property string device: "" + property string code: "" + property bool legacyPairing: false + + onLoaded: { + console.log("LOADER LOADED! type = " + type) + if (type === "confirmation" ) + { + item.deviceName = device + item.key = code + } + else if (type === "passkey" ) + { + item.deviceName = device + } + else if (type === "pidcode" ) + { + item.deviceName = device + item.legacyPairing = legacyPairing + } + } + } + + Component { + id: requestPasskeyDialogComponent + RequestpasskeyDialog { + id: requestPasskeyDialog + + onReplyRequestPasskey: { + dialogLoader.sourceComponent = undefined + nearbyDevicesModel.replyPasskey(reply) + } + } + } + + Component { + id: requestPidCodeDialogComponent + RequestPidCodeDialog { + id: requestPidCodeDialog + onReplyRequestPidCode: { + dialogLoader.sourceComponent = undefined + nearbyDevicesModel.replyRequestPidCode(reply) + } + onCancelRequest: { + dialogLoader.sourceComponent = undefined + } + } + } + + Component { + id: requestConfirmDialogComponent + RequestConfirmDialog { + id: requestConfirmDialog + onReplyRequestConfirmation: { + dialogLoader.sourceComponent = undefined + nearbyDevicesModel.replyRequestConfirmation(reply) + } + } + } + } + } +} + diff --git a/qml/MessageDialog.qml b/qml/MessageDialog.qml new file mode 100644 index 0000000..f707ec2 --- /dev/null +++ b/qml/MessageDialog.qml @@ -0,0 +1,37 @@ +/* + * dialer - QML User Interface Component + * + * Copyright (c) 2011, Tom Swindell. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +import Qt 4.7 + +AbstractDialog { + id: root + + property string mesg: '' + + Rectangle { + id: dialog + width: parent.width * 0.8; height: 300; + anchors.centerIn: parent + color: "white" + radius: 15 + smooth: true + + Text { + id: mesgText + width: parent.width * 0.9 + anchors.centerIn: parent + wrapMode: Text.WordWrap + color: "black" + font.pixelSize: 32 + text: "
" + root.mesg + "
" + } + } +} diff --git a/qml/RequestConfirmDialog.qml b/qml/RequestConfirmDialog.qml new file mode 100644 index 0000000..ef9e74a --- /dev/null +++ b/qml/RequestConfirmDialog.qml @@ -0,0 +1,90 @@ +/* + * Copyright 2012 Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import Qt 4.7 + +Rectangle { + id: root + anchors.fill: parent + color: "#51504F" + + property string deviceName: "" + property string key: "" + signal replyRequestConfirmation(bool reply) + +Column { + id: container + width: parent.width - 15 + anchors {centerIn: parent} + spacing: 10 + Component.onCompleted: { + console.log("request confirm dialog height: " + container.height + " width = " + container.width + " name = " + root.deviceName + " code = " + root.key) + } + + Text { + id: textlabel + width: parent.width + height: paintedHeight + horizontalAlignment: Text.AlignHCenter + text: qsTr("Pair with %1 with key %2?").arg(root.deviceName).arg(root.key) + wrapMode: Text.WordWrap + font.pixelSize: 24 + color: "White" + } + + Row { + id: buttonGroup + anchors.horizontalCenter: parent.horizontalCenter + spacing: 10 + height: 50 + + Image { + id: acceptButton + source: "/usr/share/hfdialer/images/ivi_btn-incomingcall-accept.png" + height: parent.height + width: root.width / 2 - 20 + + MouseArea { + anchors.fill: parent + onClicked: { + replyRequestConfirmation(true); + } + } + Text { + text: qsTr("Accept") + anchors.centerIn:parent + font.pointSize: 14 + color: "white" + } + } + + Image { + id: rejectButton + source: "/usr/share/hfdialer/images/ivi_btn-incomingcall-decline.png" + + height: parent.height + width: root.width / 2 - 20 + + MouseArea { + anchors.fill: parent + onClicked: { + replyRequestConfirmation(false); + } + } + + Text { + text: qsTr("Reject") + anchors.centerIn:parent + font.pointSize: 14 + color: "white" + } + } + } + +} +} diff --git a/qml/RequestPidCodeDialog.qml b/qml/RequestPidCodeDialog.qml new file mode 100644 index 0000000..8ff76e0 --- /dev/null +++ b/qml/RequestPidCodeDialog.qml @@ -0,0 +1,133 @@ +/* + * Copyright 2012 Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import Qt 4.7 + +Rectangle { + id: root + anchors.fill: parent + color: "#51504F" + + signal replyRequestPidCode(string reply) + signal cancelRequest() + property string deviceName + property string replyValue: legacyPairing ? "0000" : Math.floor(Math.random()*999999) + property bool legacyPairing: false + + Component.onCompleted: { + numPad.pidRequest = true + numPad.pidEdit = textInputField + } + + Column { + width: parent.width - 15 + anchors {centerIn: parent} + spacing: 10 + + Text { + id: textlabel + text: qsTr("Enter the following code on %1").arg(deviceName) + width: parent.width + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + font.pixelSize: 24 + color: "white" + } + + Rectangle { + id: textInput + anchors.horizontalCenter: parent.horizontalCenter + height: 40 + width: parent.width + color: "white" + radius: 5 + + TextInput { + id: textInputField + anchors.centerIn: parent + width: parent.width + height: parent.height * 0.75 + font.pixelSize: 24 + color: "black" + text: replyValue + horizontalAlignment: Text.AlignHCenter + activeFocusOnPress: false + } + } + + Row { + id: buttonGroup + anchors.horizontalCenter: parent.horizontalCenter + spacing: 10 + width: parent.width + height: 50 + + Image { + id: acceptButton + source: "/usr/share/hfdialer/images/ivi_btn-incomingcall-accept.png" + width: buttonGroup.width / 2 - 5 + height: parent.height + + MouseArea { + anchors.fill: parent + onClicked: { + console.log(deviceName + " replying with key: " + textInputField.text) + numPad.pidRequest = false + replyRequestPidCode(textInputField.text); + } + } + + Text { + id: acceptButtonText + text: qsTr("Accept") + anchors.centerIn:parent + horizontalAlignment: Text.AlignHCenter + font.pixelSize: 20 + color: "white" + } + } + + Image { + id: rejectButton + source: "/usr/share/hfdialer/images/ivi_btn-incomingcall-decline.png" + width: buttonGroup.width / 2 - 5 + height: parent.height + + MouseArea { + anchors.fill: parent + onClicked: { + console.log(deviceName + " replying with key: " + textInputField.text) + numPad.pidRequest = false + cancelRequest() + } + } + + Text { + id: cancelButtonText + text: qsTr("Cancel") + anchors.centerIn:parent + horizontalAlignment: Text.AlignHCenter + font.pixelSize: 20 + color: "white" + } + } + + + } + + } + ///we do this because this property is actually set post onCompleted: + onLegacyPairingChanged: { + console.log("legacy pair? " + legacyPairing) + if(!legacyPairing) { + replyRequestPidCode(textInputField.text); + console.log(deviceName + " replying with key: " + replyValue) + } + } + +} diff --git a/qml/RequestpasskeyDialog.qml b/qml/RequestpasskeyDialog.qml new file mode 100644 index 0000000..828abeb --- /dev/null +++ b/qml/RequestpasskeyDialog.qml @@ -0,0 +1,88 @@ +/* + * Copyright 2012 Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import Qt 4.7 + +Rectangle { + id: root + anchors.fill: parent + color: "#51504F" + + + signal replyRequestPasskey(int reply) + property string deviceName + + Component.onCompleted: { + numPad.pidRequest = true + numPad.pidEdit = textInputField + } + +Column { + width: parent.width - 15 + spacing: 10 + + Text { + id: textlabel + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("Enter passcode to use:") + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + width: parent.width + font.pixelSize: 24 + color: "white" + } + + Rectangle { + id: textInput + anchors.horizontalCenter: parent.horizontalCenter + height: 40 + width: parent.width + color: "white" + radius: 5 + + TextInput { + id: textInputField + anchors.centerIn: parent + width: parent.width + height: parent.height * 0.75 + font.pixelSize: 24 + color: "black" + text: replyValue + horizontalAlignment: Text.AlignHCenter + activeFocusOnPress: false + } + } + + Image { + id: acceptButton + source: "/usr/share/hfdialer/images/ivi_btn-incomingcall-accept.png" + anchors.horizontalCenter: parent.horizontalCenter + width: textInput.width + height: 50 + + MouseArea { + id: mouseArea + anchors.fill: parent + + onClicked: { + numPad.pidRequest = false + nearbyDevicesModel.replyRequestPasskey(textInputField.text); + } + } + } + + Text { + id: text + text: qsTr("Accept") + anchors.centerIn:parent + horizontalAlignment: Text.AlignHCenter + font.pixelSize: 20 + color: "white" + } +} +} diff --git a/qml/javascripts/framework.js b/qml/javascripts/framework.js new file mode 100644 index 0000000..f8f734e --- /dev/null +++ b/qml/javascripts/framework.js @@ -0,0 +1,40 @@ +/* + * Generic Javascript Utility Functions + * + * Copyright (c) 2011, Tom Swindell. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +Date.prototype.getFormat = function() +{ + return (this.getDate() < 10 ? '0' : '') + this.getDate() + '/' + + (this.getMonth() < 10 ? '0' : '') + this.getMonth() + '/' + + this.getFullYear() + + ' | ' + + (this.getHours() < 10 ? '0' : '') + this.getHours() + ':' + + (this.getMinutes() < 10 ? '0' : '') + this.getMinutes() + ':' + + (this.getSeconds() < 10 ? '0' : '') + this.getSeconds(); +} + +function friendlyInterval(duration) +{ + duration = Number(duration); + if(isNaN(duration)) duration = 0; + + var hours = Math.floor(duration / 3600); + var minutes = Math.floor((duration % 3600) / 60); + var seconds = duration % 60; + + return (hours < 10 ? '0' : '') + hours + ':' + (minutes < 10 ? '0' : '') + minutes + ':' + (seconds < 10 ? '0' : '') + seconds; +} + +function friendlyDuration(start, end) +{ + var duration = Math.floor(((new Date(end)) - (new Date(start))) / 1000); + return friendlyInterval(duration); +} + diff --git a/qml/main.qml b/qml/main.qml new file mode 100644 index 0000000..694dd09 --- /dev/null +++ b/qml/main.qml @@ -0,0 +1,109 @@ +/* + * Copyright 2012 Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +import Qt 4.7 +import com.hfdialer 1.0 + +import 'javascripts/framework.js' as Support + +Item +{ + id: main + + width: 1024; height: 600 + + Dialer { id: adapter } + + Component.onCompleted: { + console.log("######## Completed loading component, initializing..."); + + adapter.incomingCall.connect(function() + { + var call = adapter.currentCall; + + console.log("*** QML *** :: INCOMING CALL:" + call); + console.log("*** QML *** :: MSISDN: " + call.msisdn); + console.log("*** QML *** :: START: " + call.startedAt); + console.log(""); + + dialpage.activeCall = call + + }); + + if(adapter.currentCall) + { + dialpage.activeCall = call + } + + } + + function showErrorMessage(mesg) { + mesgDialog.mesg = mesg; + mesgDialog.state = 'shown'; + } + + function dial(msisdn) { + if(msisdn.trim().length == 0) + { + console.log("*** QML *** :: You can't dial without a number!"); + showErrorMessage(qsTr("No number specified!")); + return false; + } + + if (!adapter.modemOnline) + { + console.log("*** QML *** :: modem is not available or powered down"); + showErrorMessage(qsTr("modem is not available or powered down!")); + return false; + } + + console.log("*** QML *** :: Attempting to dial MSISDN: " + msisdn); + + dialpage.activeCall = { + state: 'dialing', + msisdn: msisdn + }; + + adapter.dial(msisdn); + + return true; + } + + function dialMailbox() { + if(adapter.mailbox) { + console.log("*** QML *** :: Attempting to call mailbox number: " + adapter.mailbox); + main.dial(adapter.mailbox); + } else { + console.log("*** QML *** :: No mailbox number defined!"); + showErrorMessage(qsTr("No mailbox number defined.")); + } + } + + function dialSpeedDial(index) { + if(adapter.speedDial(index)) + { + console.log("*** QML *** :: Calling speed dial " + index + ": " + adapter.speedDial(index)); + main.dial(adapter.speedDial(index)); + } else { + console.log("*** QML *** :: No speed dial number defined for: " + index); + showErrorMessage(qsTr("No speed dial for " + (index + 1))); + } + } + + DialPage + { + id: dialpage + anchors.fill: parent + } + + MessageDialog { + id: mesgDialog + state: 'hidden' + } +} + diff --git a/qml/qml.pro b/qml/qml.pro new file mode 100644 index 0000000..6fa77d2 --- /dev/null +++ b/qml/qml.pro @@ -0,0 +1,9 @@ +TEMPLATE = subdirs +CONFIG += ordered + +qml.files = *.qml javascripts +qml.path = $${installPrefix}/usr/share/hfdialer/qml + +OTHER_FILES += *.qml + +INSTALLS += qml diff --git a/sounds/ring-1.wav b/sounds/ring-1.wav new file mode 100644 index 0000000..5730ca4 Binary files /dev/null and b/sounds/ring-1.wav differ diff --git a/sounds/sounds.pro b/sounds/sounds.pro new file mode 100644 index 0000000..de18634 --- /dev/null +++ b/sounds/sounds.pro @@ -0,0 +1,7 @@ +TEMPLATE = subdirs +CONFIG += ordered + +sounds.files += *.wav +sounds.path += $${installPrefix}/usr/share/hfdialer/sounds + +INSTALLS += sounds diff --git a/src/callitem.cpp b/src/callitem.cpp new file mode 100644 index 0000000..aff978c --- /dev/null +++ b/src/callitem.cpp @@ -0,0 +1,229 @@ +/* + * hfdialer - Hands Free Voice Call Manager + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#include "common.h" +#include "callitem.h" +#include "dialerapplication.h" +#include "pacontrol.h" +#include +#include +#include + +#define DEFAULT_RINGTONE "ring-1.wav" + +CallItem::CallItem(const QString path, QObject *parent) + : QObject(parent), + m_path(path), + m_rtKey(new MGConfItem("/apps/dialer/defaultRingtone")), + m_isconnected(FALSE) +{ + TRACE; + QString l_ringtoneFile = QString("/usr/share/hfdialer/sounds/%1").arg(DEFAULT_RINGTONE); + QString l_rtKeyValue = m_rtKey->value(QVariant(l_ringtoneFile)).toString(); + + if (!QFileInfo(l_rtKeyValue).exists()) { + qWarning() << QString("CallItem: %1 ringtone not found: %2") + .arg(m_path) + .arg(l_rtKeyValue); + } + + if (isValid()) { + init(); + } +} + +CallItem::~CallItem() +{ + TRACE; + PAControl::instance()->unrouteAudio(); + + if (m_rtKey) { + delete m_rtKey; + } + m_rtKey = 0; + + // delete the callproxy object + if (callProxy()) + delete callProxy(); +} + +void CallItem::init() +{ + TRACE; + if (!m_path.isEmpty()) { + m_call = new CallProxy(m_path); + if (m_call->isValid()) { + // dynamic_cast(model())->setCall(call); + connect(m_call,SIGNAL(stateChanged()),this,SLOT(callStateChanged())); + connect(m_call,SIGNAL(dataChanged()),this,SLOT(callDataChanged())); + connect(m_call,SIGNAL(multipartyChanged()),this,SLOT(callMultipartyChanged())); + } else { + qCritical("Invalid CallProxy instance!"); + } + } else { + qCritical("Empty call path. Can not create CallProxy!"); + } +} + +bool CallItem::isValid() +{ + TRACE; + return (!path().isEmpty()); +} + +bool CallItem::isValid() const +{ + TRACE; + return (!path().isEmpty()); +} + +QString CallItem::path() const +{ + TRACE; + return m_path; +} + +bool CallItem::setPath(QString path) +{ + TRACE; + if (!m_path.isEmpty()) { + qCritical("Path already set and can not be changed once it is set"); + return false; + } else if (path.isEmpty()) { + qCritical("It makes no sense to set Path to an empty string!?!?"); + return false; + } + + m_path = path; + + init(); + + return true; +} + +void CallItem::setDirection(CallDirection direction) +{ + TRACE; +} + +QString CallItem::lineID() const +{ + TRACE; + return m_call->lineID(); +} + +QString CallItem::name() const +{ + TRACE; + return m_call->name(); +} + +CallState CallItem::state() const +{ + TRACE; + CallState cs = STATE_NONE; + QString state = m_call->state(); + + if (state == "active") + cs = STATE_ACTIVE; + + else if (state == "held") + cs = STATE_HELD; + else if (state == "dialing") + cs = STATE_DIALING; + else if (state == "alerting") + cs = STATE_ALERTING; + else if (state == "incoming") + cs = STATE_INCOMING; + else if (state == "waiting") + cs = STATE_WAITING; + else if (state == "disconnected") + cs = STATE_DISCONNECTED; + + return cs; +} + +CallDirection CallItem::direction() const +{ + TRACE; + return DIRECTION_NONE; +} + +CallDisconnectReason CallItem::reason() const +{ + TRACE; + return DISCONNECT_NONE; +} + +int CallItem::duration() const +{ + TRACE; + return m_call->duration(); +} + +QDateTime CallItem::startTime() const +{ + TRACE; + return m_call->startTime(); +} + +CallProxy* CallItem::callProxy() const +{ + TRACE; + return m_call; +} + +void CallItem::click() +{ + TRACE; + + emit clicked(); +} + +void CallItem::silenceRingtone() +{ + TRACE; +} + +void CallItem::callStateChanged() +{ + TRACE; + emit stateChanged(); +} + +void CallItem::callDataChanged() +{ + TRACE; +} + +void CallItem::callDisconnected(const QString &reason) +{ + TRACE; + Q_UNUSED(reason); +} + + +bool CallItem::multiparty() +{ + TRACE; + return false; +} + +void CallItem::callMultipartyChanged() +{ + TRACE; + emit multipartyChanged(); +} + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/callitem.h b/src/callitem.h new file mode 100644 index 0000000..5b74c59 --- /dev/null +++ b/src/callitem.h @@ -0,0 +1,117 @@ +/* + * hfdialer - Hands Free Voice Call Manager + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#ifndef CALLITEM_H +#define CALLITEM_H + +#include +#include +#include +#include +#include "callproxy.h" + +enum CallState { + STATE_NONE = 0, + STATE_ACTIVE, + STATE_HELD, + STATE_DIALING, + STATE_ALERTING, + STATE_INCOMING, + STATE_WAITING, + STATE_DISCONNECTED, + STATE_LAST, +}; + +enum CallDirection { + DIRECTION_NONE = 0, + DIRECTION_IN, + DIRECTION_OUT, + DIRECTION_MISSED, + DIRECTION_LAST, +}; + +enum CallDisconnectReason { + DISCONNECT_NONE = 0, + DISCONNECT_LOCAL, + DISCONNECT_REMOTE, + DISCONNECT_NETWORK, + DISCONNECT_LAST, +}; + +class CallItem: public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString path READ path WRITE setPath) + Q_PROPERTY(QString lineID READ lineID) + Q_PROPERTY(QString name READ name) + Q_PROPERTY(CallState state READ state) + Q_PROPERTY(CallDirection direction READ direction WRITE setDirection) + Q_PROPERTY(CallDisconnectReason reason READ reason) + Q_PROPERTY(int duration READ duration) + Q_PROPERTY(QDateTime startTime READ startTime) + Q_PROPERTY(bool multiparty READ multiparty) + Q_PROPERTY(CallProxy* callProxy READ callProxy) + + public: + CallItem(const QString path = QString(), QObject *parent = 0); + virtual ~CallItem(); + + QString path() const; + QString lineID() const; + QString name() const; + CallState state() const; + CallDirection direction() const; + CallDisconnectReason reason() const; + int duration() const; + QDateTime startTime() const; + CallProxy *callProxy() const; + bool isValid(); + bool isValid() const; + bool multiparty(); + +public Q_SLOTS: + void init(); + bool setPath(QString path); // Setting this will create the CallProxy + void setDirection(CallDirection direction); + void click(); + + void silenceRingtone(); + +Q_SIGNALS: + // TODO: handle tap-and-hold + void clicked(); + void stateChanged(); + void dataChanged(); + void multipartyChanged(); + +private Q_SLOTS: + void callStateChanged(); + void callDataChanged(); + void callDisconnected(const QString &reason); + void callMultipartyChanged(); + +private: + QString m_path; + MGConfItem *m_rtKey; + bool m_isconnected; + QString m_ringtonefile; + CallProxy *m_call; + + Q_DISABLE_COPY(CallItem) +}; + +#endif // CALLITEM_H + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/callmanager.cpp b/src/callmanager.cpp new file mode 100644 index 0000000..01608e7 --- /dev/null +++ b/src/callmanager.cpp @@ -0,0 +1,481 @@ +/* + * hfdialer - Hands Free Voice Call Manager + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#include "common.h" +#include "callmanager.h" + +class CallManagerPrivate +{ +public: + //ResourceProxy *resource; + QHash callItems; +}; + +CallManager::CallManager(const QString &modemPath, QObject *parent) + : OfonoVoiceCallManager(OfonoModem::AutomaticSelect, modemPath, parent), + d(new CallManagerPrivate) +{ + TRACE; + + // Transform existing calls list, into list of CallItems + updateCallItems(); + + // Begin tracking calls + connect(this, SIGNAL(callAdded(const QString)), + this, SLOT(addCall(const QString))); + connect(this, SIGNAL(callRemoved(const QString)), + this, SLOT(removeCall(const QString))); + + // Hook into parent class signals + connect(this, SIGNAL(dialComplete(const bool)), + this, SLOT(dialFinished(const bool))); + connect(this, SIGNAL(swapCallsComplete(const bool)), + this, SLOT(swapFinished(const bool))); + connect(this, SIGNAL(hangupAllComplete(const bool)), + this, SLOT(hangupAllFinished(const bool))); + connect(this, SIGNAL(sendTonesComplete(const bool)), + this, SLOT(sendTonesFinished(const bool))); + connect(this, SIGNAL(holdAndAnswerComplete(const bool)), + this, SLOT(holdAndAnswerFinished(const bool))); + connect(this, SIGNAL(transferComplete(const bool)), + this, SLOT(transferFinished(const bool))); + connect(this, SIGNAL(releaseAndAnswerComplete(const bool)), + this, SLOT(releaseAndAnswerFinished(const bool))); + connect(this, SIGNAL(privateChatComplete(const bool)), + this, SLOT(privateChatFinished(const bool))); + connect(this, SIGNAL(createMultipartyComplete(const bool)), + this, SLOT(createMultipartyFinished(const bool))); + connect(this, SIGNAL(hangupMultipartyComplete(const bool)), + this, SLOT(hangupMultipartyFinished(const bool))); + + connect(this,SIGNAL(callsChanged()),this,SLOT(callChangedSlot())); + + connect(this, SIGNAL(validityChanged(bool)), this, SLOT(modemValidityChanged(bool)) ); + + if (isValid()) + emit connected(); +} + +CallManager::~CallManager() +{ + TRACE; + // FIXME: Do something here!!! + qDebug() << QString("Destroying VoiceCallManager"); + qDebug() << QString("Purging all CallItems"); + foreach (CallItem *item, d->callItems) { + disconnect(item, SIGNAL(stateChanged())); + disconnect(item, SIGNAL(dataChanged())); + delete item; + } + if (d->callItems.size() > 0) + emit callsChanged(); + d->callItems.clear(); +} + +void CallManager::modemValidityChanged(bool valid) +{ + TRACE; + if (valid) + emit connected(); +} + +QList CallManager::getCallItems() const +{ + TRACE; + return d->callItems.values(); +} + +int CallManager::callCount() const +{ + TRACE; + qDebug()<<"call count is currently = "<callItems.size(); + return d->callItems.size(); +} + +int CallManager::multipartyCallCount() const +{ + TRACE; + int call_count = 0; + foreach (CallItem *c, d->callItems) { + if(c->multiparty()) { + call_count++; + } + } + return call_count; +} + +CallItem *CallManager::activeCall() const +{ + TRACE; + if (d->callItems.size()) + foreach (CallItem *c, d->callItems) + if (c->state() == STATE_ACTIVE) + return c; + return NULL; +} + +CallItem *CallManager::heldCall() const +{ + TRACE; + if (d->callItems.size()) + foreach (CallItem *c, d->callItems) + if (c->state() == STATE_HELD) + return c; + return NULL; +} + +CallItem *CallManager::dialingCall() const +{ + TRACE; + if (d->callItems.size()) + foreach (CallItem *c, d->callItems) + if (c->state() == STATE_DIALING) + return c; + return NULL; +} + +CallItem *CallManager::incomingCall() const +{ + TRACE; + if (d->callItems.size()) + foreach (CallItem *c, d->callItems) + if (c->state() == STATE_INCOMING) + return c; + return NULL; +} + +CallItem *CallManager::waitingCall() const +{ + TRACE; + if (d->callItems.size()) + foreach (CallItem *c, d->callItems) + if (c->state() == STATE_WAITING) + return c; + return NULL; +} + +CallItem *CallManager::alertingCall() const +{ + TRACE; + if (d->callItems.size()) + foreach (CallItem *c, d->callItems) + if (c->state() == STATE_ALERTING) + return c; + return NULL; +} + +void CallManager::setActiveCall(const CallItem &call) +{ + TRACE; + swapCalls(); +} + +void CallManager::dial(const QString &number) +{ + TRACE; + // Nothing to do if the modem is not powered up + + if(!modem()->powered()) { + emit callsChanged(); + return; + } + + // If not online (flight mode?), check if the requested number is + // one of the allowed EmergencyNumbers, in which case, continue. + // Otherwise, notify that only Emergency calls are permitted. + if(!modem()->online()) { + if(modem()->powered() && !emergencyNumbers().contains(number)) { + emit callsChanged(); + emit onlyEmergencyCalls(); + return; + } + } + + proceedCallDial(number); +} + +void CallManager::privateChat(const CallItem &call) +{ + TRACE; +} + +/* + * Resource Policy Manager Handler slots + */ +void CallManager::deniedCallDial() +{ + TRACE; + qCritical() << QString("Denied: Dial resource"); +} + +void CallManager::lostCallDial() +{ + TRACE; + qCritical() << QString("Lost: Dial resource"); + hangupAll(); +} + +void CallManager::proceedCallDial(const QString number) +{ + TRACE; + OfonoVoiceCallManager::dial(stripLineID(number), QString()); +} + +void CallManager::deniedCallAnswer() +{ + TRACE; + qCritical() << QString("Denied: Call resource"); + hangupAll(); +} + +void CallManager::deniedIncomingCall(CallItem *call) +{ + TRACE; + + qCritical() << QString("Denied: Incoming Call resource"); + qDebug() << QString("Insert new CallItem %1").arg(call->path()); + emit callsChanged(); + emit incomingCall(call); +} + +void CallManager::lostIncomingCall(CallItem *call) +{ + TRACE; + Q_UNUSED(call) + qCritical() << QString("Lost: Incoming Call resource"); +} + +void CallManager::proceedIncomingCall(CallItem *call) +{ + TRACE; + qDebug() << QString("Acquired: Incoming Call resource"); + qDebug() << QString("Insert new CallItem %1").arg(call->path()); + emit callsChanged(); + emit incomingCall(call); +} + +/* + * Private slots for async replies + */ + +void CallManager::updateCallItems() +{ + TRACE; + bool changed = false; + + // If ofono call list is empty (no calls), empty our CallItem list too. + if (getCalls().isEmpty() && !d->callItems.isEmpty()) { + qDebug() << QString("Purging all CallItems"); + foreach (CallItem *item, d->callItems) + delete item; + d->callItems.clear(); + emit callsChanged(); + return; + } + + // Remove CallItems that are not in the ofono "calls" list + QMutableHashIterator iter(d->callItems); + while (iter.hasNext()) { + CallItem *item = iter.next().value(); + // This item is not in the ofono list, remove it + if (!getCalls().contains(item->path())) { + qDebug() << QString("Removing old CallItem %1").arg(item->path()); + delete item; + iter.remove(); + changed = true; + } + } + + // Insert new CallItems for paths in the ofono "calls" list we are missing + foreach (QString path, getCalls()) { + // Insert a new CallItem + if (!d->callItems.contains(path)) { + qDebug() << QString("Inserting new CallItem %1").arg(path); + CallItem *call = new CallItem(path); + connect (call, SIGNAL(stateChanged()), + this, SLOT(callStateChanged())); + connect (call, SIGNAL(multipartyChanged()), + this, SLOT(callMultipartyChanged())); + d->callItems.insert(path, call); + + // NOTE: Must explicity bubble this up since incoming and waiting + // calls do not "changeState" unless they are handled or + // timeout + if ((call->state() == STATE_INCOMING) || + (call->state() == STATE_WAITING)) { + proceedIncomingCall(call); + } else { + changed = true; + } + } + } + + if (changed) + emit callsChanged(); +} + +void CallManager::addCall(const QString &path) +{ + TRACE; + qDebug() << QString("CallAdded: \"%1\"").arg(path); + qDebug() <<"Call number is now "<< callCount(); + updateCallItems(); + emit callCountChanged(callCount()); +} + +void CallManager::removeCall(const QString &path) +{ + TRACE; + qDebug() << QString("CallRemoved: \"%1\"").arg(path); + qDebug() <<"Call number is now "<< callCount(); + updateCallItems(); + emit callCountChanged(callCount()); +} + +void CallManager::dialFinished(const bool status) +{ + TRACE; + qDebug() <<"Call number is now "<< callCount(); + + if (!status) { + qCritical() << QString("dial() Failed: %1 - %2") + .arg(errorName()) + .arg(errorMessage()); + // Fix BMC#10848: + // Notify that state of the call has changed when the dialing fails + emit callsChanged(); + } +} + +void CallManager::hangupAllFinished(const bool status) +{ + TRACE; + if (!status) + qCritical() << QString("hangupAll() Failed: %1 - %2") + .arg(errorName()) + .arg(errorMessage()); + + //If there are still calls for some reason, delete them. + if (callCount() > 0) + { + foreach (CallItem *item, d->callItems) { + disconnect(item, SIGNAL(stateChanged())); + disconnect(item, SIGNAL(dataChanged())); + delete item; + } + + d->callItems.clear(); + callCount(); + emit callsChanged(); + } + callCount(); +} + +void CallManager::swapFinished(const bool status) +{ + TRACE; + if (!status) + qCritical() << QString("swapCalls() Failed: %1 - %2") + .arg(errorName()) + .arg(errorMessage()); +} + +void CallManager::holdAndAnswerFinished(const bool status) +{ + TRACE; + if (!status) + qCritical() << QString("HoldAndAnswer() Failed: %1 - %2") + .arg(errorName()) + .arg(errorMessage()); +} + +void CallManager::transferFinished(const bool status) +{ + TRACE; + if (!status) + qCritical() << QString("Transfer() Failed: %1 - %2") + .arg(errorName()) + .arg(errorMessage()); +} + +void CallManager::releaseAndAnswerFinished(const bool status) +{ + TRACE; + if (!status) + qCritical() << QString("ReleaseAndAnswer() Failed: %1 - %2") + .arg(errorName()) + .arg(errorMessage()); +} + +void CallManager::privateChatFinished(const bool status) +{ + TRACE; + if (!status) + qCritical() << QString("PrivateChat() Failed: %1 - %2") + .arg(errorName()) + .arg(errorMessage()); +} + +void CallManager::createMultipartyFinished(const bool status) +{ + TRACE; + if (!status) + qCritical() << QString("CreateMultiparty() Failed: %1 - %2") + .arg(errorName()) + .arg(errorMessage()); +} + +void CallManager::hangupMultipartyFinished(const bool status) +{ + TRACE; + if (!status) + qCritical() << QString("HangupMultiparty() Failed: %1 - %2") + .arg(errorName()) + .arg(errorMessage()); +} + +void CallManager::sendTonesFinished(const bool status) +{ + TRACE; + if (!status) + qCritical() << QString("SendTones() Failed: %1 - %2") + .arg(errorName()) + .arg(errorMessage()); +} + +void CallManager::callStateChanged() +{ + CallItem *call = dynamic_cast(sender()); + qDebug() << QString("%1 (%2) state has changed to %3") + .arg(call->path()) + .arg(call->lineID()) + .arg(call->state()); + qDebug() << "number of calls "<< callCount(); + emit callsChanged(); +} + +void CallManager::callMultipartyChanged() +{ + TRACE; + emit callsChanged(); + emit multipartyCallCountChanged(multipartyCallCount()); +} + +void CallManager::callChangedSlot() +{ + TRACE + qDebug()<<"callChanged called!"; +} + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/callmanager.h b/src/callmanager.h new file mode 100644 index 0000000..5ec3362 --- /dev/null +++ b/src/callmanager.h @@ -0,0 +1,110 @@ +/* + * hfdialer - Hands Free Voice Call Manager + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#ifndef CALLMANAGER_H +#define CALLMANAGER_H + +#include + +#include "callitem.h" +#include + +class CallManager: public OfonoVoiceCallManager +{ + Q_OBJECT; + + Q_PROPERTY(int multipartyCallCount READ multipartyCallCount NOTIFY multipartyCallCountChanged); + Q_PROPERTY(int callCount READ callCount NOTIFY callCountChanged); + +public: + explicit CallManager(const QString &modemPath="", QObject *parent=0); + virtual ~CallManager(); + + /* Properties */ + int multipartyCallCount() const; + int callCount() const; + + // Extended version of OfonoVoiceCallManager::getCalls() that + // returns QList of CallItems rather than strings + Q_INVOKABLE QList getCallItems() const; + + Q_INVOKABLE CallItem *activeCall() const; + Q_INVOKABLE CallItem *heldCall() const; + Q_INVOKABLE CallItem *dialingCall() const; + Q_INVOKABLE CallItem *incomingCall() const; + Q_INVOKABLE CallItem *waitingCall() const; + Q_INVOKABLE CallItem *alertingCall() const; + +public Q_SLOTS: + void setActiveCall(const CallItem &call); + + // Overloaded version of OfonoVoiceCallManager::dial() that + // assumes CLIR == default + void dial(const QString &number); + + // Overloaded version of OfonoVoiceCallManager::privateChat() that + // takes a CallItem rather than path string + void privateChat(const CallItem &call); + + // Push denied answer signal to upper layers from call proxy + void deniedCallAnswer(); + +Q_SIGNALS: + // Abstracts both callAdded() and callRemoved() into a single event + void callsChanged(); + + void incomingCall(CallItem *call); + void incomingCall(QString path); + void callDisconnected(const CallItem &call); + void onlyEmergencyCalls(); + void connected(); + void multipartyCallCountChanged(const int &count); + void callCountChanged(const int &count); + +private Q_SLOTS: + void callChangedSlot(); + void updateCallItems(); + void addCall(const QString &path); + void removeCall(const QString &path); + void dialFinished(const bool status); + void hangupAllFinished(const bool status); + void swapFinished(const bool status); + void holdAndAnswerFinished(const bool status); + void transferFinished(const bool status); + void releaseAndAnswerFinished(const bool status); + void privateChatFinished(const bool status); + void createMultipartyFinished(const bool status); + void hangupMultipartyFinished(const bool status); + void sendTonesFinished(const bool status); + void callStateChanged(); + void callMultipartyChanged(); + void modemValidityChanged(bool valid); + + // Handlers for Resource Policy Manager states and conditions + void proceedCallDial(const QString number); + void deniedCallDial(); + void lostCallDial(); + void proceedIncomingCall(CallItem *call); + void deniedIncomingCall(CallItem *call); + void lostIncomingCall(CallItem *call); + +private: + class CallManagerPrivate *d; + + Q_DISABLE_COPY(CallManager); +}; + +#endif // CALLMANAGER_H + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/callproxy.cpp b/src/callproxy.cpp new file mode 100644 index 0000000..272de57 --- /dev/null +++ b/src/callproxy.cpp @@ -0,0 +1,327 @@ +/* + * hfdialer - Hands Free Voice Call Manager + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#include +#include "common.h" +#include "callproxy.h" +#include "managerproxy.h" + +// Returns a valid QDateTime if parsable as such, otherwise the result +// will be !isValid() +static QDateTime qDateTimeFromOfono(const QString &val) +{ + TRACE; + QDateTime result; + + if (val.isEmpty()) + return result; + + // NOTE: Ofono formats time to string with the following format spec: + // %Y-%m-%dT%H:%M:%S%z (for example: "2001-10-19T10:32:30-05:00") + + // Start by trying to parse this as an ISODate "YYYY-MM-DDTHH:MM:SSTZD" + result = QDateTime::fromString(val,Qt::ISODate); +#ifdef VERBOSE + qDebug() << QString("Converted %1 with Qt::ISODate: %2") + .arg(val) + .arg(result.toString()); +#endif + + if (!result.isValid()) { + // ISODate conversion has failed, fallback to manual method + // NOTE: QDateTime::fromString(val, Qt::ISODate) Fails since the date + // format from Ofono is in RFC 822 form, but QDateTime can't parse it + struct tm time_tm; + QByteArray bytes = val.toAscii(); + const char *str = bytes.constData(); + if (strptime(str, "%Y-%m-%dT%H:%M:%S%z", &time_tm) != NULL) { + time_t t = mktime(&time_tm); + if (t >= (time_t)(0)) { + result.setTime_t(t); +#ifdef VERBOSE + qDebug() << QString("Converted %1 with strptime: %2") + .arg(val) + .arg(result.toString()); +#endif + } + } + + if (!result.isValid()) + qCritical() << QString("Format error, unknown date/time: %1") + .arg(str); + } + + return result; +} + +CallProxy::CallProxy(const QString &callPath) + : org::ofono::VoiceCall(OFONO_SERVICE, + callPath, + QDBusConnection::systemBus()), + m_lineid(QString()), + m_state(QString()), + m_startTime(QDateTime()), + m_reason(QString()), + m_connected(false), + m_multiparty(false) +{ + TRACE; + + if (!org::ofono::VoiceCall::isValid()) { + qCritical() << QString("Failed to connect to %1 for call %2:\n\t%3") + .arg(staticInterfaceName()) + .arg(callPath) + .arg(lastError().message()); + } else { + QDBusPendingReply reply; + QDBusPendingCallWatcher *watcher; + + reply = GetProperties(); + watcher = new QDBusPendingCallWatcher(reply); + + // Force this to be sync to ensure we have initial properties + watcher->waitForFinished(); + getPropertiesFinished(watcher); + + if (isValid()) { + connect(this, + SIGNAL(PropertyChanged(const QString&,const QDBusVariant&)), + SLOT(propertyChanged(const QString&,const QDBusVariant&))); + connect(this, SIGNAL(DisconnectReason(const QString&)), + SLOT(disconnectReason(const QString&))); + } else { + qCritical() << QString("Invalid CallProxy instance: state == %1") + .arg(m_state); + } + } +} + +CallProxy::~CallProxy() +{ + TRACE; + // FIXME: Do something here!!! +} + +bool CallProxy::isValid() +{ + TRACE; + return (org::ofono::VoiceCall::isValid() && + m_connected && + (m_state != "disconnected")); +} + +QString CallProxy::lineID() const +{ + TRACE; + return m_lineid; +} + +QString CallProxy::name() const +{ + TRACE; + return m_name; +} + +QString CallProxy::state() const +{ + TRACE; + return m_state; +} + +QDateTime CallProxy::startTime() const +{ + TRACE; + return m_startTime; +} + +int CallProxy::duration() const +{ + TRACE; + if (m_startTime.isValid()) + return m_startTime.secsTo(QDateTime::currentDateTime()); + else + return 0; +} + +QString CallProxy::reason() const +{ + TRACE; + return m_reason; +} + +bool CallProxy::multiparty() const +{ + TRACE; + return m_multiparty; +} + +void CallProxy::answer() +{ + TRACE; + proceedCallAnswer(); +} + +void CallProxy::proceedCallAnswer() +{ + TRACE; + + QDBusPendingReply reply; + QDBusPendingCallWatcher *watcher; + + reply = Answer(); + watcher = new QDBusPendingCallWatcher(reply); + + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + SLOT(answerFinished(QDBusPendingCallWatcher*))); +} + +void CallProxy::deniedCallAnswer() +{ + TRACE; + + // Hang up the incoming call, if resources to accept it are inavailabe + hangup(); + + emit ManagerProxy::instance()->callManager()->deniedCallAnswer(); +} + +void CallProxy::deflect(const QString toNumber) +{ + TRACE; + + QDBusPendingReply reply; + QDBusPendingCallWatcher *watcher; + + reply = Deflect(toNumber); + watcher = new QDBusPendingCallWatcher(reply); + + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + SLOT(deflectFinished(QDBusPendingCallWatcher*))); +} + +void CallProxy::hangup() +{ + TRACE; + + QDBusPendingReply<> reply; + QDBusPendingCallWatcher *watcher; + + reply = Hangup(); + watcher = new QDBusPendingCallWatcher(reply); + + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + SLOT(hangupFinished(QDBusPendingCallWatcher*))); +} + +void CallProxy::getPropertiesFinished(QDBusPendingCallWatcher *watcher) +{ + TRACE; + + QDBusPendingReply reply = *watcher; + + if (reply.isError()) { + qCritical() << QString("Failed to connect to %1 for call %2:\n\t%3") + .arg(staticInterfaceName()) + .arg(path()) + .arg(lastError().message()); + return; + } + + QVariantMap props = reply.value(); + + QString l_start; + + m_lineid = qdbus_cast(props["LineIdentification"]); + m_name = qdbus_cast(props["Name"]); + m_state = qdbus_cast(props["State"]); + l_start = qdbus_cast(props["StartTime"]); + m_multiparty = qdbus_cast(props["Multiparty"]); + + setStartTimeFromString(l_start); + + // Indicate for this instance, that we've actually performed at least + // one round trip call to this VoiceCall and we are in sync with it + m_connected = true; +} + +void CallProxy::answerFinished(QDBusPendingCallWatcher *watcher) +{ + TRACE; + QDBusPendingReply reply = *watcher; + if (reply.isError()) + qCritical() << QString("Answer() Failed: %1 - %2") + .arg(reply.error().name()) + .arg(reply.error().message()); +} + +void CallProxy::deflectFinished(QDBusPendingCallWatcher *watcher) +{ + TRACE; + QDBusPendingReply reply = *watcher; + if (reply.isError()) + qCritical() << QString("Deflect() Failed: %1 - %2") + .arg(reply.error().name()) + .arg(reply.error().message()); +} + +void CallProxy::hangupFinished(QDBusPendingCallWatcher *watcher) +{ + TRACE; + QDBusPendingReply<> reply = *watcher; + if (reply.isError()) + qCritical() << QString("Hangup() Failed: %1 - %2") + .arg(reply.error().name()) + .arg(reply.error().message()); +} + +void CallProxy::propertyChanged(const QString &in0, const QDBusVariant &in1) +{ + TRACE; + + if (in0 == "LineIdentification") { + m_lineid = qdbus_cast(in1.variant()); + emit dataChanged(); + } else if (in0 == "State") { + m_state = qdbus_cast(in1.variant()); + emit stateChanged(); + } else if (in0 == "StartTime") { + setStartTimeFromString(qdbus_cast(in1.variant())); + } else if (in0 == "Multiparty") { + m_multiparty = qdbus_cast(in1.variant()); + emit multipartyChanged(); + } else { + qDebug() << QString("Unexpected property \"%1\" changed...").arg(in0); + } +} + +void CallProxy::disconnectReason(const QString &in0) +{ + TRACE; + m_reason = in0; + emit callDisconnected(in0); +} + +void CallProxy::setStartTimeFromString(const QString &val) +{ + TRACE; + if (val.isEmpty()) + return; + + m_startTime = qDateTimeFromOfono(val); + + if (!m_startTime.isValid()) + m_startTime = QDateTime::QDateTime::currentDateTime(); +} + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/callproxy.h b/src/callproxy.h new file mode 100644 index 0000000..492e7bb --- /dev/null +++ b/src/callproxy.h @@ -0,0 +1,102 @@ +/* + * hfdialer - Hands Free Voice Call Manager + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#ifndef CALLPROXY_H +#define CALLPROXY_H + +#include "voicecall_interface.h" +#include +#include + +#define OFONO_SERVICE "org.ofono" +#define OFONO_MANAGER_PATH "/" + +#define DEFAULT_CLIR "default" + +class CallProxy: public org::ofono::VoiceCall +{ + Q_OBJECT; + + Q_PROPERTY(QString lineID READ lineID); + Q_PROPERTY(QString name READ name); + Q_PROPERTY(QString state READ state); + Q_PROPERTY(QDateTime startTime READ startTime); + Q_PROPERTY(int duration READ duration); + Q_PROPERTY(QString reason READ reason); + Q_PROPERTY(bool multiparty READ multiparty); + +public: + CallProxy(const QString &callPath); + virtual ~CallProxy(); + bool isValid(); + + QString lineID() const; + QString name() const; + QString state() const; + QDateTime startTime() const; + int duration() const; + QString reason() const; + bool multiparty() const; + +public Q_SLOTS: + // Answers the incoming call + // NOTE: only valid if state is incoming + void answer(); + + // Deflects the incoming or waiting call to number provided. + // NOTE: only valid if state is incoming or waiting + void deflect(const QString toNumber); + + // Hangs up the voice call + void hangup(); + +Q_SIGNALS: + void callDisconnected(const QString &reason); + void stateChanged(); + void dataChanged(); + void multipartyChanged(); + +private Q_SLOTS: + // Slots to handle asyncronous DBus replies + void getPropertiesFinished(QDBusPendingCallWatcher *watcher); + void answerFinished(QDBusPendingCallWatcher *watcher); + void deflectFinished(QDBusPendingCallWatcher *watcher); + void hangupFinished(QDBusPendingCallWatcher *watcher); + + // Slots to handle DBus signals from ofono + void propertyChanged(const QString &in0, const QDBusVariant &in1); + void disconnectReason(const QString &in0); + + void proceedCallAnswer(); + void deniedCallAnswer(); + +private: + void setStartTimeFromString(const QString &val); + +private: + QStringList m_properties; + QString m_lineid; + QString m_name; + QString m_state; + QDateTime m_startTime; + QString m_reason; + bool m_connected; + bool m_multiparty; + + Q_DISABLE_COPY(CallProxy); +}; + +#endif // CALLPROXY_H + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..453640d --- /dev/null +++ b/src/common.h @@ -0,0 +1,31 @@ +/* + * hfdialer - Hands Free Voice Call Manager + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#ifndef COMMON_H +#define COMMON_H + +class QString; + +#ifdef VERBOSE +#include +#define TRACE qDebug() << "[" << __FILE__ << "]" << __func__ << "():" << __LINE__; +#else +#define TRACE +#endif + +QString stripLineID(QString lineid); + +#endif // COMMON_H + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/dbus/com.hfdialer.xml b/src/dbus/com.hfdialer.xml new file mode 100644 index 0000000..e78a08e --- /dev/null +++ b/src/dbus/com.hfdialer.xml @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/src/dbus/org.ofono.history.xml b/src/dbus/org.ofono.history.xml new file mode 100644 index 0000000..f495399 --- /dev/null +++ b/src/dbus/org.ofono.history.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + diff --git a/src/dbus/org.ofono.manager.xml b/src/dbus/org.ofono.manager.xml new file mode 100644 index 0000000..dd11568 --- /dev/null +++ b/src/dbus/org.ofono.manager.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + diff --git a/src/dbus/org.ofono.modem.xml b/src/dbus/org.ofono.modem.xml new file mode 100644 index 0000000..fee9d0c --- /dev/null +++ b/src/dbus/org.ofono.modem.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/dbus/org.ofono.operator.xml b/src/dbus/org.ofono.operator.xml new file mode 100644 index 0000000..f8ff35e --- /dev/null +++ b/src/dbus/org.ofono.operator.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + diff --git a/src/dbus/org.ofono.voicecall.xml b/src/dbus/org.ofono.voicecall.xml new file mode 100644 index 0000000..7b1c710 --- /dev/null +++ b/src/dbus/org.ofono.voicecall.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/dbusdialeradapter.cpp b/src/dbusdialeradapter.cpp new file mode 100644 index 0000000..7868615 --- /dev/null +++ b/src/dbusdialeradapter.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2011, Tom Swindell. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#include "common.h" +#include "dbusdialeradapter.h" +#include "managerproxy.h" + +DBusDialerAdapter::DBusDialerAdapter(DialerApplication *application) + : QDBusAbstractAdaptor(application) +{ + TRACE; +} + +DBusDialerAdapter::~DBusDialerAdapter() +{ + TRACE; +} + +void DBusDialerAdapter::call(const QString &msisdn) +{ + TRACE; + + if(!ManagerProxy::instance()->callManager()) return; + ManagerProxy::instance()->callManager()->dial(msisdn); +} + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/dbusdialeradapter.h b/src/dbusdialeradapter.h new file mode 100644 index 0000000..8c851e4 --- /dev/null +++ b/src/dbusdialeradapter.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2011, Tom Swindell. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ +#ifndef DBUSDIALERADAPTER_H +#define DBUSDIALERADAPTER_H + +#include "dialerapplication.h" +#include + +class DBusDialerAdapter : public QDBusAbstractAdaptor +{ + Q_OBJECT; + Q_CLASSINFO("D-Bus Interface", "com.hfdialer"); + +public: + explicit DBusDialerAdapter(DialerApplication *application); + ~DBusDialerAdapter(); + +Q_SIGNALS: + +public Q_SLOTS: + void call(const QString &msisdn); +}; + +#endif // DBUSDIALERADAPTER_H + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/dbustypes.cpp b/src/dbustypes.cpp new file mode 100644 index 0000000..49808ec --- /dev/null +++ b/src/dbustypes.cpp @@ -0,0 +1,57 @@ +/* + * hfdialer - Hands Free Voice Call Manager + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#include "dbustypes.h" + +// Marshall the CallHistoryEvent data into a D-BUS argument +QDBusArgument & operator << (QDBusArgument &argument, + const CallHistoryEvent &d) +{ + argument.beginStructure(); + argument << d.id << d.lineid << d.type << d.start << d.end; + argument.endStructure(); + return argument; +} + +// Retrieve the CallHistoryEvent data from the D-BUS argument +const QDBusArgument & operator >> (const QDBusArgument &argument, + CallHistoryEvent &d) +{ + argument.beginStructure(); + argument >> d.id >> d.lineid >> d.type >> d.start >> d.end; + argument.endStructure(); + return argument; +} + +// Marshall the OfonoPathProperties data into a D-BUS argument +QDBusArgument & operator << (QDBusArgument &argument, + const OfonoPathProperties &d) +{ + argument.beginStructure(); + argument << d.path << d.properties; + argument.endStructure(); + return argument; +} + +// Retrieve the OfonoPathProperties data from the D-BUS argument +const QDBusArgument & operator >> (const QDBusArgument &argument, + OfonoPathProperties &d) +{ + argument.beginStructure(); + argument >> d.path >> d.properties; + argument.endStructure(); + return argument; +} + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/dbustypes.h b/src/dbustypes.h new file mode 100644 index 0000000..6569a66 --- /dev/null +++ b/src/dbustypes.h @@ -0,0 +1,85 @@ +/* + * hfdialer - Hands Free Voice Call Manager + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#ifndef DIALERDBUSTYPES_H +#define DIALERDBUSTYPES_H + +#include +#include +#include + +struct CallHistoryEvent +{ + uint id; + QString lineid; + ushort type; + int start; + int end; +}; + +Q_DECLARE_METATYPE ( CallHistoryEvent ) + +// Marshall the CallHistoryEvent data into a D-BUS argument +QDBusArgument &operator<<(QDBusArgument &argument, + const CallHistoryEvent &mystruct); + +// Retrieve the CallHistoryEvent data from the D-BUS argument +const QDBusArgument &operator>>(const QDBusArgument &argument, + CallHistoryEvent &mystruct); + +typedef QList< CallHistoryEvent > QArrayOfHistoryEvent; + +Q_DECLARE_METATYPE ( QArrayOfHistoryEvent ) + +/* + * New DBus type needed for Ofono calls that expect an array of + * Object paths and that objects properties: "a(oa{sv})" + * + * Used in: + * org.ofono.VoiceCallManager.GetCalls() + * org.ofono.NetworkRegistraion.GetOperators() + * org.ofono.NetworkRegistration.Scan() + * org.ofono.ConnectionManager.GetContexts() + * org.ofono.MessageManager.GetMessages() + */ +struct OfonoPathProperties +{ + QDBusObjectPath path; + QVariantMap properties; +}; + +Q_DECLARE_METATYPE ( OfonoPathProperties ) + +// Marshall the OfonoPathProperties data into a D-BUS argument +QDBusArgument &operator<<(QDBusArgument &argument, + const OfonoPathProperties &mystruct); + +// Retrieve the CallHistoryEvent data from the D-BUS argument +const QDBusArgument &operator>>(const QDBusArgument &argument, + OfonoPathProperties &mystruct); + +typedef QList< OfonoPathProperties > QArrayOfPathProperties; + +Q_DECLARE_METATYPE ( QArrayOfPathProperties ) + +inline void registerMyDataTypes() { + qDBusRegisterMetaType< OfonoPathProperties >(); + qDBusRegisterMetaType< QArrayOfPathProperties >(); + qDBusRegisterMetaType< CallHistoryEvent >(); + qDBusRegisterMetaType< QArrayOfHistoryEvent >(); +} + +#endif //DIALERDBUSTYPES_H + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/dialerapplication.cpp b/src/dialerapplication.cpp new file mode 100644 index 0000000..98d6793 --- /dev/null +++ b/src/dialerapplication.cpp @@ -0,0 +1,299 @@ +/* + * hfdialer - Hands Free Voice Call Manager + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#include "common.h" +#include "dbustypes.h" +#include "dialerapplication.h" +#include "dialercontext.h" +#include "dbusdialeradapter.h" +#include "hfdialer_adaptor.h" +#include "pacontrol.h" +#include "qmlmainwindow.h" +#include +#include +#include + +#define OFONO_VOICECALLMANAGER_INTERFACE "org.ofono.VoiceCallManager" + +DialerApplication::DialerApplication(int &argc, char **argv) + : QApplication(argc, argv) +{ + TRACE; + init(); +} + +void DialerApplication::releasePrestart() +{ + TRACE; + // Now is the time for set up and display of information + // that needs to be done to allow the dialeror other + // pages to display correctly when opened. GenericPage has a + // activateWidgets() method for common setup and + // each Page type (dialer, people, favorites, etc) can also + // implement the method for Page specific setup and signal + // and slot connections + + +} + +void DialerApplication::restorePrestart() +{ + TRACE; + // Now is the time for clean up and resetting an information + // that needs to be done to allow the dialer pages to display + // correctly when reopened. GenericPage has a + // deactivateAndResetWidgets() method for common setup and + // each Page type (dialer, people, favorites, etc) can also + // implement the method for Page specific clean up + + // Call the default implementation to hide the window. +} + +void DialerApplication::connectAll() +{ + TRACE; + + ManagerProxy *m_manager = ManagerProxy::instance(); + if (m_manager->modem() && + m_manager->modem()->isValid() && + !m_manager->modem()->path().isEmpty()) + { + qDebug() << QString("Connecting to CallManager...."); + m_manager->setNetwork(m_manager->modem()->path()); + m_manager->setCallManager(m_manager->modem()->path()); + m_manager->setVolumeManager(m_manager->modem()->path()); + m_manager->setVoicemail(m_manager->modem()->path()); + + connect(m_manager->network(), SIGNAL(connected()), this, + SLOT(networkConnected())); + connect(m_manager->network(), SIGNAL(disconnected()), this, + SLOT(networkDisconnected())); + connect(m_manager->callManager(), SIGNAL(connected()), this, + SLOT(callManagerConnected())); + connect(m_manager->callManager(), SIGNAL(disconnected()), this, + SLOT(callManagerDisconnected())); + connect(m_manager->callManager(), SIGNAL(callsChanged()), this, + SLOT(onCallsChanged())); + connect(m_manager->voicemail(), SIGNAL(messagesWaitingChanged()), this, + SLOT(messagesWaitingChanged())); + PAControl* paControl = PAControl::instance(); + qDebug()<<"UBER DEBUG!!! I can has connect with paControl onCallsChanged"; + connect(m_manager->callManager(), SIGNAL(callsChanged()), paControl, + SLOT(onCallsChanged())); + } +} + +bool DialerApplication::isConnected() +{ + TRACE; + return m_connected; +} + +bool DialerApplication::hasError() const +{ + TRACE; + return !m_lastError.isEmpty(); +} + +QString DialerApplication::lastError() const +{ + TRACE; + return m_lastError; +} + +void DialerApplication::setError(const QString msg) +{ + TRACE; + m_lastError.clear(); + m_lastError = QString(msg); +} + +DialerApplication *DialerApplication::instance() +{ + TRACE; + return qobject_cast(QApplication::instance()); +} + +void DialerApplication::init() +{ + TRACE; + m_connected = false; + m_lastError = QString(); + + // Notify Qt of our custom DBus MetaTypes + registerMyDataTypes(); + + m_manager = ManagerProxy::instance(); + if (!m_manager || !m_manager->isValid()) + //% "Failed to connect to org.ofono.Manager: is ofonod running?" + setError(qtTrId("xx_no_ofono_error")); + else + m_connected = true; + + HfdialerAdaptor *adapter = new HfdialerAdaptor(this); + if(!adapter) + { + qWarning() << "DBus adapter instantiation failed."; + } + + if(!QDBusConnection::systemBus().registerService("com.hfdialer")) + { + qCritical() << "Error registering on zee bus: " << + QDBusConnection::systemBus().lastError().message(); + } + + if(!QDBusConnection::systemBus().registerObject(DBUS_SERVICE_PATH, this)) + { + qCritical() << "Error registering dbus object:" << + QDBusConnection::systemBus().lastError().message(); + } + connect(m_manager, SIGNAL(modemChanged()), + SLOT(modemChanged())); + + this->connectAll(); + + m_mainWindow = QMLMainWindow::instance(); + connect(m_mainWindow, SIGNAL(closeWindow()),this, SLOT(closeWindow())); +} + +void DialerApplication::modemChanged() +{ + TRACE; + if (m_manager->modem() != NULL) + { + connect(m_manager->modem(), SIGNAL(connected()), + SLOT(modemConnected())); + connect(m_manager->modem(), SIGNAL(disconnected()), + SLOT(modemDisconnected())); + } + else + { + qDebug()<<"modem is null"; + } +} + +void DialerApplication::modemConnected() +{ + TRACE; + //TODO: Handle multiple modems + if (m_manager->modem() && m_manager->modem()->isValid()) + { + m_modem = m_manager->modem(); + + m_modem->setPowered(true); + + qDebug() << QString("Modem connected"); + connect(m_modem, SIGNAL(interfacesChanged(QStringList)), this, + SLOT(modemInterfacesChanged(QStringList))); + connect(m_modem, SIGNAL(poweredChanged(bool)), this, + SLOT(modemPowered(bool))); + + if (m_modem->powered() && + m_modem->interfaces().contains(OFONO_VOICECALLMANAGER_INTERFACE)) + { + /* connect all now, modem is enabled */ + this->connectAll(); + } + } +} + +void DialerApplication::modemDisconnected() +{ + TRACE; + //TODO: Handle multiple modems +} + +void DialerApplication::modemInterfacesChanged(QStringList interfaces) +{ + TRACE; + qDebug() << QString("Modem Interfaces: ") << interfaces; + + if (interfaces.contains(OFONO_VOICECALLMANAGER_INTERFACE) && + m_manager->modem()->powered()) + { + this->connectAll(); + } +} + +void DialerApplication::modemPowered(bool isPowered) +{ + TRACE; + qDebug() << QString("Modem Powered: ") << isPowered; + + if (isPowered && + m_manager->modem()->interfaces().contains(OFONO_VOICECALLMANAGER_INTERFACE)) + { + this->connectAll(); + } +} + +void DialerApplication::networkConnected() +{ + TRACE; + if (m_manager->network() && m_manager->network()->isValid()) + m_network = m_manager->network(); +} + +void DialerApplication::networkDisconnected() +{ + TRACE; +} + +void DialerApplication::callManagerConnected() +{ + TRACE; + if (m_manager->callManager() && m_manager->callManager()->isValid()) + m_callManager = m_manager->callManager(); + + + qDebug() << QString("Disconnect calls changed signal"); + disconnect(m_callManager, SIGNAL(callsChanged())); + + qDebug() << QString("Disconnect incoming signal"); + disconnect(m_callManager, SIGNAL(incomingCall(CallItem*))); + + qDebug() << QString("Disconnect resource lost"); + disconnect(m_callManager, SIGNAL(callResourceLost(const QString))); +} + +void DialerApplication::callManagerDisconnected() +{ + TRACE; + qDebug() << QString("CallMgr disconnected"); +} + +void DialerApplication::messagesWaitingChanged() +{ + TRACE; +} + +void DialerApplication::onCallsChanged() +{ + TRACE; + QMLMainWindow::instance()->tryToShow(); +} + +void DialerApplication::raise() +{ + TRACE; + QMLMainWindow::instance()->tryToShow(); +} + +void DialerApplication::closeWindow() +{ + TRACE; + m_mainWindow->hide(); +} + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/dialerapplication.h b/src/dialerapplication.h new file mode 100644 index 0000000..756cb29 --- /dev/null +++ b/src/dialerapplication.h @@ -0,0 +1,84 @@ +/* + * hfdialer - Hands Free Voice Call Manager + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#ifndef DIALERAPPLICATION_h +#define DIALERAPPLICATION_h + +#include "managerproxy.h" +#include "qmlmainwindow.h" +#include +#include + +class DialerApplication: public QApplication +{ + Q_OBJECT; + + Q_PROPERTY(bool isConnected READ isConnected); + Q_PROPERTY(bool hasError READ hasError); + Q_PROPERTY(QString lastError READ lastError); + +public: + DialerApplication(int &argc, char **argv); + + static DialerApplication* instance(); + + virtual void releasePrestart(); + virtual void restorePrestart(); + + bool hasError() const; + QString lastError() const; + void setError(const QString msg); + + bool isConnected(); + +Q_SIGNALS: + void showUi(); + void hideUi(); +public Q_SLOTS: + void closeWindow(); + void raise(); +private Q_SLOTS: + void connectAll(); + void messagesWaitingChanged(); + + void modemChanged(); + void modemConnected(); + void modemDisconnected(); + void modemInterfacesChanged(QStringList interfaces); + void modemPowered(bool isPowered); + void networkConnected(); + void networkDisconnected(); + void callManagerConnected(); + void callManagerDisconnected(); + + void onCallsChanged(); + +private: + void init(); + + ManagerProxy *m_manager; + ModemProxy *m_modem; + NetworkProxy *m_network; + CallManager *m_callManager; + + bool m_connected; + QString m_lastError; + QMLMainWindow *m_mainWindow; + QDBusConnection *m_bus; + Q_DISABLE_COPY(DialerApplication); +}; + +#endif // DIALERAPPLICATION_H + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/dialercontext.cpp b/src/dialercontext.cpp new file mode 100644 index 0000000..c6ea624 --- /dev/null +++ b/src/dialercontext.cpp @@ -0,0 +1,229 @@ +/* + * hfdialer - Hands Free Voice Call Manager + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#include "common.h" +#include "dialercontext.h" + +#define AUTOSELECT OfonoModem::AutomaticSelect + +#ifndef CONFIG_KEY_TARGET_MODE +#define CONFIG_KEY_TARGET_MODE "/apps/dialer/mode" +#endif + +#include + +DialerContext *DialerContext::gContext = 0; + +class DialerContextPrivate +{ +public: + DialerContextPrivate() + : modemManager(0), + volumeManager(0), + voicemailManager(0), + callManager(0), + modesKey(CONFIG_KEY_TARGET_MODE) + { TRACE } + + ~DialerContextPrivate() { + delete volumeManager; + volumeManager = 0; + delete voicemailManager; + voicemailManager = 0; + delete callManager; + callManager = 0; + delete modemManager; + modemManager = 0; + } + + OfonoModemManager *modemManager; + OfonoCallVolume *volumeManager; + OfonoMessageWaiting *voicemailManager; + CallManager *callManager; + QStringList modes; + MGConfItem modesKey; +}; + + +DialerContext::DialerContext(QObject *parent) + : QObject(parent), + d(new DialerContextPrivate) +{ + if (gContext) + qFatal(__func__, ": There can be only one!"); + + // Read and configure default runtime modes + setModes(d->modesKey.value().toStringList()); + + // Create misc services + // TODO: We may not actually need this, since OfonoModem class + // allows for "auto selection" and switching of modems + d->modemManager = new OfonoModemManager(parent); + d->volumeManager = new OfonoCallVolume(AUTOSELECT,""); + d->voicemailManager = new OfonoMessageWaiting(AUTOSELECT,""); + d->callManager = new CallManager(); + + // OfonoModemManager signals to monitor + connect(d->modemManager, SIGNAL(modemAdded(const QString&)), + SLOT(onModemAdded(const QString&))); + connect(d->modemManager, SIGNAL(modemRemoved(const QString&)), + SLOT(onModemRemoved(const QString&))); + + // CallManager signals to monitor + connect(d->callManager, SIGNAL(validityChanged(bool)), + SLOT(onCallManagerValidityChanged(bool))); + connect(d->callManager, SIGNAL(callsChanged()), SLOT(onCallsChanged())); + + // OfonoCallVolume signals to monitor + connect(d->volumeManager, SIGNAL(validityChanged(bool)), + SLOT(onCallVolumeValidityChanged(bool))); + + // OfonoMessageWaiting signals to monitor + connect(d->voicemailManager, SIGNAL(validityChanged(bool)), + SLOT(onVoicemailValidityChanged(bool))); + connect(d->voicemailManager, SIGNAL(voicemailWaitingChanged(bool)), + SLOT(onVoicemailWaitingChanged(bool))); + connect(d->voicemailManager, SIGNAL(voicemailMessageCountChanged(int)), + SLOT(onVoicemailCountChanged(int))); + + // GConf Key change signals to monitor + connect(&d->modesKey, SIGNAL(valueChanged()), SLOT(onModesChanged())); + + if (d->callManager) + qDebug() << __func__ << ": Using modem - " + << d->callManager->modem()->path(); + + gContext = this; +} + +/* + * Public methods + */ +DialerContext::~DialerContext() +{ + delete d; + d = 0; + gContext=0; +} + +DialerContext *DialerContext::instance() +{ + if (!gContext) + gContext = new DialerContext(); + return gContext; +} + +OfonoModemManager* DialerContext::modemManager() const +{ + return d->modemManager; +} + +OfonoCallVolume* DialerContext::volumeManager() const +{ + return d->volumeManager; +} + +OfonoMessageWaiting* DialerContext::voicemailManager() const +{ + return d->voicemailManager; +} + +CallManager* DialerContext::callManager() const +{ + return d->callManager; +} + +QStringList DialerContext::modes() const +{ + return d->modes; +} + +void DialerContext::setModes(const QStringList &modelist) +{ + d->modes = modelist; + d->modes.removeDuplicates(); + emit modesChanged(); +} + +/* + * Private methods/slots + */ + +void DialerContext::onModemAdded(const QString &path) +{ + TRACE; + // TODO: Handle modem additions, maybe... + qWarning() << __func__ << ": Unhandled ModemAdded - " << path; +} + +void DialerContext::onModemRemoved(const QString &path) +{ + TRACE; + // TODO: Handle modem removals, currently active for sure, others, maybe... + qWarning() << __func__ << ": Unhandled ModemAdded - " << path; +} + +void DialerContext::onCallVolumeValidityChanged(bool valid) +{ + TRACE; + // TODO: Reset the volumeManager service reference + qWarning() << __func__ << ": valid? " << ((valid)?"true":"false"); +} + +void DialerContext::onVoicemailValidityChanged(bool valid) +{ + TRACE; + // TODO: Reset the voicemailManager service reference + qWarning() << __func__ << ": valid? " << ((valid)?"true":"false"); +} + +void DialerContext::onVoicemailWaitingChanged(bool waiting) +{ + TRACE; + // TODO: Send notifications (or bubble this up for UI to handle?) + qDebug() << __func__ << ": Messages? " << ((waiting)?"true":"false"); +} + +void DialerContext::onVoicemailCountChanged(int count) +{ + TRACE; + // TODO: Send notifications (or bubble this up for UI to handle?) + qDebug() << __func__ << ": Message count == " << count; +} + +void DialerContext::onCallManagerValidityChanged(bool valid) +{ + TRACE; + // TODO: Reset the callManager service reference + qWarning() << __func__ << ": valid? " << ((valid)?"true":"false"); +} + +void DialerContext::onCallsChanged() +{ + TRACE; + // TODO: Send notifications (or bubble this up for UI to handle?) + qDebug() << __func__ << ": Calls count == " + << d->callManager->getCalls().count(); +} + +void DialerContext::onModesChanged() +{ + TRACE; + setModes(d->modesKey.value().toStringList()); + // Send notification of change + emit modesChanged(); + qDebug() << __func__ << ": New modes == " << d->modes.join(", "); +} + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/dialercontext.h b/src/dialercontext.h new file mode 100644 index 0000000..634d0a2 --- /dev/null +++ b/src/dialercontext.h @@ -0,0 +1,92 @@ +/* + * hfdialer - Hands Free Voice Call Manager + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#ifndef DIALERCONTEXT_H +#define DIALERCONTEXT_H + +// libofono-qt headers +#include +#include +#include + +// local headers +#include "callmanager.h" + +// Convienence Macro for access to Class instance +#define DC DialerContext::instance() + +class DialerContext: public QObject +{ + Q_OBJECT; + + Q_PROPERTY(OfonoModemManager* modemManager READ modemManager); + Q_PROPERTY(OfonoCallVolume* volumeManager READ volumeManager); + Q_PROPERTY(OfonoMessageWaiting* voicemailManager READ voicemailManager); + Q_PROPERTY(CallManager* callManager READ callManager); + Q_PROPERTY(QStringList modes READ modes WRITE setModes); + +public: + virtual ~DialerContext(); + + static DialerContext *instance(); + + OfonoModemManager* modemManager() const; + OfonoCallVolume* volumeManager() const; + OfonoMessageWaiting* voicemailManager() const; + CallManager* callManager() const; + QStringList modes() const; + +public slots: + // Slot to set current mode at runtime + void setModes(const QStringList &modeList); + +Q_SIGNALS: + void modesChanged(); + +private Q_SLOTS: + + // Slots to handle signals from Manager oFono service + void onModemAdded(const QString &path); + void onModemRemoved(const QString &path); + + // Slots to handle signals from CallVolume oFono service + void onCallVolumeValidityChanged(bool valid); + + // Slots to handle signals from MessageWaiting oFono service + void onVoicemailValidityChanged(bool valid); + void onVoicemailWaitingChanged(bool waiting); + void onVoicemailCountChanged(int count); + + // Slots to handle signals from CallManager oFono service + void onCallManagerValidityChanged(bool valid); + void onCallsChanged(); + + // Slot to handle runtime mode changes in GConf key + void onModesChanged(); + +protected: + DialerContext(QObject *parent = 0); + +private: + DialerContext(const DialerContext&); + DialerContext& operator= (DialerContext&); + + class DialerContextPrivate *d; + + static DialerContext *gContext; +}; + +#endif // DIALERCONTEXT_H + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..dfe2f0b --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,64 @@ +/* + * hfdialer - Hands Free Voice Call Manager + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#include "dialercontext.h" +#include "dialerapplication.h" +#include "qmlmainwindow.h" +#include "common.h" + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + TRACE; + QDBusConnection bus = QDBusConnection::systemBus(); + QStringList serviceNames = bus.interface()->registeredServiceNames(); + + if (serviceNames.contains("com.hfdialer")) + { + QDBusMessage message = QDBusMessage::createMethodCall("com.hfdialer","/com/dialer","com.hfdialer", "raise"); + bus.call(message,QDBus::NoBlock); + + return 0; + } + + DialerApplication app(argc, argv); + + QMLMainWindow *qmw = QMLMainWindow::instance(); + + QString argString(argv[1]); + + if (argString != "noshow") + qmw->tryToShow(); + + return app.exec(); +} + +QString stripLineID(QString lineid) +{ + TRACE; + static QRegExp rx = QRegExp("([^0-9*#])"); + + if (lineid.indexOf('+') == 0) { + lineid.replace(rx, ""); + return lineid.insert(0,"+"); + } else { + return lineid.replace(rx, ""); + } +} + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/managerproxy.cpp b/src/managerproxy.cpp new file mode 100644 index 0000000..3f94a10 --- /dev/null +++ b/src/managerproxy.cpp @@ -0,0 +1,320 @@ +/* + * hfdialer - Hands Free Voice Call Manager + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#include "managerproxy.h" +#include "manager_interface.h" +#include "dialerapplication.h" +#include + +ManagerProxy *ManagerProxy::gManager = 0; + +ManagerProxy::ManagerProxy(const QString &service, + const QString &path, + const QDBusConnection &connection, + QObject *parent) + : org::ofono::Manager(service, path, connection, parent), + m_modemPath (""), + m_modem(0), + m_network(0), + m_callManager(0), + m_volumeManager(0), + m_voicemail(0) +{ + TRACE; + if (gManager) + qFatal("ManagerProxy: There can be only one!"); + + if (!isValid()) { + qDebug() << "Failed to connect to Ofono: \n\t" << lastError().message(); + } else { + QDBusPendingReply reply; + QDBusPendingCallWatcher * watcher; + + reply = GetModems(); + watcher = new QDBusPendingCallWatcher(reply); + + // Force this to be sync to ensure we have initial properties + watcher->waitForFinished(); + managerDBusGetModemsDone(watcher); + + connect(this, + SIGNAL(ModemAdded(const QDBusObjectPath&, const QVariantMap&)), + SLOT(modemAdded(const QDBusObjectPath&, const QVariantMap&))); + connect(this, + SIGNAL(ModemRemoved(const QDBusObjectPath&)), + SLOT(modemRemoved(const QDBusObjectPath&))); + + } + + gManager = this; + + if (m_modem && m_modem->isValid() && !m_modem->powered()) + m_modem->setPowered(true); +} + +ManagerProxy::~ManagerProxy() +{ + TRACE; + if (m_volumeManager) + delete m_volumeManager; + m_volumeManager = 0; + + if (m_voicemail) + delete m_voicemail; + m_voicemail = 0; + + if (m_callManager) + delete m_callManager; + m_callManager = 0; + + if (m_network) + delete m_network; + m_network = 0; + + if (m_modem) + delete m_modem; + m_modem = 0; + + gManager=0; +} + +void ManagerProxy::managerDBusGetModemsDone(QDBusPendingCallWatcher *call) +{ + QDBusPendingReply reply = *call; + TRACE; + if (reply.isError()) { + // TODO: Handle this properly, by setting states, or disabling features + qWarning() << "org.ofono.Manager.GetModems() failed: " << + reply.error().message(); + } else { + QArrayOfPathProperties modems = reply.value(); + if (modems.count() >= 1) { + // FIXME: Handle multiple modems... + foreach (OfonoPathProperties p, modems) + { + qDebug() << "modem: " << p.path.path(); + m_modemList << QString(p.path.path()); + } + + OfonoPathProperties p = modems[0]; + if (m_modemPath.isNull() || m_modemPath.isEmpty()) { + qDebug() << QString("\n======\nUsing modem: \"%1\"\n======").arg(p.path.path()); + m_modemPath = QString(p.path.path()); + setModem(m_modemPath); + setNetwork(m_modemPath); + setCallManager(m_modemPath); + setVolumeManager(m_modemPath); + setVoicemail(m_modemPath); + // TODO: Connect to service proxies as available/needed here + } + } + } +} + +void ManagerProxy::modemAdded(const QDBusObjectPath &in0,const QVariantMap &in1) +{ + Q_UNUSED(in1) + TRACE; + + // TODO: Handle modem additions, maybe... + qWarning() << QString("Unhandled ModemAdded event: \"%1\"") + .arg(in0.path()); + + qDebug() << QString("modem added: %1").arg(in0.path()); + m_modemList << QString(in0.path()); + m_modemList.removeDuplicates(); + + setModem(in0.path()); + setCallManager(m_modemPath); +} + +void ManagerProxy::modemRemoved(const QDBusObjectPath &in0) +{ + TRACE; + + // TODO: Handle modem removals, currently active for sure, others, maybe... + qWarning() << QString("Unhandled ModemRemoved event: \"%1\"") + .arg(in0.path()); + + qDebug() << QString("modem removed: ").arg(in0.path()); + m_modemList.removeOne(QString(in0.path())); +} + +ManagerProxy *ManagerProxy::instance() +{ + if (!gManager) + gManager = new ManagerProxy(); + + return gManager; +} + +ModemProxy* ManagerProxy::modem() const +{ + return m_modem; +} + +NetworkProxy* ManagerProxy::network() const +{ + return m_network; +} + +CallManager* ManagerProxy::callManager() const +{ + return m_callManager; +} + +VolumeManager* ManagerProxy::volumeManager() const +{ + return m_volumeManager; +} + +VoicemailProxy* ManagerProxy::voicemail() const +{ + return m_voicemail; +} + +QStringList ManagerProxy::getModemList() +{ + return m_modemList; +} + +void ManagerProxy::setModem(QString modemPath) +{ + if (m_modem && + m_modem->isValid() && + m_modem->path() == modemPath) + { + //If we have a modem, it's valid, but not powered, power it up. + if (!m_modem->powered()) + m_modem->setPowered(true); + + return; + } + + if (m_modem) + { + if (m_modemList.contains(m_modem->path())) + { + m_modemList.removeAll(m_modem->path()); + } + delete m_modem; + m_modem = NULL; + } + + if (m_modemList.contains(modemPath)) { + m_modem = new ModemProxy(modemPath); + emit modemChanged(); + } +} + +void ManagerProxy::setNetwork(QString modempath) +{ + if (!m_modem || !m_modem->isValid()) + return; + + if (modempath == m_modem->path()) { + if (m_network && m_network->isValid()) { + return; + } + else { + delete m_network; + m_network = new NetworkProxy(modempath); + emit networkChanged(); + } + } + else { + if(m_network || !m_network->isValid()) { + delete m_network; + m_network = new NetworkProxy(modempath); + emit networkChanged(); + } + } +} + +void ManagerProxy::setCallManager(QString modempath) +{ + TRACE + if (!m_modem || !m_modem->isValid()) + return; + + if (modempath == m_modem->path()) { + if (m_callManager && m_callManager->isValid()) { + return; + } + else { + delete m_callManager; + m_callManager = new CallManager(modempath); + emit callManagerChanged(); + } + } + else { + + if(m_callManager) + delete m_callManager; + + m_callManager = new CallManager(modempath); + connect(m_callManager, SIGNAL(connected()), this, SIGNAL(callManagerChanged())); + emit callManagerChanged(); + } +} + +void ManagerProxy::setVolumeManager(QString modempath) +{ + if (!m_modem || !m_modem->isValid()) + return; + + if (modempath == m_modem->path()) { + if (m_volumeManager && m_volumeManager->isValid()) { + return; + } + else { + delete m_volumeManager; + m_volumeManager = new VolumeManager(modempath); + emit volumeManagerChanged(); + } + } + else { + if(m_volumeManager || !m_volumeManager->isValid()) { + delete m_volumeManager; + m_volumeManager = new VolumeManager(modempath); + emit volumeManagerChanged(); + } + } +} + +void ManagerProxy::setVoicemail(QString modempath) +{ + if (!m_modem || !m_modem->isValid()) + return; + + if (modempath == m_modem->path()) { + if (m_voicemail && m_voicemail->isValid()) { + return; + } + else { + delete m_voicemail; + m_voicemail = new VoicemailProxy(modempath); + emit voicemailChanged(); + } + } + else { + if(m_voicemail || !m_voicemail->isValid()) { + delete m_voicemail; + m_voicemail = new VoicemailProxy(modempath); + emit voicemailChanged(); + } + } +} + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/managerproxy.h b/src/managerproxy.h new file mode 100644 index 0000000..f27ea6e --- /dev/null +++ b/src/managerproxy.h @@ -0,0 +1,89 @@ +/* + * hfdialer - Hands Free Voice Call Manager + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#ifndef MANAGERPROXY_H +#define MANAGERPROXY_H + +#include "manager_interface.h" +#include "modemproxy.h" +#include "networkproxy.h" +#include "callmanager.h" +#include "common.h" +#include +#include + +#define OFONO_SERVICE "org.ofono" +#define OFONO_MANAGER_PATH "/" + +class ManagerProxy: public org::ofono::Manager +{ + Q_OBJECT; + Q_PROPERTY(ModemProxy* modem READ modem); + Q_PROPERTY(NetworkProxy* network READ network); + +public: + virtual ~ManagerProxy(); + + static ManagerProxy *instance(); + QStringList getModemList(); + void setModem(QString modempath); + void setNetwork(QString modempath); + void setCallManager(QString modempath); + void setVolumeManager(QString modempath); + void setVoicemail(QString modempath); + + ModemProxy* modem() const; + NetworkProxy* network() const; + CallManager* callManager() const; + VolumeManager* volumeManager() const; + VoicemailProxy* voicemail() const; + +public slots: + void managerDBusGetModemsDone(QDBusPendingCallWatcher *call); + +Q_SIGNALS: + void modemChanged(); + void networkChanged(); + void callManagerChanged(); + void volumeManagerChanged(); + void voicemailChanged(); + +private Q_SLOTS: + void modemAdded(const QDBusObjectPath &in0, const QVariantMap &in1); + void modemRemoved(const QDBusObjectPath &in0); + +protected: + ManagerProxy(const QString &service=OFONO_SERVICE, + const QString &path=OFONO_MANAGER_PATH, + const QDBusConnection &connection=QDBusConnection::systemBus(), + QObject *parent = 0); + +private: + ManagerProxy(const ManagerProxy&); + ManagerProxy& operator= (ManagerProxy&); + + QString m_modemPath; + ModemProxy *m_modem; + NetworkProxy *m_network; + CallManager *m_callManager; + VolumeManager *m_volumeManager; + VoicemailProxy *m_voicemail; + QStringList m_modemList; + + static ManagerProxy *gManager; +}; + +#endif + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/modemproxy.cpp b/src/modemproxy.cpp new file mode 100644 index 0000000..b150463 --- /dev/null +++ b/src/modemproxy.cpp @@ -0,0 +1,374 @@ +/* + * hfdialer - Hands Free Voice Call Manager + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#include "modemproxy.h" +#include +#include "common.h" + +ModemProxy::ModemProxy(const QString &objectPath) + : org::ofono::Modem(OFONO_SERVICE, + objectPath, + QDBusConnection::systemBus()), + m_interfaces(0) +{ + TRACE; + if (!isValid()) { + qDebug() << "Failed to connect to Ofono: \n\t" << lastError().message(); + } else { + m_path = objectPath; + QDBusPendingReply reply; + QDBusPendingCallWatcher * watcher; + + reply = GetProperties(); + watcher = new QDBusPendingCallWatcher(reply); + + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(modemDBusGetPropDone(QDBusPendingCallWatcher*))); + + connect(this, + SIGNAL(PropertyChanged(const QString&,const QDBusVariant&)), + SLOT(propertyChanged(const QString&,const QDBusVariant&))); + } +} + +ModemProxy::~ModemProxy() +{ + TRACE; +} + +QStringList ModemProxy::interfaces() const { return m_interfaces; } +QString ModemProxy::path() const { return m_path; } +QString ModemProxy::manufacturer() const { return m_manufacturer; } +QString ModemProxy::model() const { return m_model; } +QString ModemProxy::revision() const { return m_revision; } +QString ModemProxy::serial() const { return m_serial; } +bool ModemProxy::powered() const { return m_powered; } +bool ModemProxy::online() const { return m_online; } + +void ModemProxy::setPowered(bool is_powered) +{ + TRACE; + if (m_powered == is_powered) + return; + + QVariant powered(is_powered); + + QDBusPendingReply reply; + reply = SetProperty("Powered", QDBusVariant(powered)); + + if (reply.isError()) + qCritical() << "SetProperty \"Powered\" failed!"; +} + +void ModemProxy::setOnline(bool is_online) +{ + TRACE; + if (m_online == is_online) + return; + + QDBusPendingReply reply; + reply = SetProperty("Online", QDBusVariant(m_online?"true":"false")); + if (reply.isError()) + qCritical() << "SetProperty \"Powered\" failed!"; + else + m_online = is_online; +} + +void ModemProxy::modemDBusGetPropDone(QDBusPendingCallWatcher *call) +{ + TRACE; + QDBusPendingReply reply = *call; + + if (reply.isError()) { + // TODO: Handle this properly, by setting states, or disabling features... + qDebug() << "org.ofono.ModemProxy.getProperties() failed: " << + reply.error().message(); + } else { + QVariantMap properties = reply.value(); + m_interfaces = qdbus_cast(properties["Interfaces"]); + m_manufacturer = qdbus_cast(properties["Manufacturer"]); + m_model = qdbus_cast(properties["Model"]); + m_powered = qdbus_cast(properties["Powered"]); + m_revision = qdbus_cast(properties["Revision"]); + m_serial = qdbus_cast(properties["Serial"]); + m_online = qdbus_cast(properties["Online"]); + + // First sucessfull GetProperties == connected + if (!m_connected) { + m_connected = true; + emit connected(); + } + } +} + +void ModemProxy::propertyChanged(const QString &in0, const QDBusVariant &in1) +{ + TRACE; + qDebug() << "org.ofono.ModemProxy.propertyChanged()" + << in0 << ": " << in1.variant(); + if (in0 == "Interfaces") { + m_interfaces = qdbus_cast(in1.variant()); + emit interfacesChanged(m_interfaces); + } + else if (in0 == "Powered") { + m_powered = qdbus_cast(in1.variant()); + emit poweredChanged(m_powered); + } else if (in0 == "Online") { + m_online = qdbus_cast(in1.variant()); + emit onlineChanged(m_online); + } else { + qDebug() << QString("Unhandled property \"%1\" changed...").arg(in0); + } +} + +/* + * VoicemailProxy (aka MessageWaiting) implementation + */ + +VoicemailProxy::VoicemailProxy(const QString &objectPath) + : org::ofono::MessageWaiting(OFONO_SERVICE, + objectPath, + QDBusConnection::systemBus()), + m_connected(false) +{ + TRACE; + if (!isValid()) { + qDebug() << "Failed to connect to Ofono: \n\t" << lastError().message(); + } else { + m_path = objectPath; + QDBusPendingReply reply; + QDBusPendingCallWatcher * watcher; + + reply = GetProperties(); + watcher = new QDBusPendingCallWatcher(reply); + + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(voicemailDBusGetPropDone(QDBusPendingCallWatcher*))); + connect(this, SIGNAL(PropertyChanged(const QString&, const QDBusVariant&)), + SLOT(voicemailPropertyChanged(const QString&, const QDBusVariant&))); + } +} + +VoicemailProxy::~VoicemailProxy() +{ + TRACE; +} + +bool VoicemailProxy::isConnected() const { return m_connected; } +QString VoicemailProxy::path() const { return m_path; } +QString VoicemailProxy::mailbox() const { return m_mailbox; } +int VoicemailProxy::count() const { return m_count; } +bool VoicemailProxy::waiting() const { return m_waiting; } + +void VoicemailProxy::setMailbox(QString lineid) +{ + TRACE; + if (lineid.isEmpty() || (m_mailbox == lineid)) + return; + + QDBusPendingReply<> reply; + reply = SetProperty("VoicemailMailboxNumber", QDBusVariant(lineid)); + reply.waitForFinished(); + + if (reply.isError()) + qCritical() << "SetProperty \"VoicemailMailboxNumber\" failed: " << + reply.error().message(); + else + m_mailbox = lineid; +} + +void VoicemailProxy::voicemailDBusGetPropDone(QDBusPendingCallWatcher *call) +{ + TRACE; + QDBusPendingReply reply = *call; + + if (reply.isError()) { + // TODO: Handle this properly, by setting states, or disabling features... + qDebug() << "org.ofono.MessageWaiting.getProperties() failed: " << + reply.error().message(); + } else { + QVariantMap properties = reply.value(); + bool waiting = qdbus_cast(properties["VoicemailWaiting"]); + int count = qdbus_cast(properties["VoicemailMessageCount"]); + QString mailbox = qdbus_cast(properties["VoicemailMailboxNumber"]); + + if (count != m_count) { + m_count = count; + emit messagesWaitingChanged(); + } + if (waiting != m_waiting) { + m_waiting = waiting; + emit messagesWaitingChanged(); + } + if (!mailbox.isEmpty() && (mailbox != m_mailbox)) { + m_mailbox = mailbox; + emit mailboxChanged(); + } + + // First sucessfull GetProperties == connected + if (!m_connected) { + m_connected = true; + emit connected(); + } + } +} + +void VoicemailProxy::voicemailPropertyChanged(const QString &in0, const QDBusVariant &in1) +{ + TRACE; + qDebug() << QString("Property \"%1\" changed...").arg(in0); + bool waiting; + int count; + QString mailbox; + if (in0 == "VoicemailWaiting") { + waiting = qdbus_cast(in1.variant()); + } else if (in0 == "VoicemailMessageCount") { + count = qdbus_cast(in1.variant()); + } else if (in0 == "VoicemailMailboxNumber") { + mailbox = qdbus_cast(in1.variant()); + } else + qDebug() << QString("Unexpected property changed..."); + + if ((count != m_count) || (waiting != m_waiting)) { + m_count = count; + m_waiting = waiting; + emit messagesWaitingChanged(); + } + if (!mailbox.isEmpty() && (mailbox != m_mailbox)) { + m_mailbox = mailbox; + emit mailboxChanged(); + } +} + +/* + * CallVolume Manager implementation + */ + +VolumeManager::VolumeManager(const QString &objectPath) + : org::ofono::CallVolume(OFONO_SERVICE, + objectPath, + QDBusConnection::systemBus()) +{ + TRACE; + if (!isValid()) { + qDebug() << "Failed to connect to Ofono: \n\t" << lastError().message(); + } else { + m_path = objectPath; + QDBusPendingReply reply; + QDBusPendingCallWatcher * watcher; + + reply = GetProperties(); + watcher = new QDBusPendingCallWatcher(reply); + + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(volumeDBusGetPropDone(QDBusPendingCallWatcher*))); + } +} + +VolumeManager::~VolumeManager() +{ + TRACE; +} + +QString VolumeManager::path() const { return m_path; } +int VolumeManager::speakerVolume() const { return m_speakerVolume; } +int VolumeManager::micVolume() const { return m_micVolume; } +bool VolumeManager::muted() const { return m_muted; } +bool VolumeManager::isConnected() const { return m_connected; } + +void VolumeManager::setSpeakerVolume(int volume) +{ + TRACE; + if (m_speakerVolume == volume) + return; + + if ((volume < 0) || (volume > 100)) { + qWarning() << "SpeakerVolume value out of range (0<>100)"; + return; + } + + QDBusPendingReply<> reply; + reply = SetProperty("SpeakerVolume", QDBusVariant(volume)); + reply.waitForFinished(); + + if (reply.isError()) + qCritical() << "SetProperty \"SpeakerVolume\" failed: " << + reply.error().message(); + else + m_speakerVolume = volume; +} + +void VolumeManager::setMicVolume(int volume) +{ + TRACE; + if (m_micVolume == volume) + return; + + if ((volume < 0) || (volume > 100)) { + qWarning() << "MicrophoneVolume value out of range (0<>100)"; + return; + } + + QDBusPendingReply<> reply; + reply = SetProperty("MicrophoneVolume", QDBusVariant(volume)); + reply.waitForFinished(); + + if (reply.isError()) + qCritical() << "SetProperty \"MicrophoneVolume\" failed: " << + reply.error().message(); + else + m_micVolume = volume; +} + +void VolumeManager::setMuted(bool is_muted) +{ + TRACE; + if (m_muted == is_muted) + return; + + QDBusPendingReply<> reply; + reply = SetProperty("Muted", QDBusVariant(is_muted)); + reply.waitForFinished(); + + if (reply.isError()) + qCritical() << "SetProperty \"Muted\" failed: " << + reply.error().message(); + else + m_muted = is_muted; +} + +void VolumeManager::volumeDBusGetPropDone(QDBusPendingCallWatcher *call) +{ + TRACE; + QDBusPendingReply reply = *call; + + if (reply.isError()) { + // TODO: Handle this properly, by setting states, or disabling features... + qDebug() << "org.ofono.CallVolume.getProperties() failed: " << + reply.error().message(); + } else { + QVariantMap properties = reply.value(); + m_speakerVolume = qdbus_cast(properties["SpeakerVolume"]); + m_micVolume = qdbus_cast(properties["MicrophoneVolume"]); + m_muted = qdbus_cast(properties["Muted"]); + + // First sucessfull GetProperties == connected + if (!m_connected) { + m_connected = true; + emit connected(); + } + } +} + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/modemproxy.h b/src/modemproxy.h new file mode 100644 index 0000000..d7c8e28 --- /dev/null +++ b/src/modemproxy.h @@ -0,0 +1,164 @@ +/* + * hfdialer - Hands Free Voice Call Manager + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#ifndef MODEMPROXY_H +#define MODEMPROXY_H + +#include "modem_interface.h" +#include +#include + +#define OFONO_SERVICE "org.ofono" +#define OFONO_MANAGER_PATH "/" + +class ModemProxy: public org::ofono::Modem +{ + Q_OBJECT; + Q_PROPERTY(QStringList interfaces READ interfaces); + Q_PROPERTY(QString path READ path); + Q_PROPERTY(QString manufacturer READ manufacturer); + Q_PROPERTY(QString model READ model); + Q_PROPERTY(QString revision READ revision); + Q_PROPERTY(QString serial READ serial); + Q_PROPERTY(bool powered READ powered WRITE setPowered); + Q_PROPERTY(bool online READ online WRITE setOnline); + +public: + ModemProxy(const QString &objectPath); + virtual ~ModemProxy(); + + QStringList interfaces() const; + QString path() const; + QString manufacturer() const; + QString model() const; + QString revision() const; + QString serial() const; + bool powered() const; + bool online() const; + +public slots: + void setPowered(bool is_powered); + void setOnline(bool is_online); + + void modemDBusGetPropDone(QDBusPendingCallWatcher *call); + +Q_SIGNALS: + void interfacesChanged(QStringList interfaces); + void poweredChanged(bool powered); + void onlineChanged(bool powered); + void connected(); + void disconnected(); + +private Q_SLOTS: + // Slots to handle DBus signals from ofono + void propertyChanged(const QString &in0, const QDBusVariant &in1); + +private: + QStringList m_properties; // raw return from GetProperties + QStringList m_interfaces; + QString m_path; + QString m_manufacturer; + QString m_model; + QString m_revision; + QString m_serial; + bool m_powered; + bool m_online; + bool m_connected; +}; + +class VoicemailProxy: public org::ofono::MessageWaiting +{ + Q_OBJECT; + + Q_PROPERTY(bool isConnected READ isConnected NOTIFY connected); + Q_PROPERTY(bool waiting READ waiting); + Q_PROPERTY(int count READ count); + Q_PROPERTY(QString mailbox READ mailbox WRITE setMailbox); + +public: + VoicemailProxy(const QString &objectPath); + virtual ~VoicemailProxy(); + + bool isConnected() const; + + QString path() const; + bool waiting() const; + int count() const; + QString mailbox() const; + +public slots: + void setMailbox(QString lineid); + +Q_SIGNALS: + void messagesWaitingChanged(); + void mailboxChanged(); + void connected(); + void disconnected(); + +private slots: + void voicemailDBusGetPropDone(QDBusPendingCallWatcher *call); + void voicemailPropertyChanged(const QString &in0, const QDBusVariant &in1); + +private: + QStringList m_properties; // raw return from GetProperties + QString m_path; + bool m_waiting; + int m_count; + QString m_mailbox; + bool m_connected; +}; + +class VolumeManager: public org::ofono::CallVolume +{ + Q_OBJECT; + Q_PROPERTY(int speakerVolume READ speakerVolume WRITE setSpeakerVolume); + Q_PROPERTY(int micVolume READ micVolume WRITE setMicVolume); + Q_PROPERTY(bool muted READ muted WRITE setMuted); + Q_PROPERTY(bool isConnected READ isConnected); + +public: + VolumeManager(const QString &objectPath); + virtual ~VolumeManager(); + + QString path() const; + int speakerVolume() const; + int micVolume() const; + bool muted() const; + bool isConnected() const; + +public slots: + void setSpeakerVolume(int volume); + void setMicVolume(int volume); + void setMuted(bool is_muted); + void volumeDBusGetPropDone(QDBusPendingCallWatcher *call); + +Q_SIGNALS: + void speakerVolumeChanged(int volume); + void micVolumeChanged(int volume); + void muteChanged(bool muted); + void connected(); + void disconnected(); + +private: + QStringList m_properties; // raw return from GetProperties + QString m_path; + int m_speakerVolume; + int m_micVolume; + bool m_muted; + bool m_connected; +}; + +#endif + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/networkproxy.cpp b/src/networkproxy.cpp new file mode 100644 index 0000000..59b042c --- /dev/null +++ b/src/networkproxy.cpp @@ -0,0 +1,143 @@ +/* + * hfdialer - Hands Free Voice Call Manager + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#include "networkproxy.h" +#include + +/* ************************************************************** + * Network Operator Class + * **************************************************************/ + +OperatorProxy::OperatorProxy(const QString &operatorPath) + : org::ofono::NetworkOperator(OFONO_SERVICE, + operatorPath, + QDBusConnection::systemBus()), + m_path(operatorPath), m_countryCode(""), + m_networkCode(""), m_name(""), m_status(""), m_technologies("") +{ + if (!isValid()) { + qCritical() << org::ofono::NetworkOperator::staticInterfaceName() << + " connection failed: " << lastError().message(); + } else { + QDBusPendingReply reply; + QDBusPendingCallWatcher * watcher; + + reply = GetProperties(); + watcher = new QDBusPendingCallWatcher(reply); + + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(operatorDBusGetPropDone(QDBusPendingCallWatcher*))); + } +} + +OperatorProxy::~OperatorProxy() +{ +} + +QString OperatorProxy::path() const { return m_path; } +QString OperatorProxy::countryCode() const { return m_countryCode; } +QString OperatorProxy::networkCode() const { return m_networkCode; } +QString OperatorProxy::name() const { return m_name; } +QString OperatorProxy::status() const { return m_status; } +QStringList OperatorProxy::technologies() const { return m_technologies; } + +void OperatorProxy::operatorDBusGetPropDone(QDBusPendingCallWatcher *call) +{ + QDBusPendingReply reply = *call; + + if (reply.isError()) { + // TODO: Handle this properly + qCritical() << org::ofono::NetworkOperator::staticInterfaceName() << + ".GetProperties() failed: " << reply.error().message(); + } else { + QVariantMap properties = reply.value(); + m_countryCode = qdbus_cast(properties["MobileCountryCode"]); + m_networkCode = qdbus_cast(properties["MobileNetworkCode"]); + m_name = qdbus_cast(properties["Name"]); + m_status = qdbus_cast(properties["Status"]); + m_technologies = qdbus_cast(properties["Technologies"]); + } +} + +/* ************************************************************** + * Network Registration Class + * **************************************************************/ +NetworkProxy::NetworkProxy(const QString &modemPath) + : org::ofono::NetworkRegistration(OFONO_SERVICE, + modemPath, + QDBusConnection::systemBus()), + m_mode(""), m_name(""), m_status(""), m_connected(false) +{ + if (!isValid()) { + qCritical() << org::ofono::NetworkRegistration::staticInterfaceName() << + " connection failed: " << lastError().message(); + } else { + QDBusPendingReply reply; + QDBusPendingCallWatcher * watcher; + + reply = GetProperties(); + watcher = new QDBusPendingCallWatcher(reply); + + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(networkDBusGetPropDone(QDBusPendingCallWatcher*))); + } +} + +NetworkProxy::~NetworkProxy() +{ +} + +QList NetworkProxy::operators() const { return m_operators; } +OperatorProxy* NetworkProxy::currentOperator() const +{ + return m_currentOperator; +} +QString NetworkProxy::mode() const { return m_mode; } +QString NetworkProxy::name() const { return m_name; } +QString NetworkProxy::status() const { return m_status; } + +void NetworkProxy::networkDBusGetPropDone(QDBusPendingCallWatcher *call) +{ + QDBusPendingReply reply = *call; + + if (reply.isError()) { + // TODO: Handle this properly + qCritical() << org::ofono::NetworkRegistration::staticInterfaceName() << + ".GetProperties() failed: " << reply.error().message(); + } else { + QVariantMap properties = reply.value(); + QList paths = + qdbus_cast >(properties["AvailableOperators"]); + + foreach (QDBusObjectPath p, paths) { + QString path = QString(p.path()); + OperatorProxy *op = new OperatorProxy(path); + + m_operatorPaths.append(path); + m_operators.append(op); + + } + m_mode = qdbus_cast(properties["Mode"]); + m_name = qdbus_cast(properties["Operator"]); + m_status = qdbus_cast(properties["Status"]); + + // First sucessfull GetProperties == connected + if (!m_connected) { + m_connected = true; + emit connected(); + } + } +} + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/networkproxy.h b/src/networkproxy.h new file mode 100644 index 0000000..186984d --- /dev/null +++ b/src/networkproxy.h @@ -0,0 +1,110 @@ +/* + * hfdialer - Hands Free Voice Call Manager + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#ifndef NETWORKPROXY_H +#define NETWORKPROXY_H + +#include "modem_interface.h" +#include "operator_interface.h" +#include +#include + +#define OFONO_SERVICE "org.ofono" +#define OFONO_MANAGER_PATH "/" + +/* ************************************************************** + * Network Operator Class + * **************************************************************/ +class OperatorProxy: public org::ofono::NetworkOperator +{ + Q_OBJECT; + Q_PROPERTY(QString path READ path); + Q_PROPERTY(QString countryCode READ countryCode); + Q_PROPERTY(QString networkCode READ networkCode); + Q_PROPERTY(QString name READ name); + Q_PROPERTY(QString status READ status); + Q_PROPERTY(QStringList technologies READ technologies); + +public: + OperatorProxy(const QString &objectPath); + virtual ~OperatorProxy(); + + QString path() const; + QString countryCode() const; + QString networkCode() const; + QString name() const; + QString status() const; + QStringList technologies() const; + +public slots: + void operatorDBusGetPropDone(QDBusPendingCallWatcher *call); + +Q_SIGNALS: + void propertyChanged(); + +private: + QStringList m_properties; // raw return from GetProperties + QString m_path; + QString m_countryCode; + QString m_networkCode; + QString m_name; + QString m_status; + QStringList m_technologies; +}; + +/* ************************************************************** + * Network Registration Class + * **************************************************************/ +class NetworkProxy: public org::ofono::NetworkRegistration +{ + Q_OBJECT; + Q_PROPERTY(QList operators READ operators); + Q_PROPERTY(OperatorProxy currentOperator READ currentOperator); + Q_PROPERTY(QString mode READ mode); + Q_PROPERTY(QString name READ name); + Q_PROPERTY(QString status READ status); + +public: + NetworkProxy(const QString &objectPath); + virtual ~NetworkProxy(); + + QList operators() const; + OperatorProxy* currentOperator() const; + QString mode() const; + QString name() const; + QString status() const; + +public slots: + void networkDBusGetPropDone(QDBusPendingCallWatcher *call); + +Q_SIGNALS: + void propertyChanged(); + void connected(); + void disconnected(); + +private: + OperatorProxy *m_currentOperator; + + QStringList m_properties; // raw return from GetProperties + QStringList m_operatorPaths; + QList m_operators; + QString m_mode; + QString m_name; + QString m_status; + bool m_connected; +}; + +#endif + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/pacontrol.cpp b/src/pacontrol.cpp new file mode 100644 index 0000000..60a158d --- /dev/null +++ b/src/pacontrol.cpp @@ -0,0 +1,710 @@ +/* + * hfdialer - Hands Free Voice Call Manager + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#include "pacontrol.h" +#include "managerproxy.h" +#include "callmanager.h" +#include +#include +#include +#include + +// Define our pulse audio loop and connection variables +static PAControl* paControl = new PAControl; +// Create a mainloop API and connection to the default server +static pa_glib_mainloop *pa_ml = NULL; + +static void pa_subscribed_events_cb(pa_context *c, enum pa_subscription_event_type t, uint32_t , void *); + +static void operation_callback(pa_context *c, int success, void *userdata) { + Q_UNUSED(c); + Q_UNUSED(userdata); + if (!success) { + qDebug() << QString("Operation Failed"); + paControl->setErrorMsg(QString("Operation Failed")); + } +} + +static void module_callback(pa_context *c, uint32_t index, void *userdata) { + Q_UNUSED(c); + Q_UNUSED(userdata); + if (index == PA_INVALID_INDEX) { + qDebug() << QString("Load module failed"); + paControl->setErrorMsg(QString("Load module failed")); + } +} + +static void pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int is_last, void *userdata) { + Q_UNUSED(c); + Q_UNUSED(userdata); + PADevice *source; + + if (is_last > 0) { + //end of the list + paControl->release(); + return; + } + + //qDebug() << "pa_sourcelist_cb() Source added: " << l->name; + source = new PADevice(); + source->index = l->index; + if(l->name != NULL) + source->name = l->name; + if(l->description != NULL) + source->description = l->description; + paControl->addSource(source); +} + +static void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int is_last, void *userdata) { + Q_UNUSED(c); + Q_UNUSED(userdata); + PADevice *sink; + + if (is_last > 0) { + //end of the list + paControl->release(); + return; + } + + sink = new PADevice(); + sink->index = l->index; + if(l->name != NULL) + sink->name = l->name; + if(l->description != NULL) + sink->description = l->description; + paControl->addSink(sink); +} + +static void pa_modulelist_cb(pa_context *c, const pa_module_info *i, int is_last, void *userdata) { + Q_UNUSED(c); + Q_UNUSED(userdata); + PAModule *module; + + if (is_last > 0) { + //end of the list + paControl->release(); + return; + } + + module = new PAModule(); + module->index = i->index; + if(i->name != NULL) + module->name = i->name; + if(i->argument != NULL) + module->argument = i->argument; + paControl->addModule(module); +} + +static void pa_state_cb(pa_context *c, void *) { + + pa_context_state_t state = pa_context_get_state(c); + if(state == PA_CONTEXT_READY) + { + paControl->setState(true); + pa_context_set_subscribe_callback(c, pa_subscribed_events_cb, NULL); + pa_operation *o; + if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t) + (PA_SUBSCRIPTION_MASK_MODULE| + PA_SUBSCRIPTION_MASK_SINK| + PA_SUBSCRIPTION_MASK_SOURCE| + PA_SUBSCRIPTION_MASK_SINK_INPUT| + PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT), NULL, NULL))) { + qWarning("pa_context_subscribe() failed"); + } + if(o) pa_operation_unref(o); + ///Get an initial list of sinks, sources and modules. + paControl->addRef(); + pa_context_get_sink_info_list(c, pa_sinklist_cb, NULL); + paControl->addRef(); + pa_context_get_source_info_list(c, pa_sourcelist_cb, NULL); + paControl->addRef(); + pa_context_get_module_info_list(c, pa_modulelist_cb, NULL); + } + else if(state == PA_CONTEXT_FAILED) + { + ///Pulseaudio crashed? + paControl->setState(false); + } + else + { + qDebug()<<"PA state: "<<(int) state; + } +} + +static void pa_subscribed_events_cb(pa_context *c, enum pa_subscription_event_type t, uint32_t , void *) +{ + switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) + { + case PA_SUBSCRIPTION_EVENT_SINK: + qDebug("PA_SUBSCRIPTION_EVENT_SINK event triggered"); + foreach(PADevice* dev, paControl->sinkList) + { + delete dev; + } + paControl->sinkList.clear(); + paControl->addRef(); + pa_context_get_sink_info_list(c, pa_sinklist_cb, NULL); + break; + case PA_SUBSCRIPTION_EVENT_SOURCE: + qDebug("PA_SUBSCRIPTION_EVENT_SOURCE event triggered"); + foreach(PADevice* dev, paControl->sourceList) + { + delete dev; + } + paControl->sourceList.clear(); + paControl->addRef(); + pa_context_get_source_info_list(c, pa_sourcelist_cb, NULL); + break; + case PA_SUBSCRIPTION_EVENT_MODULE: + qDebug("PA_SUBSCRIPTION_EVENT_MODULE event triggered"); + foreach(PAModule* dev, paControl->moduleList) + { + delete dev; + } + paControl->moduleList.clear(); + paControl->addRef(); + pa_context_get_module_info_list(c, pa_modulelist_cb, NULL); + break; + } +} + +PAControl::PAControl(QObject *parent) + :QObject(parent) { + paControl = this; + status = SUCCESS; + m_paState=false; + + paInit(); +} + +PAControl::~PAControl() +{ + paCleanup(); + + foreach (PADevice *source, sourceList) { + qDebug() << QString("delete source"); + delete source; + } + foreach (PADevice *sink, sinkList) { + qDebug() << QString("delete sink"); + delete sink; + } + foreach (PAModule *module, moduleList) { + qDebug() << QString("delete module"); + delete module; + } + + qDebug() << QString("~PAControl()"); + paControl = NULL; +} + +PAControl* PAControl::instance() +{ + return paControl; +} + +void PAControl::paInit() { + qDebug() << "paInit()"; + + // Create a mainloop API and connection to the default server + if(!pa_ml) + pa_ml = pa_glib_mainloop_new(NULL); + pa_ctx = pa_context_new( + pa_glib_mainloop_get_api(pa_ml), + "com.tizen.hfp"); + + // This function connects to the pulse server + if (pa_context_connect(pa_ctx, + NULL, + PA_CONTEXT_NOFAIL, NULL) < 0) + { + qCritical("PulseAudioService: pa_context_connect() failed"); + paCleanup(); + return; + } + + m_refCounter = 0; + m_connected = false; + m_audioRouted = false; + m_btSourceReady =false; + m_btSinkReady = false; + + pa_context_set_state_callback(pa_ctx, pa_state_cb, NULL); +} + +void PAControl::paCleanup() { + qDebug() << "paCleanup()"; + if(pa_ctx) + pa_context_disconnect(pa_ctx); + if(pa_ctx) + pa_context_unref(pa_ctx); +} + +void PAControl::setState(bool state) +{ + m_paState = state; + if(state == false) + { + emit paFailed(); + } +} + +void PAControl::addRef() +{ + m_refCounter++; +} + +void PAControl::release() +{ + m_refCounter--; + Q_ASSERT(m_refCounter >= 0); +} + +void PAControl::reconnect() { + qDebug() << "Pulseaudio: reconnect()"; + if(paControl) + delete paControl; + paControl = new PAControl(); +} + + + +PADevice* PAControl::findBluezSource() { + + if (sourceList.size() == 0) + { + addRef(); + pa_op = pa_context_get_source_info_list(pa_ctx, pa_sourcelist_cb, NULL); + if(pa_op) pa_operation_unref(pa_op); + + return NULL; + } + + foreach (PADevice *source, sourceList) { + QString name = source->name; + + if (name.startsWith(QString("bluez_source."), Qt::CaseSensitive)) { + qDebug() << QString(" Matched Bluez source: ") << name; + return source; + } + } + + qDebug() << QString("Bluez source: none found"); + return NULL; +} + +PADevice* PAControl::findBluezSink() { + + if (sinkList.size() == 0) + { + addRef(); + pa_op = pa_context_get_sink_info_list(pa_ctx, pa_sinklist_cb, NULL); + if(pa_op) pa_operation_unref(pa_op); + + return NULL; + } + + foreach (PADevice *sink, sinkList) { + QString name = sink->name; + + if (name.startsWith(QString("bluez_sink."), Qt::CaseSensitive)) { + qDebug() << QString(" Matched Bluez sink: ") << name; + return sink; + } + } + + qDebug() << QString("Bluez sink: none found"); + return NULL; +} + +PADevice* PAControl::findAlsaSource(QString alsasource) { + + if (sourceList.size() == 0) + { + addRef(); + pa_op = pa_context_get_source_info_list(pa_ctx, pa_sourcelist_cb, NULL); + if(pa_op) pa_operation_unref(pa_op); + + return NULL; + } + + foreach (PADevice *source, sourceList) { + qDebug() << QString("Alsa source: ") << source->name; + QString name = source->name; + + if (!alsasource.isNull() && !alsasource.isEmpty()) + { + // if alsa source name is provided + if (alsasource == name) + { + qDebug() << QString(" Matched Alsa source: ") << name; + return source; + } + } else if (name.startsWith(QString("alsa_input."), Qt::CaseSensitive) && + name.endsWith(QString("analog-stereo"), Qt::CaseSensitive) && + !name.contains(QString("timb"))) { + // this is default behavior, it will try to look up one + qDebug() << QString(" Matched Alsa source: ") << name; + return source; + } + } + + qDebug() << QString("Alsa source: none found"); + return NULL; +} + +PADevice* PAControl::findAlsaSink(QString alsasink) { + + if (sinkList.size() == 0) + { + addRef(); + pa_op = pa_context_get_sink_info_list(pa_ctx, pa_sinklist_cb, NULL); + if(pa_op) pa_operation_unref(pa_op); + + return NULL; + } + + foreach (PADevice *sink, sinkList) { + qDebug() << QString("Alsa sink: ") << sink->name; + QString name = sink->name; + + if (!alsasink.isNull() && !alsasink.isEmpty()) + { + // if alsa sink name is provided + if (alsasink == name) + { + qDebug() << QString(" Matched Alsa sink: ") << name; + return sink; + } + } else if (name.startsWith(QString("alsa_output."), Qt::CaseSensitive) && + name.endsWith(QString("analog-stereo"), Qt::CaseSensitive) && + !name.contains(QString("timb"))) { + // this is default behavior, it will try to look up one + qDebug() << QString(" Matched Alsa sink: ") << name; + return sink; + } + } + + qDebug() << QString("Alsa sink: none found"); + return NULL; +} + +PAModule* PAControl::findModule(QString name, QString pattern) { + + if (moduleList.size() == 0) + { + addRef(); + pa_op = pa_context_get_module_info_list(pa_ctx, pa_modulelist_cb, NULL); + if(pa_op) pa_operation_unref(pa_op); + + return NULL; + } + + foreach (PAModule *module, moduleList) { + if (module->name.contains(name) && module->argument.contains(pattern)) { + qDebug() << QString(" Matched module: ") << module->name; + qDebug() << QString(" argument: ") << module->argument; + return module; + } + } + + qDebug() << QString("Module: none found"); + return NULL; +} + +QList PAControl::getAllModules() +{ + if (moduleList.size() == 0) + { + addRef(); + pa_op = pa_context_get_module_info_list(pa_ctx, pa_modulelist_cb, NULL); + if(pa_op) pa_operation_unref(pa_op); + } + + return moduleList; +} + +void PAControl::addSource(PADevice* device) +{ + foreach(PADevice* dev, sourceList) + { + if(dev->name == device->name) + { + delete device; + return; /// already exists + } + } + + sourceList.append(device); + emit sourceAppeared(device); +} + +void PAControl::addSink(PADevice* device) +{ + foreach(PADevice* dev, sinkList) + { + if(dev->name == device->name) + { + delete device; + return; /// already exists + } + } + + sinkList.append(device); + emit sinkAppeared(device); +} + +void PAControl::addModule(PAModule *module) +{ + foreach(PAModule* dev, moduleList) + { + if(dev->name == module->name && dev->index == module->index) + { + delete module; + return; /// already exists + } + } + + moduleList.append(module); + emit moduleAppeared(module); +} + +void PAControl::routeSourceWithSink(PADevice *source, PADevice *sink) { + qDebug() << "Routing from " << source->name << " to " << sink->name; + + if (source != NULL && sink != NULL) { + QString arg = "source=\"" % source->name % "\" sink=\"" % sink->name % "\""; + + pa_op = pa_context_load_module(pa_ctx, "module-loopback", arg.toAscii().data(), module_callback, NULL); + if(pa_op) pa_operation_unref(pa_op); + + qDebug() << QString("load-module module-loopback ") << arg; + } +} + +void PAControl::toggleMuteSource(PADevice *source, bool isMute) { + + if (source != NULL) { + pa_op =pa_context_set_source_mute_by_name(pa_ctx, source->name.toAscii().data(), isMute, operation_callback, NULL); + if(pa_op) pa_operation_unref(pa_op); + + qDebug() << QString("set source mute ") << source->name << QString(" to ") << isMute; + } +} + +void PAControl::unloadModule(PAModule* module) { + + if (module != NULL && module->index >= 0) { + pa_op = pa_context_unload_module(pa_ctx, module->index, operation_callback, NULL); + if(pa_op) pa_operation_unref(pa_op); + qDebug() << QString("unload-module module-loopback ") << QString(module->name) << QString(" at index ") << module->index; + } +} + +PAStatus PAControl::getStatus() { + return this->status; +} + +void PAControl::setErrorMsg(QString msg) { + if (msg != NULL) + { + this->status = ERROR; + this->errorMsg = msg; + } +} + +QString PAControl::getErrorMsg() { + return this->errorMsg; +} + +void PAControl::onSourceAppeared(PADevice* device) { + + CallManager *cm = ManagerProxy::instance()->callManager(); + if (!cm || !cm->isValid()) + return; + + if(cm->callCount() == 0) + { + qDebug() << "no calls active, ignore"; + return; + } + + if(device->name.contains("bluez_source")) + { + m_btSourceReady = true; + } + + if(!m_audioRouted && m_btSourceReady && m_btSinkReady) + { + qDebug() << QString("Route microphone and speakers"); + routeAudio(); + } +} + +void PAControl::onSinkAppeared(PADevice* device) { + CallManager *cm = ManagerProxy::instance()->callManager(); + if (!cm || !cm->isValid()) + return; + + if(cm->callCount() == 0) + { + qDebug() << "no calls active, ignore"; + return; + } + + if((device)->name.contains("bluez_sink")) + { + m_btSinkReady = true; + } + + if(!m_audioRouted && m_btSourceReady && m_btSinkReady) + { + qDebug() << QString("Route microphone and speakers"); + routeAudio(); + } +} + +void PAControl::routeAudio() +{ + PADevice* source; + PADevice* sink; + PADevice* mic; + PADevice* speaker; + + if (m_audioRouted) + { + qDebug() << QString("Audio already routed"); + return; + } + + if (m_refCounter > 0) + { + qDebug() << "PA callback not finished, retry"; + QTimer::singleShot(1000, this, SLOT(routeAudio())); + return; + } + + qDebug() << QString("Route audio"); + source = paControl->findBluezSource(); + sink = paControl->findBluezSink(); + + if(source == NULL || sink == NULL) { + qDebug() << QString("Bluez source or speaker not found"); + return; + } + + QString alsaSourceName = MGConfItem(QString("/apps/dialer/alsasource")).value().toString(); + QString alsaSinkName = MGConfItem(QString("/apps/dialer/alsasink")).value().toString(); + + mic = paControl->findAlsaSource(alsaSourceName); + speaker = paControl->findAlsaSink(alsaSinkName); + + if (mic != NULL and speaker != NULL) + { + paControl->routeSourceWithSink(source, speaker); + paControl->routeSourceWithSink(mic, sink); + qDebug() << QString("Create loopback modules successful"); + } + else { + qDebug() << QString("Alsa source and speaker not found"); + } + + m_audioRouted = true; + disconnect(this, SIGNAL(sourceAppeared(PADevice*))); + disconnect(this, SIGNAL(sinkAppeared(PADevice*))); +} + +void PAControl::unrouteAudio() +{ + qDebug() << QString("Unroute audio"); + PAControl* paControl = PAControl::instance(); + + QList mlist = paControl->getAllModules(); + foreach(PAModule *module, mlist) + { + if (module->name.contains("module-loopback") && + module->argument.contains("bluez") && + module->argument.contains("alsa")) { + qDebug() << QString("Found loopback module, index: ") << module->index; + paControl->unloadModule(module); + qDebug() << QString("Remove loopback module successful"); + } + } + + m_audioRouted = false; + m_btSourceReady = false; + m_btSinkReady = false; +} + +void PAControl::onCallsChanged() +{ + TRACE; + + CallManager *cm = ManagerProxy::instance()->callManager(); + if (!cm || !cm->isValid()) + { + qDebug("no call manager. Aborting"); + return; + } + + if (cm->dialingCall() || cm->activeCall() || cm->callCount() > 1) + { + // new call is dialing or phone is picked up + qDebug() << "PAControl: new call in progress"; + + if(m_audioRouted) + { + qDebug() << QString("Audio already routed"); + return; + } + + if(m_btSourceReady && m_btSinkReady) + { + qDebug() << QString("Route microphone and speakers"); + routeAudio(); + } + else + { + if(this->findBluezSource() != NULL && this->findBluezSink() != NULL) + { + // bt source and sink exists + m_btSourceReady = true; + m_btSinkReady = true; + qDebug() << QString("Route microphone and speakers"); + routeAudio(); + } + else + { + //no bt source or sink yet, let's wait until source and sink appears + m_btSourceReady = false; + m_btSinkReady = false; + connect(this, SIGNAL(sourceAppeared(PADevice*)), this, SLOT(onSourceAppeared(PADevice*))); + connect(this, SIGNAL(sinkAppeared(PADevice*)), this, SLOT(onSinkAppeared(PADevice*))); + qDebug() << QString("Audio not routed yet, wait for bt source and sinks"); + } + } + } + else if (cm->callCount() <= 0) + { + qDebug() << QString("no more ofono calls"); + if(m_audioRouted) + { + qDebug() << QString("Unroute microphone and speakers"); + unrouteAudio(); + } + } +} + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ + diff --git a/src/pacontrol.h b/src/pacontrol.h new file mode 100644 index 0000000..1ae7d18 --- /dev/null +++ b/src/pacontrol.h @@ -0,0 +1,113 @@ +/* + * hfdialer - Hands Free Voice Call Manager + * Copyright (c) 2012, Intel Corporation. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#ifndef PACONTROL_H +#define PACONTROL_H +#include +#include +#include +#include + +class PADevice { +public: + QString name; + int index; + QString description; +}; + +class PAModule { +public: + QString name; + int index; + QString argument; +}; + +enum PAStatus { + SUCCESS = 0, + ERROR = 1, +}; + +class PAControl : public QObject +{ + Q_OBJECT; +public: + PAControl(QObject *parent = 0); + ~PAControl(); + + static PAControl* instance(); + + void reconnect(); + PADevice* findBluezSource(); + PADevice* findBluezSink(); + PADevice* findAlsaSource(QString alsasource); + PADevice* findAlsaSink(QString alsasink); + PAModule* findModule(QString name, QString pattern); + QList getAllModules(); + void routeSourceWithSink(PADevice *source, PADevice *sink); + + void unloadModule(PAModule* module); + void toggleMuteSource(PADevice *source, bool isMute); + + PAStatus getStatus(); + void setErrorMsg(QString msg); + QString getErrorMsg(); + + void setState(bool state); + void addRef(); + void release(); + + void addSource(PADevice* device); + void addSink(PADevice* device); + void addModule(PAModule* module); + + QList sourceList; + QList sinkList; + QList moduleList; + +public Q_SLOTS: + void routeAudio(); + void unrouteAudio(); + +Q_SIGNALS: + void sourceAppeared(PADevice* device); + void sinkAppeared(PADevice* device); + void moduleAppeared(PAModule* device); + void paFailed(); + +private Q_SLOTS: + void onSourceAppeared(PADevice* device); + void onSinkAppeared(PADevice* device); + void onCallsChanged(); + +private: + pa_operation *pa_op; + pa_context *pa_ctx; + PAStatus status; + QString errorMsg; + + void paInit(); + void paCleanup(); + + + bool m_paState; + int m_refCounter; + bool m_connected; + bool m_audioRouted; + bool m_btSourceReady; + bool m_btSinkReady; +}; + +#endif // PACONTROL_H + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/qmlcallitem.cpp b/src/qmlcallitem.cpp new file mode 100644 index 0000000..67355e9 --- /dev/null +++ b/src/qmlcallitem.cpp @@ -0,0 +1,139 @@ +/* + * dialer - Declarative Dialer UX Main Window. + * Copyright (c) 2011, Tom Swindell. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#include "common.h" +#include "qmlcallitem.h" + +class QMLCallItemPrivate +{ +public: + CallItem *proxy; +}; + +QMLCallItem::QMLCallItem(CallItem *proxy, QObject *parent) + : QObject(parent), d(new QMLCallItemPrivate) +{ + TRACE; + d->proxy = proxy; + + QObject::connect(proxy->callProxy(), SIGNAL(stateChanged()), SLOT(onStateChanged())); +} + +QMLCallItem::~QMLCallItem() +{ + TRACE; + delete this->d; +} + +CallItem* QMLCallItem::proxy() const +{ + TRACE; + return d->proxy; +} + +QString QMLCallItem::msisdn() const +{ + TRACE; + if (d->proxy->callProxy()) + return d->proxy->callProxy()->lineID(); + return QString(); +} + +int QMLCallItem::numberLen() const +{ + TRACE; + QString number = msisdn(); + return number.size(); +} + +QString QMLCallItem::name() const +{ + TRACE; + if (d->proxy->callProxy()) + return d->proxy->callProxy()->name(); + return QString(); +} + +QString QMLCallItem::state() const +{ + TRACE; + if (d->proxy->callProxy()) + return d->proxy->callProxy()->state(); + return QString(); +} + +QString QMLCallItem::reason() const +{ + TRACE; + if (d->proxy->callProxy()) + return d->proxy->callProxy()->reason(); + return QString(); +} + +QDateTime QMLCallItem::startedAt() const +{ + TRACE; + if (d->proxy->callProxy()) + return d->proxy->callProxy()->startTime(); + return QDateTime(); +} + +int QMLCallItem::duration() const +{ + TRACE; + + if (d->proxy) + { + if (d->proxy->callProxy()) + return d->proxy->callProxy()->duration(); + } + return 0; +} + +bool QMLCallItem::isMultiparty() const +{ + TRACE; + if (d->proxy->callProxy()) + return d->proxy->callProxy()->multiparty(); + return false; +} + +void QMLCallItem::answer() +{ + TRACE; + if (d->proxy->callProxy()) + d->proxy->callProxy()->answer(); +} + +void QMLCallItem::deflect(const QString &msisdn) +{ + TRACE; + if (d->proxy->callProxy()) + d->proxy->callProxy()->deflect(msisdn); +} + +void QMLCallItem::hangup() +{ + TRACE; + if (d->proxy->callProxy()) + d->proxy->callProxy()->hangup(); +} + +void QMLCallItem::onStateChanged() +{ + TRACE; + emit this->stateChanged(d->proxy->callProxy()->state()); +} + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/qmlcallitem.h b/src/qmlcallitem.h new file mode 100644 index 0000000..7ccea73 --- /dev/null +++ b/src/qmlcallitem.h @@ -0,0 +1,72 @@ +/* + * dialer - CallItem Declarative Proxy Implementation. + * Copyright (c) 2011, Tom Swindell. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#ifndef QMLCALLITEM_H +#define QMLCALLITEM_H + +#include "callitem.h" + +#include +#include + +class QMLCallItem : public QObject +{ + Q_OBJECT; + + Q_PROPERTY(QString msisdn READ msisdn); + Q_PROPERTY(QString name READ name); + Q_PROPERTY(QString state READ state NOTIFY stateChanged); + Q_PROPERTY(QString reason READ reason); + Q_PROPERTY(int numberLen READ numberLen); + Q_PROPERTY(QDateTime startedAt READ startedAt); + Q_PROPERTY(int duration READ duration); + Q_PROPERTY(bool isMultiparty READ isMultiparty); + +public: + explicit QMLCallItem(CallItem *proxy, QObject *parent = 0); + ~QMLCallItem(); + + QString msisdn () const; + int numberLen () const; + QString name () const; + QString state () const; + + QDateTime startedAt () const; + int duration () const; + + QString reason () const; + bool isMultiparty () const; + + CallItem* proxy () const; + +Q_SIGNALS: + void stateChanged (const QString &state); + +public Q_SLOTS: + void answer (); + void deflect (const QString &msisdn); + void hangup (); + +protected Q_SLOTS: + void onStateChanged (); + +private: + class QMLCallItemPrivate *d; + + Q_DISABLE_COPY(QMLCallItem); +}; + +#endif // QMLCALLITEM_H + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/qmldialer.cpp b/src/qmldialer.cpp new file mode 100644 index 0000000..84d117c --- /dev/null +++ b/src/qmldialer.cpp @@ -0,0 +1,285 @@ +/* + * dialer - Declarative Dialer UX Main Window. + * Copyright (c) 2011, Tom Swindell. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#include "common.h" +#include "qmldialer.h" +#include "dialerapplication.h" +#include "dialercontext.h" +#include "callmanager.h" +#include "managerproxy.h" + +#define CONFIG_KEY_VOICEMAIL_NUMBER "/apps/dialer/voicemail/number" + +class QMLDialerPrivate +{ +public: + QMLDialerPrivate() + : currentCall(NULL) + { TRACE; } + + QMLCallItem *currentCall; +}; + +QMLDialer::QMLDialer(QObject *parent) + : QObject(parent), d(new QMLDialerPrivate) +{ + TRACE; + CallManager *cm = ManagerProxy::instance()->callManager(); + + this->connectAll(); + connect(ManagerProxy::instance(), SIGNAL(callManagerChanged()), SLOT(onCallManagerChanged())); + connect(cm, SIGNAL(callCountChanged()), SIGNAL(callCountChanged(cm->callCount()))); + + if(cm && cm->activeCall()) d->currentCall = new QMLCallItem(cm->activeCall(), this); +} + +QMLDialer::~QMLDialer() +{ + TRACE; + delete this->d; +} + +QString QMLDialer::mailbox() const +{ + TRACE; + + if(ManagerProxy::instance()->voicemail()->isConnected()) + { + return ManagerProxy::instance()->voicemail()->mailbox(); + } + + // Fall back to GConf voicemail setting. + return MGConfItem(CONFIG_KEY_VOICEMAIL_NUMBER).value().toString(); +} + +bool QMLDialer::modemOnline() +{ + if(ManagerProxy::instance()->modem() && + ManagerProxy::instance()->modem()->isValid()) + { + return ManagerProxy::instance()->modem()->powered(); + } + + return false; +} + +void QMLDialer::setMailbox(const QString &number) +{ + TRACE; + + if(ManagerProxy::instance()->voicemail()->isConnected()) + { + ManagerProxy::instance()->voicemail()->setMailbox(number); + return; + } + + // Fall back to GConf voicemail setting. + MGConfItem(CONFIG_KEY_VOICEMAIL_NUMBER).set(number); +} + +void QMLDialer::setModemOnline(bool online) +{ + if (ManagerProxy::instance() && ManagerProxy::instance()->modem()) + { + if(ManagerProxy::instance()->modem()->isValid()) + { + ManagerProxy::instance()->modem()->setPowered(online); + return; + } + } +} + +QString QMLDialer::speedDial(int index) const +{ + TRACE; + return MGConfItem(QString("/apps/dialer/speeddial/%1").arg(index)).value().toString(); +} + +void QMLDialer::setSpeedDial(int index, const QString &number) +{ + TRACE; + MGConfItem(QString("/apps/dialer/speeddial/%1").arg(index)).set(number); +} + +QMLCallItem* QMLDialer::currentCall() const +{ + TRACE; + return d->currentCall; +} + +int QMLDialer::callCount() +{ + TRACE; + CallManager *cm = ManagerProxy::instance()->callManager(); + return cm->callCount(); +} + + +void QMLDialer::dial(const QString &msisdn) +{ + TRACE; + CallManager *cm = ManagerProxy::instance()->callManager(); + if (cm && cm->isValid()) + { + cm->dial(msisdn); + } + +} + +void QMLDialer::hangupAll() +{ + TRACE; + CallManager *cm = ManagerProxy::instance()->callManager(); + if ((cm && cm->isValid())) + { + cm->hangupAll(); + } +} + +void QMLDialer::silenceRingtone() +{ + if(ManagerProxy::instance()->volumeManager()->isConnected()) + { + qDebug() << "Attempting to mute call with volume manager."; + ManagerProxy::instance()->volumeManager()->setMuted(true); + } + else if(d->currentCall) + { + qDebug() << "Attempting to mute call with call item signaller."; + d->currentCall->proxy()->silenceRingtone(); + } + else + { + qDebug() << "Couldn't decide how to mute ringtone!"; + } +} + +void QMLDialer::sendTones(const QString &tones) +{ + TRACE; + CallManager *cm = ManagerProxy::instance()->callManager(); + if (cm && cm->isValid()) + cm->sendTones(tones); +} + +void QMLDialer::transfer() +{ + TRACE; + CallManager *cm = ManagerProxy::instance()->callManager(); + if (cm && cm->isValid()) + cm->transfer(); +} + +void QMLDialer::swapCalls() +{ + TRACE; + CallManager *cm = ManagerProxy::instance()->callManager(); + if (cm && cm->isValid()) + cm->swapCalls(); +} + +void QMLDialer::releaseAndAnswer() +{ + TRACE; + CallManager *cm = ManagerProxy::instance()->callManager(); + if (cm && cm->isValid()) + cm->releaseAndAnswer(); +} + +void QMLDialer::holdAndAnswer() +{ + TRACE; + CallManager *cm = ManagerProxy::instance()->callManager(); + if (cm && cm->isValid()) + cm->holdAndAnswer(); +} + +void QMLDialer::createMultiparty() +{ +} + +void QMLDialer::hangupMultiparty() +{ +} + +void QMLDialer::privateChat(const QMLCallItem &call) +{ + TRACE; + CallManager *cm = ManagerProxy::instance()->callManager(); + if (cm && cm->isValid()) + cm->privateChat(*call.proxy()); +} + +void QMLDialer::onCallsChanged() +{ + TRACE; + CallManager *cm = ManagerProxy::instance()->callManager(); + + if(cm->activeCall()) + { + this->onIncomingCall(cm->activeCall()); + } + else + { + delete d->currentCall; + d->currentCall = NULL; + emit callCountChanged(cm->callCount()); + } + +} + +void QMLDialer::onIncomingCall(CallItem *callitem) +{ + TRACE; + + d->currentCall = new QMLCallItem(callitem, this); + emit this->incomingCall(); +} + +void QMLDialer::connectAll() +{ + TRACE; + CallManager *cm = ManagerProxy::instance()->callManager(); + + if (ManagerProxy::instance()->modem() && ManagerProxy::instance()->modem()->isValid()) + { + connect(ManagerProxy::instance()->modem(), SIGNAL(poweredChanged(bool)), this, SIGNAL(modemOnlineChanged(bool))); + } + + if (cm && cm->isValid()) + { + disconnect(cm, SIGNAL(callsChanged())); + disconnect(cm, SIGNAL(incomingCall(CallItem*))); + connect(cm, SIGNAL(callsChanged()), this, SLOT(onCallsChanged())); + connect(cm, SIGNAL(incomingCall(CallItem*)), SLOT(onIncomingCall(CallItem*))); + qDebug() << QString("CallMgr is connected"); + } + else if (cm) + { + disconnect(cm, SIGNAL(connected())); + connect(cm, SIGNAL(connected()), this, SLOT(connectAll())); + qDebug() << QString("CallMgr is not yet valid"); + } +} + +void QMLDialer::onCallManagerChanged() +{ + TRACE; + + CallManager *cm = ManagerProxy::instance()->callManager(); + this->connectAll(); + if(cm && cm->activeCall()) d->currentCall = new QMLCallItem(cm->activeCall(), this); +} + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/qmldialer.h b/src/qmldialer.h new file mode 100644 index 0000000..c7a93fa --- /dev/null +++ b/src/qmldialer.h @@ -0,0 +1,75 @@ +/* + * dialer - Declarative Dialer Adapter. + * Copyright (c) 2011, Tom Swindell. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#ifndef QMLDIALER_H +#define QMLDIALER_H + +#include + +#include "qmlcallitem.h" + +class QMLDialer : public QObject +{ + Q_OBJECT; + + Q_PROPERTY(QString mailbox READ mailbox WRITE setMailbox); + Q_PROPERTY(QMLCallItem* currentCall READ currentCall); + Q_PROPERTY(bool modemOnline READ modemOnline WRITE setModemOnline NOTIFY modemOnlineChanged); + Q_PROPERTY(int callCount READ callCount NOTIFY callCountChanged); + +public: + explicit QMLDialer(QObject *parent = 0); + ~QMLDialer(); + + QString mailbox () const; + QMLCallItem* currentCall () const; + bool modemOnline (); + int callCount (); + +Q_SIGNALS: + void incomingCall(); + void outgoingCall(); + void callCountChanged(int callCount); + void modemOnlineChanged(bool powered); + +public Q_SLOTS: + void setMailbox(const QString &number); + void setModemOnline(bool online); + QString speedDial(int index) const; + void setSpeedDial(int index, const QString &number); + void dial(const QString &msisdn); + void hangupAll(); + void silenceRingtone(); + void sendTones(const QString &tones); + void transfer(); + void swapCalls(); + void releaseAndAnswer(); + void holdAndAnswer(); + void createMultiparty(); + void hangupMultiparty(); + void privateChat(const QMLCallItem &callitem); + +protected Q_SLOTS: + void connectAll(); + void onCallsChanged(); + void onIncomingCall(CallItem *callitem); + void onCallManagerChanged(); + +private: + class QMLDialerPrivate *d; +}; + +#endif // QMLDIALER_H + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/qmlmainwindow.cpp b/src/qmlmainwindow.cpp new file mode 100644 index 0000000..fbdf401 --- /dev/null +++ b/src/qmlmainwindow.cpp @@ -0,0 +1,164 @@ +/* + * dialer - Declarative Dialer UX Main Window. + * Copyright (c) 2011, Tom Swindell. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#include "common.h" +#include "dialerapplication.h" + +#include "qmlcallitem.h" +#include "qmldialer.h" + +#include "qmlmainwindow.h" + +#include + +#define CONFIG_KEY_QML_LOAD_URL "/apps/dialer/qml/url" + +#define DEFAULT_QML_LOAD_URL "file:///usr/share/hfdialer/qml/main.qml" + +class QMLMainWindowPrivate +{ +public: + QMLMainWindowPrivate() + : adapter(NULL), + engine(NULL), + component(NULL), + item(NULL) + { TRACE; } + + QMLDialer *adapter; + QDeclarativeEngine *engine; + QDeclarativeView *qdv; + + QDeclarativeComponent *component; + QDeclarativeItem *item; +}; + +static void registerDataTypes() +{ + TRACE; + qmlRegisterType("com.hfdialer", 1, 0, "Dialer"); + + qmlRegisterUncreatableType("com.hfdialer", 1, 0, "CallItem", ""); +} + +QMLMainWindow::QMLMainWindow(QWidget *parent) + : QDeclarativeView(parent), + d(new QMLMainWindowPrivate) +{ + TRACE; + DialerApplication *da = DialerApplication::instance(); + CallManager *cm = ManagerProxy::instance()->callManager(); + + setResizeMode(QDeclarativeView::SizeRootObjectToView); + + this->setWindowTitle(qtTrId("xx_window")); + + this->setupUi(); + + connect(this->engine(), SIGNAL(quit()), this, SLOT(closeEvent())); +} + +QMLMainWindow::~QMLMainWindow() +{ + TRACE; + delete this->d; +} + +QMLMainWindow* QMLMainWindow::instance() +{ + TRACE; + static QMLMainWindow *_instance = NULL; + + if(_instance == NULL) + { + registerDataTypes(); + _instance = new QMLMainWindow; + } + + return _instance; +} + + +QMLMainWindow* QMLMainWindow::instanceP(QWidget* parent) +{ + TRACE; + static QMLMainWindow *_instance = NULL; + + if(_instance == NULL) + { + registerDataTypes(); + _instance = new QMLMainWindow(parent); + } + + return _instance; +} + +void QMLMainWindow::setupUi() +{ + TRACE; + MGConfItem qmlUrl(CONFIG_KEY_QML_LOAD_URL); + + d->engine = new QDeclarativeEngine(this); + + d->engine->rootContext()->setContextProperty("controller", this); //TODO: Remove + this->setSource(QUrl::fromLocalFile("/usr/share/hfdialer/qml/main.qml")); + d->component = new QDeclarativeComponent(d->engine, this); + d->component->loadUrl(qmlUrl.value(DEFAULT_QML_LOAD_URL).toString()); + + if(d->component->isError()) + { + qCritical() << "Failed to load QML Component:" << d->component->errorString(); + return; + } + + d->item = qobject_cast(d->component->create()); + if(!d->item) + { + qCritical() << "Failed to create item from component!"; + return; + } +} + +void QMLMainWindow::tryToShow() +{ + TRACE; + + if (d->component->isReady()) + { + DialerApplication *da = DialerApplication::instance(); + da->setActiveWindow(this); + da->activeWindow()->show(); + da->activeWindow()->activateWindow(); + this->show(); + } + +} +void QMLMainWindow::closeEvent() +{ + TRACE; + emit closeWindow(); +} + +void QMLMainWindow::onGeometryChanged() +{ + TRACE; +} + +void QMLMainWindow::setAdapter(QMLDialer *adapter) +{ + TRACE; + d->adapter = adapter; +} + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/qmlmainwindow.h b/src/qmlmainwindow.h new file mode 100644 index 0000000..62a07b8 --- /dev/null +++ b/src/qmlmainwindow.h @@ -0,0 +1,56 @@ +/* + * dialer - Declarative Dialer UX Main Window. + * Copyright (c) 2011, Tom Swindell. + * + * This program is licensed under the terms and conditions of the + * Apache License, version 2.0. The full text of the Apache License is at + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + +#ifndef QMLMAINWINDOW_H +#define QMLMAINWINDOW_H + +#include +#include +#include "qmldialer.h" +#include "callitem.h" + +class QMLMainWindowPrivate; + +class QMLMainWindow : public QDeclarativeView +{ + Q_OBJECT; + +public: + static QMLMainWindow* instance(); + static QMLMainWindow* instanceP(QWidget* parent); + ~QMLMainWindow(); + +Q_SIGNALS: + void closeWindow(); + +public Q_SLOTS: + void tryToShow(); + void closeEvent(); + void setAdapter(QMLDialer *adapter); //TODO: Refactor out + +protected Q_SLOTS: + void setupUi(); + + void onGeometryChanged(); + +private: + explicit QMLMainWindow(QWidget *parent = 0); + QMLMainWindowPrivate *d; + QDeclarativeView qv; + Q_DISABLE_COPY(QMLMainWindow) +}; + +#endif // QMLMAINWINDOW_H + +/* Local Variables: */ +/* mode:c++ */ +/* c-basic-offset:4 */ +/* indent-tabs-mode: nil */ +/* End: */ diff --git a/src/src.pro b/src/src.pro new file mode 100644 index 0000000..23d6e89 --- /dev/null +++ b/src/src.pro @@ -0,0 +1,90 @@ +TARGET = dialer +TEMPLATE = app +QT += dbus declarative +CONFIG += qdbus mobility qt-mobility link_pkgconfig network +PKGCONFIG += libpulse-mainloop-glib +MOBILITY += contacts multimedia +MOC_DIR = .moc +OBJECTS_DIR = .obj +MGEN_OUTDIR = .gen + +DEFINES += DBUS_SERVICE_PATH=\\\"/com/${QMAKE_TARGET}\\\" +DEFINES += DBUS_SERVICE=\\\"com.${QMAKE_TARGET}\\\" + +verbose { + DEFINES += VERBOSE +} + +wayland { + PKGCONFIG += mlite-wayland ofono-qt-wayland +} +xlib{ + PKGCONFIG += mlite-xlib ofono-qt-xlib +} +!xlib:!wayland{ + PKGCONFIG += mlite ofono-qt +} + +target.path += $$INSTALL_ROOT/usr/bin + +SOURCES += main.cpp \ + dialercontext.cpp \ + dialerapplication.cpp \ + managerproxy.cpp \ + modemproxy.cpp \ + networkproxy.cpp \ + callitem.cpp \ + callproxy.cpp \ + callmanager.cpp \ + dbustypes.cpp \ + pacontrol.cpp \ + qmlmainwindow.cpp \ + qmldialer.cpp \ + qmlcallitem.cpp \ + dbusdialeradapter.cpp \ + voicecall_interface.cpp \ + operator_interface.cpp \ + hfdialer_adaptor.cpp \ + manager_interface.cpp \ + modem_interface.cpp + +HEADERS += \ + common.h \ + dialercontext.h \ + dialerapplication.h \ + managerproxy.h \ + modemproxy.h \ + networkproxy.h \ + callitem.h \ + callproxy.h \ + callmanager.h \ + dbustypes.h \ + pacontrol.h \ + $$MODEL_HEADERS \ + $$STYLE_HEADERS \ + voicecall_interface.h \ + operator_interface.h \ + hfdialer_adaptor.h \ + manager_interface.h \ + modem_interface.h \ + qmlmainwindow.h \ + qmldialer.h \ + qmlcallitem.h \ + dbusdialeradapter.h + + + +system(qdbusxml2cpp -p voicecall_interface dbus/org.ofono.voicecall.xml) +system(qdbusxml2cpp -p operator_interface dbus/org.ofono.operator.xml) +system(qdbusxml2cpp -a hfdialer_adaptor dbus/com.hfdialer.xml) +system(qdbusxml2cpp -i dbustypes.h -p manager_interface dbus/org.ofono.manager.xml) +system(qdbusxml2cpp -i dbustypes.h -p modem_interface dbus/org.ofono.modem.xml) + +MAKE_CLEAN += $$OBJECTS_DIR/*.o +MAKE_DISTCLEAN += \ + $$MOC_DIR/* $$MOC_DIR \ + $$OBJECTS_DIR/* $$OBJECTS_DIR \ + $$MGEN_OUTDIR/* $$MGEN_OUTDIR \ + +# Install +INSTALLS += target