diff --git a/CMakeLists.txt b/CMakeLists.txt index c605502..fd6938a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,59 +1,82 @@ # Set minimum CMake version (required for CMake 3.0 or later) cmake_minimum_required(VERSION 2.8.12) option(BUILD_GUI_DEPS "Build Mycroft-GUI Dependencies" OFF) set(QT_MIN_VERSION "5.9.0") set(KF5_MIN_VERSION "5.0.0") # Use Extra CMake Modules (ECM) for common functionality. # See http://api.kde.org/ecm/manual/ecm.7.html # and http://api.kde.org/ecm/manual/ecm-kde-modules.7.html find_package(ECM REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_MODULE_PATH}) set(VERSION 2.1.0) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMInstallIcons) include(ExternalProject) # Locate plasma_install_package macro. find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Plasma I18n Notifications ) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Quick Core Qml DBus Network WebSockets ) add_subdirectory(icons) add_subdirectory(plugin) +find_program(MycroftGUI mycroft-gui-app) + +if(NOT MycroftGUI) + message(STATUS "MYCROFT GUI DEPENDENCY NOT FOUND, ADDING AND BUILDING DEPENDENCIES") + +ExternalProject_Add(mycroft-gui + GIT_REPOSITORY https://github.com/MycroftAI/Mycroft-Gui + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DKDE_INSTALL_LIBDIR=lib -DKDE_INSTALL_USE_QT_SYS_PATHS=ON -DBUILD_REMOTE_TTS=ON + BUILD_COMMAND make + INSTALL_COMMAND sudo make install +) + +ExternalProject_Add(lottie-qml + GIT_REPOSITORY https://github.com/kbroulik/lottie-qml + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DKDE_INSTALL_LIBDIR=lib -DKDE_INSTALL_USE_QT_SYS_PATHS=ON + BUILD_COMMAND make + INSTALL_COMMAND sudo make install +) +endif() + +if(MycroftGUI) + message(STATUS "MYCROFT GUI DEPENDENCY FOUND, SKIPPING BUILDING DEPENDENCIES") +endif() if(BUILD_GUI_DEPS) ExternalProject_Add(mycroft-gui GIT_REPOSITORY https://github.com/MycroftAI/Mycroft-Gui CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DKDE_INSTALL_LIBDIR=lib -DKDE_INSTALL_USE_QT_SYS_PATHS=ON -DBUILD_REMOTE_TTS=ON BUILD_COMMAND make INSTALL_COMMAND sudo make install ) ExternalProject_Add(lottie-qml GIT_REPOSITORY https://github.com/kbroulik/lottie-qml CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DKDE_INSTALL_LIBDIR=lib -DKDE_INSTALL_USE_QT_SYS_PATHS=ON BUILD_COMMAND make INSTALL_COMMAND sudo make install ) endif() plasma_install_package(plasmoid org.kde.plasma.mycroftplasmoid) install( DIRECTORY mycroft DESTINATION ${SYSCONF_INSTALL_DIR}) diff --git a/plasmoid/contents/config/main.xml b/plasmoid/contents/config/main.xml index 28bac85..c8704f6 100644 --- a/plasmoid/contents/config/main.xml +++ b/plasmoid/contents/config/main.xml @@ -1,22 +1,28 @@ true false true - + + + + + + false + diff --git a/plasmoid/contents/ui/BottomBarViewComponent.qml b/plasmoid/contents/ui/BottomBarViewComponent.qml index 6762eb4..288e34d 100644 --- a/plasmoid/contents/ui/BottomBarViewComponent.qml +++ b/plasmoid/contents/ui/BottomBarViewComponent.qml @@ -1,105 +1,119 @@ /* Copyright 2016 Aditya Mehra 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) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. 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, see . */ import QtQuick 2.9 import QtQml.Models 2.2 import QtQuick.Controls 2.2 as Controls import QtQuick.Layouts 1.3 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.kirigami 2.5 as Kirigami import QtGraphicalEffects 1.0 import Mycroft 1.0 as Mycroft Item { id: appletBottomBarComponent anchors.fill: parent property alias autoCompModel: completionItems property alias queryInput: qinput property var lastMessage function autoAppend(model, getinputstring, setinputstring) { for(var i = 0; i < model.count; ++i) if (getinputstring(model.get(i))){ return true } return null } function evalAutoLogic() { if (suggestionsBox.currentIndex === -1) { } else { suggestionsBox.complete(suggestionsBox.currentItem) } } ListModel { id: completionItems } ColumnLayout { anchors.fill: parent - PlasmaComponents.TextField { - id: qinput + + RowLayout{ Layout.fillWidth: true - Layout.preferredHeight: Kirigami.Units.gridUnit * 2 - placeholderText: i18n("Enter Query or Say 'Hey Mycroft'") - clearButtonShown: true + Layout.fillHeight: true + + PlasmaComponents.TextField { + id: qinput + Layout.fillWidth: true + Layout.preferredHeight: Kirigami.Units.gridUnit * 2 + placeholderText: i18n("Enter Query or Say 'Hey Mycroft'") + clearButtonShown: true - onAccepted: { - if(qinput.text !== ""){ - lastMessage = qinput.text + onAccepted: { + if(qinput.text !== ""){ + lastMessage = qinput.text + } + var doesExist = appletBottomBarComponent.autoAppend(autoCompModel, function(item) { return item.name === qinput.text }, qinput.text) + var evaluateExist = doesExist + if(evaluateExist === null){ + autoCompModel.append({"name": qinput.text}); + } + Mycroft.MycroftController.sendText(qinput.text); + Mycroft.MycroftController.sendRequest('mycroft.qinput.text', {"inputQuery": qinput.Text}) + qinput.text = ""; } - var doesExist = appletBottomBarComponent.autoAppend(autoCompModel, function(item) { return item.name === qinput.text }, qinput.text) - var evaluateExist = doesExist - if(evaluateExist === null){ - autoCompModel.append({"name": qinput.text}); - } - Mycroft.MycroftController.sendText(qinput.text); - Mycroft.MycroftController.sendRequest('mycroft.qinput.text', {"inputQuery": qinput.Text}) - qinput.text = ""; - } - onTextChanged: { - appletBottomBarComponent.evalAutoLogic(); - } + onTextChanged: { + appletBottomBarComponent.evalAutoLogic(); + } - AutocompleteBox { - id: suggestionsBox - model: completionItems - width: parent.width - anchors.bottom: parent.top - anchors.left: parent.left - anchors.right: parent.right - filter: qinput.text - property: "name" - onItemSelected: complete(item) + AutocompleteBox { + id: suggestionsBox + model: completionItems + width: parent.width + anchors.bottom: parent.top + anchors.left: parent.left + anchors.right: parent.right + filter: qinput.text + property: "name" + onItemSelected: complete(item) - function complete(item) { - if (item !== undefined) - qinput.text = item.name + function complete(item) { + if (item !== undefined) + qinput.text = item.name + } + } + } + + Controls.Button { + text: "Speak" // TODO generic microphone icon + onClicked: { + audioRecorder.open() } + visible: plasmoid.configuration.enableRemoteSTT } } } } diff --git a/plasmoid/contents/ui/FullRepresentation.qml b/plasmoid/contents/ui/FullRepresentation.qml index daeb2bd..aabf650 100644 --- a/plasmoid/contents/ui/FullRepresentation.qml +++ b/plasmoid/contents/ui/FullRepresentation.qml @@ -1,207 +1,228 @@ /* Copyright 2016 Aditya Mehra Copyright 2018 Marco Martin 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) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. 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, see . */ import QtQuick 2.9 import QtQml.Models 2.2 import QtQuick.Controls 2.2 as Controls import QtQuick.Layouts 1.3 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.plasma.plasmoid 2.0 import org.kde.kirigami 2.5 as Kirigami import org.kde.private.mycroftplasmoid 1.0 as MycroftPlasmoid import Mycroft 1.0 as Mycroft Item { id: root implicitWidth: Kirigami.Units.gridUnit * 20 implicitHeight: Kirigami.Units.gridUnit * 32 property bool cfg_notifications: plasmoid.configuration.notifications Item { id: topBar anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right height: Kirigami.Units.gridUnit * 4 ColumnLayout{ anchors.fill: parent TopBarViewComponent { id: topBarView Layout.fillWidth: true Layout.preferredHeight: Kirigami.Units.gridUnit * 2 } PlasmaCore.SvgItem { Layout.fillWidth: true Layout.preferredHeight: horlineSvg.elementSize("horizontal-line").height elementId: "horizontal-line" svg: PlasmaCore.Svg { id: horlineSvg2; imagePath: "widgets/line" } } PlasmaComponents.TabBar { id: tabBar visible: true Layout.fillWidth: true Layout.preferredHeight: Kirigami.Units.gridUnit * 1.5 PlasmaComponents.TabButton { id: mycroftTab iconSource: "go-home" text: "Conversation" } PlasmaComponents.TabButton { id: mycroftSkillsTab iconSource: "games-hint" text: "Hints & Tips" } PlasmaComponents.TabButton { id: mycroftMSMinstTab iconSource: "kmouth-phresebook-new" text: "Skill Browser" } } PlasmaCore.SvgItem { Layout.fillWidth: true Layout.preferredHeight: horlineSvg.elementSize("horizontal-line").height elementId: "horizontal-line" svg: PlasmaCore.Svg { id: horlineSvg; imagePath: "widgets/line" } } } } ColumnLayout { anchors.left: parent.left anchors.right: parent.right anchors.bottom: bottomBar.top anchors.top: topBar.bottom anchors.topMargin: Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing opacity: Mycroft.MycroftController.status == Mycroft.MycroftController.Open visible: tabBar.currentTab == mycroftTab; Behavior on opacity { OpacityAnimator { duration: Kirigami.Units.longDuration easing.type: Easing.InOutCubic } } Item { id: delegateItem Layout.fillWidth: true Layout.fillHeight: true + Controls.Popup { + id: audioRecorder + width: 300 + height: 125 + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + x: (root.width - width) / 2 + y: (root.height - height) / 2 + + RemoteStt { + id: remoteSttInstance + } + + onOpenedChanged: { + if(audioRecorder.opened){ + remoteSttInstance.record = true; + } else { + remoteSttInstance.record = false; + } + } + } + Mycroft.SkillView { id: skillView anchors.fill: parent Kirigami.Theme.colorSet: Kirigami.Theme.View anchors.margins: Kirigami.Units.largeSpacing clip: true onCurrentItemChanged: { backgroundVisible = false } Connections { id: mycroftConnection target: Mycroft.MycroftController onFallbackTextRecieved: { Mycroft.MycroftController.sendRequest("mycroft.desktop.applet.show_conversationview", {}) if (!plasmoid.expanded && cfg_notifications == true) { var post = data.utterance; var title = "Mycroft's Reply:" var notiftext = " " + post; MycroftPlasmoid.Notify.mycroftResponse(title, notiftext); } } } } } } Item { id: bottomBar anchors.bottom: root.bottom anchors.left: root.left anchors.right: root.right height: Kirigami.Units.gridUnit * 2 BottomBarViewComponent { id: bottomBarView } } Image { anchors.centerIn: parent opacity: Mycroft.MycroftController.status != Mycroft.MycroftController.Open source: "../images/logo.png" Behavior on opacity { OpacityAnimator { duration: Kirigami.Units.longDuration easing.type: Easing.InOutCubic } } } ColumnLayout { id: mycroftSkillscolumntab visible: tabBar.currentTab == mycroftSkillsTab; anchors.left: parent.left anchors.right: parent.right anchors.bottom: bottomBar.top anchors.top: topBar.bottom anchors.topMargin: Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing anchors.bottomMargin: Kirigami.Units.smallSpacing HintsViewComponent { id: hintsView } } ColumnLayout { id: mycroftMsmColumn visible: tabBar.currentTab == mycroftMSMinstTab; anchors.left: parent.left anchors.right: parent.right anchors.bottom: bottomBar.top anchors.top: topBar.bottom anchors.topMargin: Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing anchors.bottomMargin: Kirigami.Units.smallSpacing SkillsInstallerComponent{ id: skillsInstallerView } } } diff --git a/plasmoid/contents/ui/RemoteStt.qml b/plasmoid/contents/ui/RemoteStt.qml new file mode 100644 index 0000000..04eb8cf --- /dev/null +++ b/plasmoid/contents/ui/RemoteStt.qml @@ -0,0 +1,135 @@ +import QtQuick 2.9 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.2 +import org.kde.kirigami 2.4 as Kirigami +import Mycroft 1.0 as Mycroft + +Item { + id: root + anchors.fill: parent + property alias record: audrectimer.running + + Mycroft.AudioRec { + id: audioRec + } + + Timer { + id: audrectimer + interval: 10000 + running: false + onRunningChanged: { + if(running){ + audioRec.recordTStart() + numAnim.start() + } else { + audioRec.recordTStop() + } + } + } + + function sendAudioClip() { + audioRec.returnStream() + } + + Connections { + target: audioRec + onRecordTStatus: { + console.log(recStatus) + switch(recStatus){ + case "Completed": + console.log("In Completed") + audioRec.readStream() + sendAudioClip() + audioRecorder.close() + break; + case "Error": + console.log("inError") + break; + } + } + } + + ColumnLayout { + id: closebar + anchors.fill: parent + spacing: 0 + + Kirigami.Heading { + id: lbl1 + text: "Listening" + level: 2 + font.bold: true + Layout.alignment: Qt.AlignTop | Qt.AlignHCenter + } + + Kirigami.Separator { + Layout.fillWidth: true + Layout.preferredHeight: 1 + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + ProgressBar { + id: bar + to: 100 + from: 0 + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Kirigami.Units.largeSpacing + anchors.rightMargin: Kirigami.Units.largeSpacing + anchors.verticalCenter: parent.verticalCenter + + contentItem: Item { + implicitWidth: parent.width + implicitHeight: parent.height + + Rectangle { + width: bar.visualPosition * parent.width + height: parent.height + radius: 2 + gradient: Gradient { + GradientStop { position: 0.0; color: Kirigami.Theme.linkColor } + GradientStop { position: 0.3; color: Qt.lighter(Kirigami.Theme.linkColor, 1.5)} + GradientStop { position: 0.6; color: Qt.lighter(Kirigami.Theme.linkColor, 1.5)} + GradientStop { position: 1.0; color: Kirigami.Theme.linkColor } + } + } + } + + NumberAnimation { + id: numAnim + target: bar + property: "value" + from: 0 + to: 100 + duration: 10000 + } + } + } + + Kirigami.Separator { + Layout.fillWidth: true + Layout.preferredHeight: 1 + } + + Rectangle { + color: Kirigami.Theme.hoverColor + Layout.fillWidth: true + Layout.preferredHeight: Kirigami.Units.gridUnit * 1.5 + + Kirigami.Heading { + level: 2 + text: "Close" + anchors.centerIn: parent + anchors.margins: Kirigami.Units.largeSpacing + } + + MouseArea { + anchors.fill: parent + onClicked: audioRecorder.close() + } + } + } +} + diff --git a/plasmoid/contents/ui/config/configGeneral.qml b/plasmoid/contents/ui/config/configGeneral.qml index d196ca6..2fe8e5c 100644 --- a/plasmoid/contents/ui/config/configGeneral.qml +++ b/plasmoid/contents/ui/config/configGeneral.qml @@ -1,63 +1,61 @@ import QtQuick 2.9 import QtQml.Models 2.2 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.kirigami 2.5 as Kirigami import Mycroft 1.0 as Mycroft Item { id: page property alias cfg_websocketAddress: websocketAddress.text property alias cfg_notificationSwitch: notificationSwitch.checked property alias cfg_selectView: selectView.currentIndex + property alias cfg_enableRemoteTTS: enableRemoteTTS.checked + property alias cfg_enableRemoteSTT: enableRemoteSTT.checked - GridLayout { - Layout.fillWidth: true - columns: 2 - - PlasmaComponents.Label { - id: websocketLabel - text: "Websocket Address" - } - + Kirigami.FormLayout { + anchors.left: parent.left + anchors.right: parent.right + PlasmaComponents.TextField { - id: websocketAddress - Layout.fillWidth: true - - Component.onCompleted: { - websocketAddress.text = Mycroft.GlobalSettings.webSocketAddress - } + id: websocketAddress + Layout.fillWidth: true + Kirigami.FormData.label: i18n("Websocket Address:") + Component.onCompleted: { + websocketAddress.text = Mycroft.GlobalSettings.webSocketAddress } + } - ColumnLayout{ - Layout.fillWidth: true + CheckBox { + id: notificationSwitch + Kirigami.FormData.label: i18n ("Additional Settings:") + text: i18n("Enable Notifications") + checked: true + } - PlasmaComponents.Switch { - id: notificationSwitch - Layout.fillWidth: true - text: i18n("Enable Notifications") - checked: true - } - - RowLayout { - Layout.fillWidth: true - - PlasmaComponents.Label { - id: selectViewLabel - Layout.fillWidth: true - text: "Select Default View:" - } - - PlasmaComponents.ComboBox{ - id: selectView - Layout.fillWidth: true - model: ["Conversation View", "Dashboard View"] - } - } + CheckBox { + id: enableRemoteTTS + text: i18n("Enable Remote TTS") + checked: Mycroft.GlobalSettings.usesRemoteTTS + onCheckedChanged: Mycroft.GlobalSettings.usesRemoteTTS = checked + } + + CheckBox { + id: enableRemoteSTT + text: i18n("Enable Remote STT") + checked: false + } + + PlasmaComponents.ComboBox{ + id: selectView + Layout.fillWidth: true + Kirigami.FormData.label: i18n ("Default View:") + model: ["Conversation View", "Dashboard View"] } } } +