diff --git a/components/fullscreenpanel.cpp b/components/fullscreenpanel.cpp index 84e37b5..f43386e 100644 --- a/components/fullscreenpanel.cpp +++ b/components/fullscreenpanel.cpp @@ -1,101 +1,98 @@ /*************************************************************************** * Copyright 2015 Marco Martin * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "fullscreenpanel.h" #include #include #include #include #include #include #include #include #include FullScreenPanel::FullScreenPanel(QQuickWindow *parent) : QQuickWindow(parent) { setFlags(Qt::FramelessWindowHint); setWindowState(Qt::WindowFullScreen); // connect(this, &FullScreenPanel::activeFocusItemChanged, this, [this]() {qWarning()<<"hide()";}); connect(this, &QWindow::activeChanged, this, &FullScreenPanel::activeChanged); initWayland(); } FullScreenPanel::~FullScreenPanel() { } void FullScreenPanel::initWayland() { if (!QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) { return; } using namespace KWayland::Client; ConnectionThread *connection = ConnectionThread::fromApplication(this); if (!connection) { return; } Registry *registry = new Registry(this); registry->create(connection); m_surface = Surface::fromWindow(this); if (!m_surface) { return; } connect(registry, &Registry::plasmaShellAnnounced, this, [this, registry] (quint32 name, quint32 version) { m_plasmaShellInterface = registry->createPlasmaShell(name, version, this); m_plasmaShellSurface = m_plasmaShellInterface->createSurface(m_surface, this); m_plasmaShellSurface->setSkipTaskbar(true); } ); /* connect(registry, &Registry::shellAnnounced, this, [this, registry] (quint32 name, quint32 version) { m_shellInterface = registry->createShell(name, version, this); if (!m_shellInterface) { return; } //bshah: following code results in error... //wl_surface@67: error 0: ShellSurface already created //Wayland display got fatal error 71: Protocol error //Additionally, errno was set to 71: Protocol error m_shellSurface = m_shellInterface->createSurface(m_surface, this); } );*/ registry->setup(); connection->roundtrip(); } void FullScreenPanel::showEvent(QShowEvent *event) { using namespace KWayland::Client; QQuickWindow::showEvent(event); } - -#include "fullscreenpanel.moc" - diff --git a/containments/homescreen/applicationlistmodel.cpp b/containments/homescreen/applicationlistmodel.cpp index ce34d35..30b90c5 100644 --- a/containments/homescreen/applicationlistmodel.cpp +++ b/containments/homescreen/applicationlistmodel.cpp @@ -1,276 +1,274 @@ /* * Copyright (C) 2014 Antonis Tsiapaliokas * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * or (at your option) any later version, as published by the Free * Software Foundation * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // Self #include "applicationlistmodel.h" // Qt #include #include #include // KDE #include #include #include #include #include #include #include #include #include #include ApplicationListModel::ApplicationListModel(QObject *parent) : QAbstractListModel(parent) { //can't use the new syntax as this signal is overloaded connect(KSycoca::self(), SIGNAL(databaseChanged(const QStringList &)), this, SLOT(sycocaDbChanged(const QStringList &))); } ApplicationListModel::~ApplicationListModel() { } QHash ApplicationListModel::roleNames() const { QHash roleNames; roleNames[ApplicationNameRole] = "ApplicationNameRole"; roleNames[ApplicationIconRole] = "ApplicationIconRole"; roleNames[ApplicationStorageIdRole] = "ApplicationStorageIdRole"; roleNames[ApplicationEntryPathRole] = "ApplicationEntryPathRole"; roleNames[ApplicationOriginalRowRole] = "ApplicationOriginalRowRole"; return roleNames; } void ApplicationListModel::sycocaDbChanged(const QStringList &changes) { if (!changes.contains("apps") && !changes.contains("xdgdata-apps")) { return; } m_applicationList.clear(); loadApplications(); } bool appNameLessThan(const ApplicationData &a1, const ApplicationData &a2) { return a1.name.toLower() < a2.name.toLower(); } void ApplicationListModel::loadApplications() { auto cfg = KSharedConfig::openConfig("applications-blacklistrc"); auto blgroup = KConfigGroup(cfg, QStringLiteral("Applications")); // This is only temporary to get a clue what those apps' desktop files are called // I'll remove it once I've done a blacklist QStringList bl; QStringList blacklist = blgroup.readEntry("blacklist", QStringList()); beginResetModel(); m_applicationList.clear(); KServiceGroup::Ptr group = KServiceGroup::root(); if (!group || !group->isValid()) return; KServiceGroup::List subGroupList = group->entries(true); QMap orderedList; QList unorderedList; // Iterate over all entries in the group while (!subGroupList.isEmpty()) { KSycocaEntry::Ptr groupEntry = subGroupList.first(); subGroupList.pop_front(); if (groupEntry->isType(KST_KServiceGroup)) { KServiceGroup::Ptr serviceGroup(static_cast(groupEntry.data())); if (!serviceGroup->noDisplay()) { KServiceGroup::List entryGroupList = serviceGroup->entries(true); for(KServiceGroup::List::ConstIterator it = entryGroupList.constBegin(); it != entryGroupList.constEnd(); it++) { KSycocaEntry::Ptr entry = (*it); if (entry->isType(KST_KServiceGroup)) { KServiceGroup::Ptr serviceGroup(static_cast(entry.data())); subGroupList << serviceGroup; } else if (entry->property("Exec").isValid()) { KService::Ptr service(static_cast(entry.data())); qDebug() << " desktopEntryName: " << service->desktopEntryName(); if (service->isApplication() && !blacklist.contains(service->desktopEntryName()) && service->showOnCurrentPlatform() && !service->property("Terminal", QVariant::Bool).toBool()) { bl << service->desktopEntryName(); ApplicationData data; data.name = service->name(); data.icon = service->icon(); data.storageId = service->storageId(); data.entryPath = service->exec(); auto it = m_appPositions.constFind(service->storageId()); if (it != m_appPositions.constEnd()) { orderedList[*it] = data; } else { unorderedList << data; } } } } } } } blgroup.writeEntry("allapps", bl); blgroup.writeEntry("blacklist", blacklist); cfg->sync(); std::sort(unorderedList.begin(), unorderedList.end(), appNameLessThan); m_applicationList << orderedList.values(); m_applicationList << unorderedList; endResetModel(); emit countChanged(); } QVariant ApplicationListModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } switch (role) { case Qt::DisplayRole: case ApplicationNameRole: return m_applicationList.at(index.row()).name; case ApplicationIconRole: return m_applicationList.at(index.row()).icon; case ApplicationStorageIdRole: return m_applicationList.at(index.row()).storageId; case ApplicationEntryPathRole: return m_applicationList.at(index.row()).entryPath; case ApplicationOriginalRowRole: return index.row(); default: return QVariant(); } } Qt::ItemFlags ApplicationListModel::flags(const QModelIndex &index) const { if (!index.isValid()) return 0; return Qt::ItemIsDragEnabled|QAbstractItemModel::flags(index); } int ApplicationListModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return m_applicationList.count(); } -void ApplicationListModel::moveRow(const QModelIndex &sourceParent, int sourceRow, const QModelIndex &destinationParent, int destinationChild) +void ApplicationListModel::moveRow(const QModelIndex& /* sourceParent */, int sourceRow, const QModelIndex& /* destinationParent */, int destinationChild) { moveItem(sourceRow, destinationChild); } Q_INVOKABLE void ApplicationListModel::moveItem(int row, int destination) { if (row < 0 || destination < 0 || row >= m_applicationList.length() || destination >= m_applicationList.length() || row == destination) { return; } if (destination > row) { ++destination; } beginMoveRows(QModelIndex(), row, row, QModelIndex(), destination); if (destination > row) { ApplicationData data = m_applicationList.at(row); m_applicationList.insert(destination, data); m_applicationList.takeAt(row); } else { ApplicationData data = m_applicationList.takeAt(row); m_applicationList.insert(destination, data); } m_appOrder.clear(); m_appPositions.clear(); int i = 0; for (auto app : m_applicationList) { m_appOrder << app.storageId; m_appPositions[app.storageId] = i; ++i; } emit appOrderChanged(); endMoveRows(); } void ApplicationListModel::runApplication(const QString &storageId) { if (storageId.isEmpty()) { return; } KService::Ptr service = KService::serviceByStorageId(storageId); - KRun::run(*service, QList(), 0); + KRun::runService(*service, QList(), 0); } QStringList ApplicationListModel::appOrder() const { return m_appOrder; } void ApplicationListModel::setAppOrder(const QStringList &order) { if (m_appOrder == order) { return; } m_appOrder = order; m_appPositions.clear(); int i = 0; for (auto app : m_appOrder) { m_appPositions[app] = i; ++i; } emit appOrderChanged(); } - -#include "applicationlistmodel.moc" diff --git a/dialer/package/contents/ui/Dialer/Dialer.qml b/dialer/package/contents/ui/Dialer/Dialer.qml index 7e4338a..235f9e5 100644 --- a/dialer/package/contents/ui/Dialer/Dialer.qml +++ b/dialer/package/contents/ui/Dialer/Dialer.qml @@ -1,90 +1,83 @@ /* * Copyright 2014 Aaron Seigo * Copyright 2014 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.0 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import "../Dialpad" Item { id: dialer property alias numberEntryText: status.text - function addNumber(number) { - status.text = status.text + number - } - Rectangle { width: parent.width / 2 x: parent.width / 4 y: parent.height - callStatusNotification.paintedHeight color: PlasmaCore.ColorScope.backgroundColor opacity: 0.6 visible: dialerUtils.callState == "failed" PlasmaComponents.Label { id: callStatusNotification anchors.fill: parent text: "Unable to make a call at this moment" horizontalAlignment: Text.AlignHCenter wrapMode: Text.WordWrap color: PlasmaCore.ColorScope.textColor } } ColumnLayout { id: dialPadArea anchors { fill: parent margins: units.largeSpacing } - PlasmaComponents.Label { + PhoneNumberInput { id: status + Layout.fillWidth: true - Layout.minimumHeight: parent.height / 6 + Layout.minimumHeight: units.gridUnit * 3.5 Layout.maximumHeight: Layout.minimumHeight - - horizontalAlignment: Qt.AlignRight - verticalAlignment: Qt.AlignVCenter - font.pointSize: 1024 - fontSizeMode: Text.Fit + font.pointSize: 30 } Dialpad { Layout.fillWidth: true Layout.fillHeight: true callback: function (string) { - addNumber(string); + status.append(string) } pressedCallback: function (string) { //TODO // ofonoWrapper.startTone(string); } releasedCallback: function (string) { // ofonoWrapper.stopTone(); } } } } diff --git a/dialer/package/contents/ui/Dialpad/DialerButton.qml b/dialer/package/contents/ui/Dialpad/DialerButton.qml index c39d802..cf4a783 100644 --- a/dialer/package/contents/ui/Dialpad/DialerButton.qml +++ b/dialer/package/contents/ui/Dialpad/DialerButton.qml @@ -1,124 +1,112 @@ /* * Copyright 2014 Aaron Seigo * Copyright 2014 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.4 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents -PlasmaComponents.Label { +Item { + id: root Layout.fillWidth: true Layout.fillHeight: true - //This is 0 to override the Label default height that would cause a binding loop - height: 0 - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignTop - font.pointSize: 21 //anything higher for some reason makes number 4 not rendered - minimumPointSize: 8 - fontSizeMode: Text.VerticalFit - - property alias sub: longHold.text - property var callback - property var pressedCallback - property var releasedCallback + property string text + property string sub + property string display + property string subdisplay + property bool special: false Rectangle { anchors.fill: parent z: -1 color: PlasmaCore.ColorScope.highlightColor radius: units.smallSpacing opacity: mouse.pressed ? 0.4 : 0 Behavior on opacity { OpacityAnimator { duration: units.longDuration easing.type: Easing.InOutQuad } } } MouseArea { id: mouse anchors.fill: parent + onPressed: { - if (pressedCallback) { - pressedCallback(parent.text); - } else if (pad.pressedCallback) { + if (pad.pressedCallback) { pad.pressedCallback(parent.text); } } onReleased: { - if (releasedCallback) { - releasedCallback(parent.text); - } else if (pad.releasedCallback) { + if (pad.releasedCallback) { pad.releasedCallback(parent.text); } } onCanceled: { - if (releasedCallback) { - releasedCallback(parent.text); - } else if (pad.releasedCallback) { + if (pad.releasedCallback) { pad.releasedCallback(parent.text); } } - onClicked: { - if (callback) { - callback(parent.text); - } else if (pad.callback) { + if (pad.callback) { pad.callback(parent.text); } } - onPressAndHold: { - var text; - if (longHold.visible) { - text = longHold.text; - } else { - text = parent.text; - } - - if (text.length > 1) { - return; - } - - if (callback) { - callback(text); - } else if (pad.callback) { + var text = parent.sub.length > 0 ? parent.sub : parent.text + if (pad.callback && text.length === 1) { pad.callback(text); } } } - PlasmaComponents.Label { - id: longHold - anchors { - bottom: parent.bottom + ColumnLayout { + spacing: -5 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + + PlasmaComponents.Label { + id: main + + text: root.display || root.text + opacity: special? 0.4 : 1.0 + // anything higher for some reason makes number 4 not rendered + font.pointSize: 30 + fontSizeMode: Text.VerticalFit + Layout.minimumWidth: parent.width + horizontalAlignment: Text.AlignHCenter } - height: parent.height * 0.4 - width: parent.width - verticalAlignment: Text.AlignBottom - horizontalAlignment: Text.AlignHCenter - visible: text.length > 0 - opacity: 0.4 - fontSizeMode: Text.VerticalFit + PlasmaComponents.Label { + id: longHold + + text: root.subdisplay || root.sub + opacity: 0.4 + font.pointSize: 16 + fontSizeMode: Text.VerticalFit + Layout.minimumWidth: parent.width + horizontalAlignment: Text.AlignHCenter + } } } diff --git a/dialer/package/contents/ui/Dialpad/DialerIconButton.qml b/dialer/package/contents/ui/Dialpad/DialerIconButton.qml index a88a528..e097386 100644 --- a/dialer/package/contents/ui/Dialpad/DialerIconButton.qml +++ b/dialer/package/contents/ui/Dialpad/DialerIconButton.qml @@ -1,96 +1,96 @@ /* * Copyright 2014 Aaron Seigo * Copyright 2014 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.4 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents Item { id: buttonRoot Layout.fillWidth: true Layout.fillHeight: true property var callback property var pressedCallback property var releasedCallback property string sub property alias source: icon.source property alias text: label.text Rectangle { anchors.fill: parent z: -1 color: PlasmaCore.ColorScope.highlightColor radius: units.smallSpacing opacity: mouse.pressed ? 0.4 : 0 Behavior on opacity { OpacityAnimator { duration: units.longDuration easing.type: Easing.InOutQuad } } } Row { anchors.centerIn: parent PlasmaCore.IconItem { id: icon anchors.verticalCenter: parent.verticalCenter width: height - height: buttonRoot.height * 0.6 + height: units.gridUnit * 3.5 } PlasmaComponents.Label { id: label height: buttonRoot.height anchors.verticalCenter: parent.verticalCenter font.pointSize: 1024 fontSizeMode: Text.VerticalFit } } MouseArea { id: mouse anchors.fill: parent onClicked: { if (callback) { callback(parent.text); } else { addNumber(parent.text); } } onPressAndHold: { var text; if (longHold.visible) { text = longHold.text; } else { text = parent.text; } if (callback) { callback(text); } else if (pad.callback) { pad.callback(text); } } } } diff --git a/dialer/package/contents/ui/Dialpad/Dialpad.qml b/dialer/package/contents/ui/Dialpad/Dialpad.qml index 6253518..d7c88b9 100644 --- a/dialer/package/contents/ui/Dialpad/Dialpad.qml +++ b/dialer/package/contents/ui/Dialpad/Dialpad.qml @@ -1,82 +1,72 @@ /* * Copyright 2014 Aaron Seigo * Copyright 2014 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.0 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents GridLayout { id: pad columns: 3 + rowSpacing: 10 + columnSpacing: 10 property var callback property var pressedCallback property var releasedCallback - property int buttonHeight: parent.height / 6 - - DialerButton { id: one; text: "1" } + DialerButton { id: one; text: "1" } DialerButton { text: "2"; sub: "ABC" } DialerButton { text: "3"; sub: "DEF" } - DialerButton { text: "4"; sub: "GHI" } + DialerButton { text: "4"; sub: "GHI" } DialerButton { text: "5"; sub: "JKL" } DialerButton { text: "6"; sub: "MNO" } - DialerButton { text: "7"; sub: "PQRS" } + DialerButton { text: "7"; sub: "PQRS" } DialerButton { text: "8"; sub: "TUV" } DialerButton { text: "9"; sub: "WXYZ" } - DialerButton { text: "*"; } - DialerButton { text: "0"; sub: "+"; } - DialerButton { text: "#" } + DialerButton { display: "*"; text: "*"; special: true; } + DialerButton { text: "0"; subdisplay: "+"; sub: "+"; } + DialerButton { display: "#"; text: "#"; special: true; } + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } DialerIconButton { id: callButton Layout.fillWidth: true - Layout.maximumHeight: buttonHeight - Layout.minimumHeight: Layout.maximumHeight + Layout.fillHeight: true enabled: status.text.length > 0 opacity: enabled ? 1 : 0.5 source: "call-start" callback: function() { call(status.text); } } Item { Layout.fillWidth: true - } - DialerIconButton { - Layout.fillWidth: true - Layout.maximumHeight: buttonHeight - Layout.minimumHeight: Layout.maximumHeight - - enabled: status.text.length > 0 - opacity: enabled ? 1 : 0.5 - source: "edit-clear" - callback: function(text) { - if (status.text.length > 0) { - status.text = status.text.substr(0, status.text.length - 1); - } - } + Layout.fillHeight: true } } diff --git a/dialer/package/contents/ui/Dialpad/PhoneNumberInput.qml b/dialer/package/contents/ui/Dialpad/PhoneNumberInput.qml new file mode 100644 index 0000000..81ccd0c --- /dev/null +++ b/dialer/package/contents/ui/Dialpad/PhoneNumberInput.qml @@ -0,0 +1,62 @@ +import QtQuick 2.0 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.1 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +PlasmaComponents.TextField { + id: root + + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignBottom + + style: TextFieldStyle { + background: Rectangle { + opacity: 0 + } + } + + signal append(string digit) + onAppend: { + text += digit + } + onTextChanged: { + text = dialerUtils.formatNumber(text); + } + + // TODO: search through contacts while typing + + Row { + anchors { + right: parent.right + rightMargin: 6 + verticalCenter: parent.verticalCenter + } + + PlasmaCore.IconItem { + id: delBtn + // ltr confusingly refers to the direction of the arrow in the icon, + // not the text direction which it should be used in. + source: LayoutMirroring.enabled ? + "edit-clear-locationbar-ltr" : "edit-clear-locationbar-rtl" + height: Math.max(root.height * 0.8, units.iconSizes.small) + width: height + opacity: (root.length > 0 && root.enabled) ? 1 : 0 + visible: opacity > 0 + Behavior on opacity { + NumberAnimation { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + MouseArea { + anchors.fill: parent + onClicked: { + if (text.length > 0) { + text = text.slice(0, -1); + } + } + } + } + } +} diff --git a/dialer/plugin/kpeoplehelperplugin.cpp b/dialer/plugin/kpeoplehelperplugin.cpp index f8daf2c..3189c58 100644 --- a/dialer/plugin/kpeoplehelperplugin.cpp +++ b/dialer/plugin/kpeoplehelperplugin.cpp @@ -1,31 +1,29 @@ /* Copyright (C) 2015 Martin Klapetek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "kpeoplehelperplugin.h" #include "kpeoplehelper.h" #include void KPeopleHelperPlugin::registerTypes(const char *uri) { Q_ASSERT(uri == QLatin1String("org.kde.plasma.private.kpeoplehelper")); qmlRegisterType(uri, 1, 0, "KPeopleHelper"); } - -#include "kpeoplehelperplugin.moc" diff --git a/dialer/src/CMakeLists.txt b/dialer/src/CMakeLists.txt index 928a05a..7737415 100644 --- a/dialer/src/CMakeLists.txt +++ b/dialer/src/CMakeLists.txt @@ -1,36 +1,38 @@ set(plasmaphonedialer_SRCS main.cpp dialerutils.cpp call-handler.cpp call-manager.cpp ) add_executable(plasmaphonedialer ${plasmaphonedialer_SRCS}) target_compile_definitions(plasmaphonedialer PRIVATE -DPROJECT_VERSION="${PROJECT_VERSION}") #find_package(ActiveApp REQUIRED) +find_library(phonenumber REQUIRED) target_link_libraries(plasmaphonedialer + phonenumber Qt5::Gui Qt5::Quick Qt5::Widgets KF5::Declarative KF5::I18n KF5::Package KF5::QuickAddons KF5::DBusAddons KF5::Notifications ${TELEPATHY_QT5_LIBRARIES} ) install(TARGETS plasmaphonedialer ${INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES plasma_dialer.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFY5RCDIR}) configure_file(org.freedesktop.Telepathy.Client.Plasma.Dialer.service.in org.freedesktop.Telepathy.Client.Plasma.Dialer.service) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.Telepathy.Client.Plasma.Dialer.service DESTINATION ${DBUS_SERVICES_INSTALL_DIR}) install(FILES Plasma.Dialer.client DESTINATION ${SHARE_INSTALL_PREFIX}/telepathy/clients/) diff --git a/dialer/src/dialerutils.cpp b/dialer/src/dialerutils.cpp index ae1c9d0..7e19f45 100644 --- a/dialer/src/dialerutils.cpp +++ b/dialer/src/dialerutils.cpp @@ -1,156 +1,185 @@ /* * Copyright 2015 Marco Martin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "dialerutils.h" #include -#include #include #include #include #include #include #include #include +#include "phonenumbers/phonenumberutil.h" +#include "phonenumbers/asyoutypeformatter.h" + DialerUtils::DialerUtils(const Tp::AccountPtr &simAccount, QObject *parent) : QObject(parent), m_missedCalls(0), m_simAccount(simAccount), m_callDuration(0), m_callContactAlias(QString()) { Tp::PendingReady *op = m_simAccount->becomeReady(Tp::Features() << Tp::Account::FeatureCore); connect(op, &Tp::PendingOperation::finished, [=](){ if (op->isError()) { qWarning() << "SIM card account failed to get ready:" << op->errorMessage(); } else { qDebug() << "SIM Account ready to use"; } }); } DialerUtils::~DialerUtils() { } void DialerUtils::dial(const QString &number) { // FIXME: this should be replaced by kpeople thing auto pendingContact = m_simAccount->connection()->contactManager()->contactsForIdentifiers(QStringList() << number); connect(pendingContact, &Tp::PendingOperation::finished, [=](){ if (pendingContact->contacts().size() < 1) { qWarning() << " no contacts"; return; } qDebug() << "Starting call..."; Tp::PendingChannelRequest *pendingChannel = m_simAccount->ensureAudioCall(pendingContact->contacts().first()); connect(pendingChannel, &Tp::PendingChannelRequest::finished, [=](){ if (pendingChannel->isError()) { qWarning() << "Error when requesting channel" << pendingChannel->errorMessage(); setCallState("failed"); } }); }); } QString DialerUtils::callState() const { return m_callState; } +const QString DialerUtils::formatNumber(const QString& number) +{ + using namespace ::i18n::phonenumbers; + + // Get formatter instance + QLocale locale; + QStringList qcountry = locale.name().split('_'); + QString countrycode(qcountry.constLast()); + const char* country = countrycode.toUtf8().constData(); + PhoneNumberUtil* util = PhoneNumberUtil::GetInstance(); + AsYouTypeFormatter* formatter = util->PhoneNumberUtil::GetAsYouTypeFormatter(country); + + // Normalize input + string stdnumber = number.toUtf8().constData(); + util->NormalizeDiallableCharsOnly(&stdnumber); + + // Format + string formatted; + formatter->Clear(); + for (char& c : stdnumber) { + formatter->InputDigit(c, &formatted); + } + delete formatter; + + return QString::fromStdString(formatted); +} + void DialerUtils::setCallState(const QString &state) { if (m_callState != state) { m_callState = state; Q_EMIT callStateChanged(); } } uint DialerUtils::callDuration() const { return m_callDuration; } void DialerUtils::setCallDuration(uint duration) { m_callDuration = duration; Q_EMIT callDurationChanged(); } QString DialerUtils::callContactAlias() const { return m_callContactAlias; } void DialerUtils::setCallContactAlias(const QString &contactAlias) { if (m_callContactAlias != contactAlias) { m_callContactAlias = contactAlias; Q_EMIT callContactAliasChanged(); } } QString DialerUtils::callContactNumber() const { return m_callContactNumber; } void DialerUtils::setCallContactNumber(const QString &contactNumber) { if (m_callContactNumber != contactNumber) { m_callContactNumber = contactNumber; Q_EMIT callContactNumberChanged(); } } bool DialerUtils::isIncomingCall() const { return m_isIncomingCall; } void DialerUtils::setIsIncomingCall(bool isIncomingCall) { if (m_isIncomingCall != isIncomingCall) { m_isIncomingCall = isIncomingCall; Q_EMIT isIncomingCallChanged(); } } void DialerUtils::emitCallEnded() { qDebug() << "Call ended:" << m_callContactNumber << m_callDuration; Q_EMIT callEnded(m_callContactNumber, m_callDuration, m_isIncomingCall); m_callDuration = 0; m_callContactNumber = QString(); m_callContactAlias = QString(); } void DialerUtils::resetMissedCalls() { m_missedCalls = 0; if (m_callsNotification) { m_callsNotification->close(); } m_callsNotification.clear(); } #include "moc_dialerutils.cpp" diff --git a/dialer/src/dialerutils.h b/dialer/src/dialerutils.h index 0b51f21..6a317c8 100644 --- a/dialer/src/dialerutils.h +++ b/dialer/src/dialerutils.h @@ -1,87 +1,88 @@ /* * Copyright 2015 Marco Martin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef DIALERUTILS_H #define DIALERUTILS_H #include #include #include #include class DialerUtils : public QObject { Q_OBJECT Q_PROPERTY(QString callState READ callState NOTIFY callStateChanged); Q_PROPERTY(uint callDuration READ callDuration NOTIFY callDurationChanged); Q_PROPERTY(QString callContactAlias READ callContactAlias NOTIFY callContactAliasChanged); Q_PROPERTY(QString callContactNumber READ callContactNumber NOTIFY callContactNumberChanged); Q_PROPERTY(bool isIncomingCall READ isIncomingCall NOTIFY isIncomingCallChanged); public: DialerUtils(const Tp::AccountPtr &simAccount, QObject *parent = 0); virtual ~DialerUtils(); QString callState() const; void setCallState(const QString &state); uint callDuration() const; void setCallDuration(uint duration); QString callContactAlias() const; void setCallContactAlias(const QString &contactAlias); QString callContactNumber() const; void setCallContactNumber(const QString &contactNumber); bool isIncomingCall() const; void setIsIncomingCall(bool isIncomingCall); void emitCallEnded(); Q_INVOKABLE void resetMissedCalls(); Q_INVOKABLE void dial(const QString &number); + Q_INVOKABLE const QString formatNumber(const QString& number); Q_SIGNALS: void missedCallsActionTriggered(); void callStateChanged(); void callDurationChanged(); void callContactAliasChanged(); void callContactNumberChanged(); void isIncomingCallChanged(); void acceptCall(); void rejectCall(); void hangUp(); void callEnded(const QString &callContactNumber, uint callDuration, bool isIncomingCall); private: QPointer m_callsNotification; QPointer m_ringingNotification; int m_missedCalls; QString m_callState; Tp::AccountPtr m_simAccount; - QString m_callContactAlias; QString m_callContactNumber; uint m_callDuration; + QString m_callContactAlias; bool m_isIncomingCall; }; #endif diff --git a/dialer/src/main.cpp b/dialer/src/main.cpp index 9885d08..26c16a6 100644 --- a/dialer/src/main.cpp +++ b/dialer/src/main.cpp @@ -1,206 +1,216 @@ /* * Copyright 2015 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "dialerutils.h" #include "call-handler.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) { - // TODO: print it on stdout too? QFile file(QDir::homePath() + "/dialer.log"); bool opened = file.open(QIODevice::WriteOnly | QIODevice::Append); Q_ASSERT(opened); - QTextStream out(&file); + QString strout; + QTextStream out(&strout); out << QTime::currentTime().toString("hh:mm:ss.zzz "); out << context.function << ":" << context.line << " "; switch (type) { - case QtDebugMsg: out << "DBG"; break; + case QtDebugMsg: out << "DBG"; break; + case QtInfoMsg: out << "NFO"; break; case QtWarningMsg: out << "WRN"; break; case QtCriticalMsg: out << "CRT"; break; case QtFatalMsg: out << "FTL"; break; } out << " " << msg << '\n'; + + // Write to log file + QTextStream fileout(&file); + fileout << strout; out.flush(); + + // Write to stdout + QTextStream console(stdout); + console << strout; + console.flush(); } int main(int argc, char **argv) { qInstallMessageHandler(myMessageOutput); QCommandLineParser parser; QApplication app(argc, argv); const QString description = i18n("Plasma Phone Dialer"); const char version[] = PROJECT_VERSION; // app.setQuitOnLastWindowClosed(false); app.setApplicationVersion(version); app.setOrganizationDomain("kde.org"); KDBusService service(KDBusService::Unique); parser.addVersionOption(); parser.addHelpOption(); parser.setApplicationDescription(description); QCommandLineOption daemonOption(QStringList() << QStringLiteral("d") << QStringLiteral("daemon"), i18n("Daemon mode. run without displaying anything.")); parser.addPositionalArgument("number", i18n("Call the given number")); parser.addOption(daemonOption); parser.process(app); Tp::registerTypes(); Tp::AccountFactoryPtr accountFactory = Tp::AccountFactory::create(QDBusConnection::sessionBus(), Tp::Features() << Tp::Account::FeatureCore ); Tp::ConnectionFactoryPtr connectionFactory = Tp::ConnectionFactory::create(QDBusConnection::sessionBus(), Tp::Features() << Tp::Connection::FeatureCore << Tp::Connection::FeatureSelfContact << Tp::Connection::FeatureConnected ); Tp::ChannelFactoryPtr channelFactory = Tp::ChannelFactory::create(QDBusConnection::sessionBus()); channelFactory->addCommonFeatures(Tp::Channel::FeatureCore); channelFactory->addFeaturesForCalls(Tp::Features() << Tp::CallChannel::FeatureContents << Tp::CallChannel::FeatureCallState << Tp::CallChannel::FeatureCallMembers << Tp::CallChannel::FeatureLocalHoldState ); // channelFactory->addFeaturesForTextChats(Tp::Features() << Tp::TextChannel::FeatureMessageQueue // << Tp::TextChannel::FeatureMessageSentSignal // << Tp::TextChannel::FeatureChatState // << Tp::TextChannel::FeatureMessageCapabilities); Tp::ContactFactoryPtr contactFactory = Tp::ContactFactory::create(Tp::Features() << Tp::Contact::FeatureAlias << Tp::Contact::FeatureAvatarData ); Tp::ClientRegistrarPtr registrar = Tp::ClientRegistrar::create(accountFactory, connectionFactory, channelFactory, contactFactory); Tp::AccountPtr simAccount = Tp::Account::create(TP_QT_ACCOUNT_MANAGER_BUS_NAME, QStringLiteral("/org/freedesktop/Telepathy/Account/ofono/ofono/account0"), connectionFactory, channelFactory); const QString packagePath("org.kde.phone.dialer"); //usually we have an ApplicationWindow here, so we do not need to create a window by ourselves KDeclarative::QmlObject *obj = new KDeclarative::QmlObject(); obj->setTranslationDomain(packagePath); obj->setInitializationDelayed(true); obj->loadPackage(packagePath); obj->engine()->rootContext()->setContextProperty("commandlineArguments", parser.positionalArguments()); DialerUtils *dialerUtils = new DialerUtils(simAccount); obj->engine()->rootContext()->setContextProperty("dialerUtils", QVariant::fromValue(dialerUtils)); obj->completeInitialization(); if (!obj->package().metadata().isValid()) { return -1; } Tp::SharedPtr callHandler(new CallHandler(dialerUtils)); registrar->registerClient(Tp::AbstractClientPtr::dynamicCast(callHandler), "Plasma.Dialer"); KPluginMetaData data = obj->package().metadata(); // About data KAboutData aboutData(data.pluginId(), data.name(), data.version(), data.description(), KAboutLicense::byKeyword(data.license()).key()); for (auto author : data.authors()) { aboutData.addAuthor(author.name(), author.task(), author.emailAddress(), author.webAddress(), author.ocsUsername()); } //The root is not a window? //have to use a normal QQuickWindow since the root item is already created QWindow *window = qobject_cast(obj->rootObject()); if (window) { QObject::connect(&service, &KDBusService::activateRequested, [=](const QStringList &arguments, const QString &workingDirectory) { Q_UNUSED(workingDirectory); window->show(); window->requestActivate(); if (arguments.length() > 0) { QString numberArg = arguments[1]; if (numberArg.startsWith("call://")) { numberArg = numberArg.mid(7); } obj->rootObject()->metaObject()->invokeMethod(obj->rootObject(), "call", Q_ARG(QVariant, numberArg)); } }); if (!parser.isSet(daemonOption)) { window->show(); window->requestActivate(); } window->setTitle(obj->package().metadata().name()); window->setIcon(QIcon::fromTheme(obj->package().metadata().iconName())); if (!parser.positionalArguments().isEmpty()) { QString numberArg = parser.positionalArguments().first(); if (numberArg.startsWith("call://")) { numberArg = numberArg.mid(7); } qWarning() << "Calling" << numberArg; obj->rootObject()->metaObject()->invokeMethod(obj->rootObject(), "call", Q_ARG(QVariant, numberArg)); } } else { qWarning() << "Error loading the ApplicationWindow"; } return app.exec(); }