Warning, /system/mycroft-gui/application/main.qml is written in an unsupported language. File is not indexed.

0001 /*
0002  * Copyright 2018 by Marco Martin <mart@kde.org>
0003  * Copyright 2018 David Edmundson <davidedmundson@kde.org>
0004  *
0005  * Licensed under the Apache License, Version 2.0 (the "License");
0006  * you may not use this file except in compliance with the License.
0007  * You may obtain a copy of the License at
0008  *
0009  *    http://www.apache.org/licenses/LICENSE-2.0
0010  *
0011  * Unless required by applicable law or agreed to in writing, software
0012  * distributed under the License is distributed on an "AS IS" BASIS,
0013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014  * See the License for the specific language governing permissions and
0015  * limitations under the License.
0016  *
0017  */
0018 
0019 import QtQuick 2.15
0020 import QtQuick.Window 2.15
0021 import QtQuick.Layouts 1.15
0022 import QtQuick.Controls 2.15
0023 import QtQuick.Controls.Material 2.15
0024 import org.kde.kirigami 2.19 as Kirigami
0025 import Mycroft 1.0 as Mycroft 
0026 import org.kde.private.mycroftgui 1.0 as MycroftGui
0027 import Qt5Compat.GraphicalEffects
0028 
0029 Kirigami.ApplicationWindow {
0030     id: root
0031     visible: true
0032 
0033     minimumHeight : deviceHeight || undefined
0034     maximumHeight : deviceHeight || undefined
0035     minimumWidth : deviceWidth || undefined
0036     maximumWidth : deviceWidth || undefined
0037     x: deviceWidth ? Screen.desktopAvailableHeight - width : undefined
0038     y: deviceHeight ? Screen.desktopAvailableHeight - height : undefined
0039 
0040     color: "black"
0041     //HACK!! needs proper api in kirigami
0042     Component.onCompleted: {
0043         globalDrawer.handle.handleAnchor = handleAnchor;
0044 
0045         // Maximize and auto connect if set
0046         if (deviceMaximized) {
0047             showMaximized()
0048         }
0049 
0050         if (singleSkillHome.length > 0 && Mycroft.MycroftController.status === Mycroft.MycroftController.Open) {
0051             Mycroft.MycroftController.sendRequest(singleSkillHome, {});
0052         }
0053         
0054         if(!isAndroid && Kirigami.Settings.isMobile){
0055             applicationSettings.usesRemoteSTT = true
0056             Mycroft.GlobalSettings.usesRemoteTTS = true
0057         }
0058     }
0059 
0060     Connections {
0061         target: Mycroft.MycroftController
0062         function onStatusChanged(status) {
0063             if (singleSkillHome.length > 0 && Mycroft.MycroftController.status === Mycroft.MycroftController.Open) {
0064                 Mycroft.MycroftController.sendRequest(singleSkillHome, {});
0065             }
0066         }
0067         
0068         function onSpeechRequestedChanged(expectingResponse) {
0069             if(expectingResponse) {
0070                 micButton.clicked()
0071             }
0072         }
0073         
0074         function onSkillTimeoutReceived(skillidleid) {
0075             if(mainView.currentItem.contentItem.skillId() == skillidleid) {
0076                 root.close()
0077             }
0078         }
0079     }
0080     
0081     Connections {
0082         target: keyFilter
0083         function onGlobalBackReceived() {
0084             mainView.currentItem.backRequested()
0085         }
0086     }
0087     
0088     // Uses Android's voice popup for speech recognition
0089     MycroftGui.SpeechIntent {
0090         id: speechIntent
0091         title: "Say something to Mycroft" // TODO i18n
0092         onSpeechRecognized: (text)=> {
0093             Mycroft.MycroftController.sendText(text)
0094         }
0095         //onRecognitionFailed: console.log("SPEECH FAILED")
0096         //onRecognitionCanceled: console.log("SPEECH CANCELED")
0097         //onNothingRecognized: console.log("SPEECH NOTHING")
0098     }
0099 
0100     //HACK
0101     Connections {
0102         target: root.pageStack.layers
0103         onDepthChanged: {
0104             if (root.pageStack.layers.depth == 1) {
0105                 globalDrawer.handle.handleAnchor = handleAnchor;
0106             } else {
0107                 globalDrawer.handle.handleAnchor = null;
0108             }
0109         }
0110     }
0111 
0112     globalDrawer: Kirigami.GlobalDrawer {
0113         bannerImageSource: "banner.png"
0114         handleVisible: !hideTextInput
0115         Kirigami.Theme.inherit: false
0116         Kirigami.Theme.colorSet: applicationSettings.darkMode ? Kirigami.Theme.Complementary : Kirigami.Theme.View
0117 
0118         actions: [
0119             Kirigami.Action {
0120                 text: "Hints"
0121                 iconName: "help-hint"
0122                 visible: !Kirigami.Settings.isMobile
0123                 checked: pageStack.layers.currentItem.objectName == "hints"
0124                 onTriggered: {
0125                     if (checked) {
0126                         pageStack.layers.pop(pageStack.layers.initialItem);
0127                     } else if (pageStack.layers.depth > 1) {
0128                         pageStack.layers.replace(Qt.resolvedUrl("HintsPage.qml"));
0129                     } else {
0130                         pageStack.layers.push(Qt.resolvedUrl("HintsPage.qml"));
0131                     }
0132                 }
0133             },
0134             Kirigami.Action {
0135                 text: "Settings"
0136                 iconName: "configure"
0137                 checked: pageStack.layers.currentItem.objectName == "Settings"
0138                 onTriggered: {
0139                     if (checked) {
0140                         pageStack.layers.pop(pageStack.layers.initialItem);
0141                     } else if (pageStack.layers.depth > 1) {
0142                         pageStack.layers.replace(Qt.resolvedUrl("SettingsPage.qml"));
0143                     } else {
0144                         pageStack.layers.push(Qt.resolvedUrl("SettingsPage.qml"));
0145                     }
0146                 }
0147             },
0148             Kirigami.Action {
0149                 text: "About"
0150                 iconName: "help-about"
0151                 checked: pageStack.layers.currentItem.objectName == "About"
0152                 onTriggered: {
0153                     if (checked) {
0154                         pageStack.layers.pop(pageStack.layers.initialItem);
0155                     } else if (pageStack.layers.depth > 1) {
0156                         pageStack.layers.replace(Qt.resolvedUrl("AboutPage.qml"));
0157                     } else {
0158                         pageStack.layers.push(Qt.resolvedUrl("AboutPage.qml"));
0159                     }
0160                 }
0161             }
0162         ]
0163         
0164         Switch {
0165             id: nightSwitch
0166             visible: !Kirigami.Settings.isMobile
0167             text: "Dark Mode"
0168             checked: applicationSettings.darkMode
0169             onCheckedChanged: applicationSettings.darkMode = checked
0170         }
0171     }
0172 
0173     Timer {
0174         interval: 20000
0175         running: Mycroft.GlobalSettings.autoConnect && Mycroft.MycroftController.status != Mycroft.MycroftController.Open
0176         triggeredOnStart: true
0177         onTriggered: {
0178             print("Trying to connect to Mycroft");
0179             Mycroft.MycroftController.start();
0180         }
0181     }
0182 
0183     pageStack.globalToolBar.style: pageStack.layers.depth == 1 ? Kirigami.ApplicationHeaderStyle.None : Kirigami.ApplicationHeaderStyle.Auto
0184 
0185     pageStack.initialPage: Kirigami.Page {
0186         leftPadding: 0
0187         rightPadding: 0
0188         topPadding: 0
0189         bottomPadding: 0
0190         onBackRequested: {
0191             if (mainView.active) {
0192                 event.accepted = true;
0193                 mainView.goBack();
0194             }
0195         }
0196         Rectangle {
0197             color: nightSwitch.checked ? "black" : Kirigami.Theme.backgroundColor
0198             rotation: globalScreenRotation || 0
0199             anchors.fill: parent
0200             Image {
0201                 visible: singleSkill.length === 0
0202                 source: "background.png"
0203                 fillMode: Image.PreserveAspectFit
0204                 anchors.fill: parent
0205                 opacity: !mainView.currentItem
0206                 Behavior on opacity {
0207                     OpacityAnimator {
0208                         duration: Kirigami.Units.longDuration
0209                         easing.type: Easing.InQuad
0210                     }
0211                 }
0212             }
0213 
0214             Popup {
0215                 id: audioRecorder
0216                 width: root.width - (Kirigami.Units.largeSpacing * 2)
0217                 height: root.height / 2
0218                 closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
0219                 Kirigami.Theme.colorSet: nightSwitch.checked ? Kirigami.Theme.Complementary : Kirigami.Theme.View
0220                 parent: root
0221                 x: (root.width - width) / 2
0222                 y: (root.height - height) / 2
0223                 
0224                 background: Rectangle {
0225                     color: Kirigami.Theme.backgroundColor
0226                     radius: Kirigami.Units.smallSpacing * 0.25
0227                     border.width: 1
0228                     Kirigami.Theme.colorSet: nightSwitch.checked ? Kirigami.Theme.Complementary : Kirigami.Theme.View
0229                     border.color: Qt.rgba(Kirigami.Theme.disabledTextColor.r, Kirigami.Theme.disabledTextColor.g, Kirigami.Theme.disabledTextColor.b, 0.7)
0230                 }
0231                 
0232                 RemoteStt {
0233                     id: remoteSttInstance
0234                 }
0235 
0236                 onOpenedChanged: {
0237                     if(audioRecorder.opened){
0238                         remoteSttInstance.record = true;
0239                     } else {
0240                         remoteSttInstance.record = false;
0241                     }
0242                 }
0243             }
0244 
0245             Mycroft.SkillView {
0246                 id: mainView
0247                 activeSkills.whiteList: singleSkill.length > 0 ? singleSkill : null
0248                 Kirigami.Theme.colorSet: nightSwitch.checked ? Kirigami.Theme.Complementary : Kirigami.Theme.View
0249                 anchors.fill: parent
0250             }
0251 
0252             Button {
0253                 anchors.centerIn: parent
0254                 text: "start"
0255                 visible: Mycroft.MycroftController.status == Mycroft.MycroftController.Closed
0256                 onClicked: (mouse)=> {
0257                     Mycroft.MycroftController.start();
0258                 }
0259             }
0260 
0261             Mycroft.StatusIndicator {
0262                 id: si
0263                 //visible: false
0264                 anchors {
0265                     top: parent.top
0266                     right: parent.right
0267                     margins: Kirigami.Units.largeSpacing
0268                 }
0269                 z: 999
0270             }
0271 
0272             Kirigami.Heading {
0273                 id: inputQuery
0274                 Kirigami.Theme.colorSet: mainView.Kirigami.Theme.colorSet
0275                 anchors.right: si.left
0276                 anchors.rightMargin: Kirigami.Units.largeSpacing
0277                 anchors.verticalCenter: si.verticalCenter
0278                 level: 3
0279                 opacity: 0
0280                 onTextChanged: {
0281                     opacity = 1;
0282                     utteranceTimer.restart();
0283                 }
0284                 Timer {
0285                     id: utteranceTimer
0286                     interval: 8000
0287                     onTriggered: {
0288                         inputQuery.text = "";
0289                         inputQuery.opacity = 0
0290                     }
0291                 }
0292                 Behavior on opacity {
0293                     OpacityAnimator {
0294                         duration: Kirigami.units.longDuration
0295                         easing.type: Easing.InOutQuad
0296                     }
0297                 }
0298 
0299                 Connections {
0300                     target: Mycroft.MycroftController
0301                     function onIntentRecevied(type, data) {
0302                         if(type == "recognizer_loop:utterance") {
0303                             inputQuery.text = data.utterances[0]
0304                         }
0305                     }
0306                 }
0307             }
0308         }
0309 
0310         //Note: a custom control as ToolBar on Android has a funny color
0311         footer: Control {
0312             Kirigami.Theme.colorSet: nightSwitch.checked ? Kirigami.Theme.Complementary : Kirigami.Theme.Window
0313             visible: !hideTextInput
0314             height: hideTextInput ? 0 : implicitHeight
0315             implicitHeight: contentItem.implicitHeight + topPadding + bottomPadding
0316             contentItem: RowLayout {
0317                 Item {
0318                     id: handleAnchor
0319                     Layout.fillHeight: true
0320                     Layout.preferredWidth: height
0321                 }
0322                 
0323                 ToolButton {
0324                     id: backButton
0325                     Kirigami.Theme.colorSet: nightSwitch.checked ? Kirigami.Theme.Complementary : Kirigami.Theme.Window
0326                     Layout.preferredWidth: handleAnchor.width
0327                     Layout.fillHeight: true
0328                     Layout.rightMargin: Kirigami.Units.smallSpacing
0329                     enabled: !isAndroid && Kirigami.Settings.isMobile ? 1 : 0
0330                     icon.name: "go-previous"
0331                     
0332                     onClicked:(mouse)=> {
0333                         mainView.currentItem.backRequested()
0334                     }
0335                     visible: !isAndroid && Kirigami.Settings.isMobile ? 1 : 0
0336                 }
0337                 
0338                 
0339                 TextField {
0340                     id: qinput
0341                     Layout.fillWidth: true
0342 
0343                     placeholderText: "Ask Mycroft..."
0344                     onAccepted: {
0345                         Mycroft.MycroftController.sendText(qinput.text)
0346                     }
0347                     focus: false
0348                     Connections {
0349                         target: speechIntent
0350                         function onSpeechRecognized(text){ 
0351                             qinput.text = text
0352                         }
0353                     }
0354                     onFocusChanged: {
0355                         if (focus) {
0356                             selectAll();
0357                         }
0358                     }
0359                 }
0360                 
0361                 ToolButton {
0362                     id: micButton
0363                     Kirigami.Theme.colorSet: nightSwitch.checked ? Kirigami.Theme.Complementary : Kirigami.Theme.Window
0364                     Layout.preferredWidth: handleAnchor.width
0365                     Layout.fillHeight: true
0366                     Layout.rightMargin: Kirigami.Units.smallSpacing
0367                     icon.name: "audio-input-microphone"
0368                     
0369                     onClicked: (mouse)=>  {
0370                         if(applicationSettings.usesRemoteSTT){
0371                             audioRecorder.open()
0372                         } else {
0373                             speechIntent.start()
0374                         }
0375                     }
0376                     visible: speechIntent.supported || applicationSettings.usesRemoteSTT
0377                 }
0378             }
0379             background: Rectangle {
0380                 color: Kirigami.Theme.backgroundColor
0381                 LinearGradient {
0382                     anchors {
0383                         left: parent.left
0384                         right: parent.right
0385                         bottom: parent.top
0386                     }
0387                     implicitHeight: Kirigami.Units.gridUnit/2
0388 
0389                     start: Qt.point(0, height)
0390                     end: Qt.point(0, 0)
0391                     gradient: Gradient {
0392                         GradientStop {
0393                             position: 0.0
0394                             color: Qt.rgba(0, 0, 0, 0.2)
0395                         }
0396                         GradientStop {
0397                             position: 0.3
0398                             color: Qt.rgba(0, 0, 0, 0.1)
0399                         }
0400                         GradientStop {
0401                             position: 1.0
0402                             color:  "transparent"
0403                         }
0404                     }
0405                 }
0406             }
0407         }
0408     }
0409 }