diff options
Diffstat (limited to 'app')
30 files changed, 2270 insertions, 193 deletions
diff --git a/app/app.pri b/app/app.pri deleted file mode 100644 index c3b1fd1..0000000 --- a/app/app.pri +++ /dev/null @@ -1,13 +0,0 @@ -TEMPLATE = app -QMAKE_LFLAGS += "-Wl,--hash-style=gnu -Wl,--as-needed" - -load(configure) -qtCompileTest(libhomescreen) - -config_libhomescreen { - CONFIG += link_pkgconfig - PKGCONFIG += homescreen - DEFINES += HAVE_LIBHOMESCREEN -} - -DESTDIR = $${OUT_PWD}/../package/root/bin diff --git a/app/app.pro b/app/app.pro index 22c9b00..78b861a 100644 --- a/app/app.pro +++ b/app/app.pro @@ -1,17 +1,18 @@ -###################################################################### -# Automatically generated by qmake (3.1) Tue Apr 24 05:31:07 2018 -###################################################################### +TEMPLATE = app +TARGET = warehouse +QT += quickcontrols2 dbus websockets -TARGET = restriction -INCLUDEPATH += . +DESTDIR = $${OUT_PWD}/../package/root/bin -QT = quickcontrols2 +CONFIG += c++11 link_pkgconfig +PKGCONFIG += qlibhomescreen qlibwindowmanager -CONFIG += link_pkgconfig -PKGCONFIG += libhomescreen qlibwindowmanager +include(../interfaces/interfaces.pri) +include(src/src.pri) -# Input -SOURCES += main.cpp -RESOURCES += main.qrc +OTHER_FILES += \ + main.qml -include(app.pri)
\ No newline at end of file +RESOURCES += \ + resources.qrc \ + images/images.qrc diff --git a/app/images/Back.svg b/app/images/Back.svg new file mode 100644 index 0000000..d91ac10 --- /dev/null +++ b/app/images/Back.svg @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Svg Vector Icons : http://www.sfont.cn --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + x="0px" + y="0px" + viewBox="0 0 1000 1000" + enable-background="new 0 0 1000 1000" + xml:space="preserve" + id="svg8" + sodipodi:docname="Back.svg" + inkscape:version="0.92.3 (unknown)"><defs + id="defs12" /><sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1855" + inkscape:window-height="1056" + id="namedview10" + showgrid="false" + inkscape:zoom="0.62286164" + inkscape:cx="213.58606" + inkscape:cy="465.41879" + inkscape:window-x="65" + inkscape:window-y="24" + inkscape:window-maximized="1" + inkscape:current-layer="svg8" /> +<metadata + id="metadata2"> Svg Vector Icons : http://www.sfont.cn <rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata> +<g + id="g6" + style="fill:#38d5ee;fill-opacity:0.39215687;stroke:#0df9ff;stroke-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none"><path + d="M10,500L500,10v306.3h490v367.5H500V990L10,500z" + id="path4" + style="fill:#38d5ee;fill-opacity:0.39215687;stroke:#0df9ff;stroke-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none" /></g> +</svg>
\ No newline at end of file diff --git a/app/images/DividingLine.svg b/app/images/DividingLine.svg new file mode 100644 index 0000000..d63589c --- /dev/null +++ b/app/images/DividingLine.svg @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> + +<svg + xmlns:i="&ns_ai;" + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + id="Layer_1" + x="0px" + y="0px" + viewBox="0 0 932.8 1" + style="enable-background:new 0 0 932.8 1;" + xml:space="preserve" + inkscape:version="0.91 r13725" + sodipodi:docname="HMI_Settings_DividingLine.svg"><metadata + id="metadata18"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs + id="defs16" /><sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="2560" + inkscape:window-height="1464" + id="namedview14" + showgrid="false" + inkscape:zoom="0.72041167" + inkscape:cx="-116.6" + inkscape:cy="0.5" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="1" + inkscape:current-layer="Layer_1" /><style + type="text/css" + id="style3"> + .st0{opacity:0.302;} + .st1{fill-rule:evenodd;clip-rule:evenodd;fill:#A8A8A8;} +</style><switch + id="switch5"><g + i:extraneous="self" + id="g7"><g + id="Divider_2_" + class="st0"><g + id="g10"><polygon + class="st1" + points="719.7,0 0,0 0,1 932.8,1 " + id="polygon12" /></g></g></g></switch></svg>
\ No newline at end of file diff --git a/app/images/Top.svg b/app/images/Top.svg new file mode 100644 index 0000000..0fffbe8 --- /dev/null +++ b/app/images/Top.svg @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Svg Vector Icons : http://www.sfont.cn --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + x="0px" + y="0px" + viewBox="0 0 1000 1000" + enable-background="new 0 0 1000 1000" + xml:space="preserve" + id="svg10" + sodipodi:docname="Top.svg" + inkscape:version="0.92.3 (unknown)"><defs + id="defs14" /><sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1855" + inkscape:window-height="1056" + id="namedview12" + showgrid="false" + inkscape:zoom="0.83153125" + inkscape:cx="500" + inkscape:cy="500" + inkscape:window-x="65" + inkscape:window-y="24" + inkscape:window-maximized="1" + inkscape:current-layer="svg10" /> +<metadata + id="metadata2"> Svg Vector Icons : http://www.sfont.cn <rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata> +<g + id="g8" + style="fill:#38d5ee;fill-opacity:0.39215687;stroke:#0df9ff;stroke-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none"><path + d="M135.1,578.8l368-431l361.8,431H667.1V990H325.8V578.8H135.1z" + id="path4" + style="fill:#38d5ee;fill-opacity:0.39215687;stroke:#0df9ff;stroke-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none" /><path + d="M168.3,10h673.8v105H168.3V10z" + id="path6" + style="fill:#38d5ee;fill-opacity:0.39215687;stroke:#0df9ff;stroke-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none" /></g> +</svg>
\ No newline at end of file diff --git a/app/images/black_normal.png b/app/images/black_normal.png Binary files differdeleted file mode 100644 index 378798a..0000000 --- a/app/images/black_normal.png +++ /dev/null diff --git a/app/images/black_split.png b/app/images/black_split.png Binary files differdeleted file mode 100644 index 9ce95ed..0000000 --- a/app/images/black_split.png +++ /dev/null diff --git a/app/images/images.qrc b/app/images/images.qrc new file mode 100644 index 0000000..867a055 --- /dev/null +++ b/app/images/images.qrc @@ -0,0 +1,7 @@ +<RCC> + <qresource prefix="/images"> + <file>DividingLine.svg</file> + <file>Back.svg</file> + <file>Top.svg</file> + </qresource> +</RCC>
\ No newline at end of file diff --git a/app/main.cpp b/app/main.cpp deleted file mode 100644 index 92f1c5f..0000000 --- a/app/main.cpp +++ /dev/null @@ -1,107 +0,0 @@ - -#include <QtQml/qqml.h> -#include <qlibwindowmanager.h> -#include <QQuickWindow> -#include <QtCore/QCommandLineParser> -#include <QtCore/QDebug> -#include <QtCore/QDir> -#include <QtCore/QStandardPaths> -#include <QtCore/QUrlQuery> -#include <QtGui/QGuiApplication> -#include <QtQml/QQmlApplicationEngine> -#include <QtQml/QQmlContext> -#include <QtQuickControls2/QQuickStyle> -#include <libhomescreen.hpp> - -int main(int argc, char* argv[]) { - QString role = QString("Restriction"); - - QGuiApplication app(argc, argv); - - QQuickStyle::setStyle("AGL"); - - QQmlApplicationEngine engine; - - QCommandLineParser parser; - parser.addPositionalArgument("port", - app.translate("main", "port for binding")); - parser.addPositionalArgument("secret", - app.translate("main", "secret for binding")); - parser.addHelpOption(); - parser.addVersionOption(); - parser.process(app); - QStringList positionalArguments = parser.positionalArguments(); - - if (positionalArguments.length() == 2) { - int port = positionalArguments.takeFirst().toInt(); - QString secret = positionalArguments.takeFirst(); - QUrl bindingAddress; - bindingAddress.setScheme(QStringLiteral("ws")); - bindingAddress.setHost(QStringLiteral("localhost")); - bindingAddress.setPort(port); - bindingAddress.setPath(QStringLiteral("/api")); - QUrlQuery query; - query.addQueryItem(QStringLiteral("token"), secret); - bindingAddress.setQuery(query); - - std::string token = secret.toStdString(); - LibHomeScreen* hs = new LibHomeScreen(); - QLibWindowmanager* qwm = new QLibWindowmanager(); - - if (qwm->init(port, secret) != 0) { - exit(EXIT_FAILURE); - } - // Request a surface as described in layers.json windowmanager’s file - if (qwm->requestSurface(role) != 0) { - exit(EXIT_FAILURE); - } - // Create an event callback against an event type. Here a lambda is - // called when SyncDraw event occurs - qwm->set_event_handler(QLibWindowmanager::Event_SyncDraw, - [qwm, role](json_object* object) { - fprintf(stderr, "Surface got syncDraw!\n"); - qwm->endDraw(role); - }); - - engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); - QObject* root = engine.rootObjects().first(); - - // HomeScreen - hs->init(port, token.c_str()); - - // release restriction - hs->set_event_handler( - LibHomeScreen::Event_ReleaseRestriction, - [qwm, role, root](json_object* object) { - json_object* areaJ = nullptr; - if (json_object_object_get_ex(object, "area", &areaJ)) { - QString area(QLatin1String(json_object_get_string(areaJ))); - - QMetaObject::invokeMethod(root, "hideImage"); - - // qwm->releaseWR(role, area); - qwm->deactivateSurface(role); - } - }); - - // allocate restriction - hs->set_event_handler( - LibHomeScreen::Event_AllocateRestriction, - [qwm, role, root](json_object* object) { - json_object* areaJ = nullptr; - if (json_object_object_get_ex(object, "area", &areaJ)) { - QString area(QLatin1String(json_object_get_string(areaJ))); - qDebug() - << "Surface got Event_AllocateRestriction " << area; - - QMetaObject::invokeMethod(root, "showImage", - Q_ARG(QVariant, area)); - - // qwm->allocateWR(role, area); - qwm->activateSurface(role, area.prepend("restriction.")); - } - }); - } - - return app.exec(); -}
\ No newline at end of file diff --git a/app/main.qml b/app/main.qml index 15369cf..a481320 100644 --- a/app/main.qml +++ b/app/main.qml @@ -1,67 +1,92 @@ +/* + * Copyright (C) 2018 The Qt Company Ltd. + * + * 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. + */ import QtQuick 2.6 import QtQuick.Controls 2.0 +import AGL.Demo.Controls 1.0 +import NativeAppModel 1.0 +import ServerAppModel 1.0 + +import 'pages' ApplicationWindow { id: root - color: "#00000000" - - Label { - id: message - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.margins: 20 - font.pixelSize: 75 - wrapMode: Text.WordWrap - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - color: "white" - text: "Only the video’s sound will be available while driving." - } + width: 1080 * screenInfo.scale_factor() + height: 1487 * screenInfo.scale_factor() - background : Image { - id: backgroundImg + SwipeView { + id: stackLayout anchors.fill: parent - anchors.topMargin: 0 - anchors.bottomMargin: 0 + currentIndex: tabBar.currentIndex - visible: true - fillMode: Image.Stretch - source: 'images/black_normal.png' + ListPage { + id: listPage - state: "begin" - states: [ - State { - name: "begin" - PropertyChanges { target: backgroundImg; opacity: 0.25 } - }, - State { - name: "end" - PropertyChanges { target: backgroundImg; opacity: 0.75 } + model: ServerAppModel { + id: listModel } - ] + } + + SearchPage { + id: searchPage - transitions: [ - Transition { - from: "begin"; to: "end" - PropertyAnimation {target: backgroundImg; properties: "opacity"; duration: 2000} + title: 'SearchPage' + } + + ManagementPage { + id: managementPage + + model: NativeAppModel { + id: managementModel } - ] - } + } + + Connections { + target: managementPage.model + onApplistChanged: { + listPage.model.setNativeApplist(applist) + } + } - function showImage(area) { - if (area === 'normal') { - backgroundImg.source = 'images/black_normal.png' - } else { - backgroundImg.source = 'images/black_split.png' + Component.onCompleted: { + listPage.model.getNextPage(0) + managementPage.model.refresh() } - backgroundImg.state = "end" } - - function hideImage() { - backgroundImg.state = "begin" + + footer: TabBar { + id: tabBar + height: 80 + currentIndex: stackLayout.currentIndex + + TabButton { + text: "List" + font.pixelSize: 20 + height: parent.height + } + TabButton { + text: "Search" + font.pixelSize: 20 + height: parent.height + } + TabButton { + text: "Management" + font.pixelSize: 20 + height: parent.height + } } } diff --git a/app/main.qrc b/app/main.qrc deleted file mode 100644 index c9f6449..0000000 --- a/app/main.qrc +++ /dev/null @@ -1,7 +0,0 @@ -<RCC> - <qresource prefix="/"> - <file>main.qml</file> - <file>images/black_normal.png</file> - <file>images/black_split.png</file> - </qresource> -</RCC> diff --git a/app/pages/DetailPage.qml b/app/pages/DetailPage.qml new file mode 100644 index 0000000..043714d --- /dev/null +++ b/app/pages/DetailPage.qml @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2018 The Qt Company Ltd. + * + * 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. + */ + +import QtQuick 2.6 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.0 + + Page { + id: detailpage + visible: true + + property StackView stack: null + property var model + + Item { + id: backItem + width: 80 + height: 40 + + Image { + anchors.fill: parent + source: 'qrc:/images/Back.svg' + } + + MouseArea { + anchors.fill: parent + onClicked: { + stack.pop(StackView.Immediate) + } + } + } + + RowLayout { + id: infoLayout + + height: parent.width / 3 + width: parent.width + anchors.top: backItem.bottom + anchors.topMargin: 10 + + spacing: 20 + + Item { + Layout.preferredWidth: 200 + Layout.preferredHeight: 200 + Image { + anchors.fill: parent + source: model.icon + } + } + + ColumnLayout { + spacing: 5 + Label { + Layout.fillWidth: true + text: model.name.toUpperCase() + font.pixelSize: 32 + color: '#00ADDC' + } + Label { + text: 'Author: ' + model.author + font.pixelSize: 24 + } + Label { + text: 'Version: ' + model.version + font.pixelSize: 16 + font.italic: true + } + Label { + text: 'Category: ' + model.category + font.pixelSize: 16 + } + Label { + text: 'UpdateTime: ' + new Date(model.createdtime) + font.pixelSize: 16 + } + } + } + + Image { + id: dividingLine + width: parent.width + anchors.top: infoLayout.bottom + anchors.topMargin: 10 + anchors.horizontalCenter: parent.horizontalCenter + source: 'qrc:/images/DividingLine.svg' + } + + Label { + width: parent.width + anchors.top: dividingLine.bottom + anchors.topMargin: 20 + + text: model.description + wrapMode: Text.Wrap + font.pixelSize: 40 + } + }
\ No newline at end of file diff --git a/app/pages/DownloadBar.qml b/app/pages/DownloadBar.qml new file mode 100644 index 0000000..38139e6 --- /dev/null +++ b/app/pages/DownloadBar.qml @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 The Qt Company Ltd. + * + * 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. + */ + +import QtQuick 2.6 +import QtQuick.Controls 2.0 + +ProgressBar { + property real progress: 0 + + id: progressbar + z: -1 + value: (progress/100) + + background: Rectangle { + anchors.fill: parent + color: "transparent" + opacity: 0 + } + + contentItem: Item { + anchors.fill: parent + + Rectangle { + width: progressbar.value * parent.width + height: parent.height + opacity: 0.3 + color: "#32d0eb" + } + } +}
\ No newline at end of file diff --git a/app/pages/ListPage.qml b/app/pages/ListPage.qml new file mode 100644 index 0000000..f4b78da --- /dev/null +++ b/app/pages/ListPage.qml @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2018 The Qt Company Ltd. + * + * 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. + */ + +import QtQuick 2.6 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.0 + +Page { + id: root + property alias model: listView.model + + property int appSize: 0 + property int pageSize: 0 + property int currentPageIndex: 0 + + property int nPullHeight: 100 + + BusyIndicator { + id: prevBusyIndicator + anchors.horizontalCenter: parent.horizontalCenter + implicitWidth: 60 + implicitHeight: 60 + running: false + } + + StackView { + id: stack + initialItem: listView + anchors.fill: parent + anchors.margins: root.width * 0.075 + } + + ListView { + id: listView + //anchors.fill: parent + //anchors.margins: root.width * 0.075 + clip: true + + delegate: MouseArea { + id: delegate + width: listView.width + height: width / 6 + + RowLayout { + anchors.fill: parent + + Item { + Layout.preferredWidth: 100 + Layout.preferredHeight: 100 + + MouseArea{ + anchors.fill: parent + z: 2 + onClicked: { + stack.push("qrc:/pages/DetailPage.qml", {stack: stack, model: model}, StackView.Immediate) + } + } + Image { + anchors.fill: parent + source: model.icon + } + } + ColumnLayout { + Label { + Layout.fillWidth: true + text: model.name.toUpperCase() + color: '#00ADDC' + } + Label { + text: 'Version: ' + model.version + font.pixelSize: 16 + font.italic: true + } + Label { + text: 'Description: ' + model.description + font.pixelSize: 16 + wrapMode: Text.Wrap + elide: Text.ElideRight + Layout.preferredWidth: 400 + Layout.preferredHeight: 40 + } + } + ColumnLayout { + spacing: 5 + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + + Button { + x: x - 6 + text: model.statetext + onClicked: { + if (model.statetext === 'Install' || model.statetext === 'Update') { + listView.model.install(model.index) + } else if (model.statetext === 'Launch') { + if (listView.model.launch(model.id) > 1) { + homescreenHandler.tapShortcut(model.name) + } else { + console.warn('app cannot be launched') + } + } + } + implicitWidth: 140 + implicitHeight: 40 + //Layout.rightMargin: 5 + } + } + + } + DownloadBar { + anchors.fill: parent + progress: model.progress + } + Image { + source: 'qrc:/images/DividingLine.svg' + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + visible: model.index > 0 + } + + + } + + states: [ + State { + name: "prevPageState"; when: listView.contentY < -nPullHeight + StateChangeScript { + name: "prevPageScript" + script: getPrevPage() + } + } + ] + } + + function getPrevPage() { + listView.y = nPullHeight; + + currentPageIndex = currentPageIndex > 0 ? currentPageIndex-1 : 0; + prevBusyIndicator.running = true + prevPageTimer.start(); + } + + Timer { + id: prevPageTimer + interval: 1000 + repeat: false + running: false + onTriggered: { + listView.model.getPrevPage(currentPageIndex) + prevPageAnimation.start(); + } + } + + NumberAnimation { + id: prevPageAnimation + target: listView + property: "y" + duration: 100 + from: nPullHeight + to: 0 + onStopped: { + prevBusyIndicator.running = false + listView.y = 0; + } + } + + Connections { + target: listView.model + onRequestCompleted: { + appSize = appsize + pageSize = pagesize + console.log("request completed!", appSize, pageSize) + } + } +} diff --git a/app/pages/ManagementPage.qml b/app/pages/ManagementPage.qml new file mode 100644 index 0000000..9a4cf31 --- /dev/null +++ b/app/pages/ManagementPage.qml @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2018 The Qt Company Ltd. + * + * 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. + */ + +import QtQuick 2.6 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.0 + +Page { + id: root + property alias model: listView.model + + property int nPullHeight: 300 + + BusyIndicator { + id: busyIndicator + anchors.horizontalCenter: parent.horizontalCenter + implicitWidth: 60 + implicitHeight: 60 + running: false + } + + ListView { + id: listView + anchors.fill: parent + anchors.margins: root.width * 0.075 + clip: true + + delegate: MouseArea { + id: delegate + width: listView.width + height: width / 6 + RowLayout { + anchors.fill: parent + Item { + Layout.preferredWidth: 100 + Layout.preferredHeight: 100 + Image { + anchors.fill: parent + source: 'file://' + model.icon + } + } + ColumnLayout { + Label { + Layout.fillWidth: true + text: model.name.toUpperCase() + color: '#00ADDC' + } + Label { + text: 'Version: ' + model.version + font.pixelSize: 16 + font.italic: true + } + Label { + text: 'Description: ' + model.description + font.pixelSize: 16 + wrapMode: Text.Wrap + elide: Text.ElideRight + Layout.preferredWidth: 400 + Layout.preferredHeight: 40 + } + } + ColumnLayout { + spacing: 5 + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + + Button { + x: x - 6 + text: 'Launch' + onClicked: { + if (listView.model.launch(model.id) > 1) { + homescreenHandler.tapShortcut(model.name) + } else { + console.warn('app cannot be launched') + } + } + implicitWidth: 140 + implicitHeight: 40 + } + Button { + x: x - 6 + visible: model.name.toUpperCase() != "HOMESCREEN" && model.name.toUpperCase() != "LAUNCHER" + text: 'Uninstall' + onClicked: { + listView.model.uninstall(model.index) + } + implicitWidth: 140 + implicitHeight: 40 + } + } + } + Image { + source: 'qrc:/images/DividingLine.svg' + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + visible: model.index > 0 + } + + } + + states: [ + State { + name: "refreshState"; when: listView.contentY < -nPullHeight + StateChangeScript { + name: "refreshScript" + script: refreshModel() + } + } + ] + } + + function refreshModel() { + listView.y = nPullHeight; + + busyIndicator.running = true + refreshTimer.start(); + } + + Timer { + id: refreshTimer + interval: 1000 + repeat: false + running: false + onTriggered: { + listView.model.refresh() + refreshAnimation.start(); + } + } + + NumberAnimation { + id: refreshAnimation + target: listView + property: "y" + duration: 100 + from: nPullHeight + to: 0 + onStopped: { + busyIndicator.running = false + listView.y = 0; + } + } + +} + diff --git a/app/pages/SearchPage.qml b/app/pages/SearchPage.qml new file mode 100644 index 0000000..1d495b4 --- /dev/null +++ b/app/pages/SearchPage.qml @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2018 The Qt Company Ltd. + * + * 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. + */ + +import QtQuick 2.6 +import QtQuick.Controls 2.0 + +Page { + id: root + + title: 'SearchPage' +} diff --git a/app/pkg-config_wrapper.sh b/app/pkg-config_wrapper.sh deleted file mode 100644 index 02ed39b..0000000 --- a/app/pkg-config_wrapper.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh -PKG_CONFIG_SYSROOT_DIR=/opt/sdk/sysroots/aarch64-agl-linux -export PKG_CONFIG_SYSROOT_DIR -PKG_CONFIG_LIBDIR=/opt/sdk/sysroots/aarch64-agl-linux/usr/lib/pkgconfig:/opt/sdk/sysroots/aarch64-agl-linux/usr/share/pkgconfig -export PKG_CONFIG_LIBDIR -exec pkg-config "$@" diff --git a/app/resources.qrc b/app/resources.qrc new file mode 100644 index 0000000..bcf707b --- /dev/null +++ b/app/resources.qrc @@ -0,0 +1,10 @@ +<RCC> + <qresource prefix="/"> + <file>main.qml</file> + <file>pages/ListPage.qml</file> + <file>pages/SearchPage.qml</file> + <file>pages/ManagementPage.qml</file> + <file>pages/DownloadBar.qml</file> + <file>pages/DetailPage.qml</file> + </qresource> +</RCC>
\ No newline at end of file diff --git a/app/src/appinfo.cpp b/app/src/appinfo.cpp new file mode 100644 index 0000000..b06dd2f --- /dev/null +++ b/app/src/appinfo.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2016, 2017 Mentor Graphics Development (Deutschland) GmbH + * Copyright (C) 2016 The Qt Company Ltd. + * Copyright (c) 2018 TOYOTA MOTOR CORPORATION + * + * 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. + */ + +#include "appinfo.h" +#include "hmi-debug.h" + +class AppInfo::Private : public QSharedData { + public: + Private(); + Private(const Private& other); + + QString id; + QString version; + int width; + int height; + QString name; + QString description; + QString shortname; + QString author; + QString iconPath; + AppState state; + qreal progress; + + QString serverid; + QString wgtpath; + QString filename; + QString categoryid; + QString categoryname; + QString deviceid; + QString devicename; + double createdtime; +}; + +AppInfo::Private::Private() : width(-1), height(-1) {} + +AppInfo::Private::Private(const Private& other) + : QSharedData(other), + id(other.id), + version(other.version), + width(other.width), + height(other.height), + name(other.name), + description(other.description), + shortname(other.shortname), + author(other.author), + iconPath(other.iconPath), + state(other.state), + progress(other.progress), + serverid(other.serverid), + wgtpath(other.wgtpath), + filename(other.filename), + categoryid(other.categoryid), + categoryname(other.categoryname), + deviceid(other.deviceid), + devicename(other.devicename), + createdtime(other.createdtime) {} + +AppInfo::AppInfo() : d(new Private) {} + +AppInfo::AppInfo(const QString& icon, const QString& name, const QString& id) + : d(new Private) { + d->iconPath = icon; + d->name = name; + d->id = id; +} + +AppInfo::AppInfo(const AppInfo& other) : d(other.d) {} + +AppInfo::~AppInfo() {} + +AppInfo& AppInfo::operator=(const AppInfo& other) { + d = other.d; + return *this; +} + +QString AppInfo::id() const { + return d->id; +} + +QString AppInfo::version() const { + return d->version; +} + +int AppInfo::width() const { + return d->width; +} + +int AppInfo::height() const { + return d->height; +} + +QString AppInfo::name() const { + return d->name; +} + +QString AppInfo::description() const { + return d->description; +} + +QString AppInfo::shortname() const { + return d->shortname; +} + +QString AppInfo::author() const { + return d->author; +} + +QString AppInfo::iconPath() const { + return d->iconPath; +} + +AppInfo::AppState AppInfo::state() const { + return d->state; +} + +qreal AppInfo::progress() const { + return d->progress; +} + +QString AppInfo::serverId() const { + return d->serverid; +} +QString AppInfo::wgtPath() const { + return d->wgtpath; +} +QString AppInfo::fileName() const { + return d->filename; +} + +QString AppInfo::categoryId() const { + return d->categoryid; +} +QString AppInfo::categoryName() const { + return d->categoryname; +} +QString AppInfo::deviceId() const { + return d->deviceid; +} +QString AppInfo::deviceName() const { + return d->devicename; +} +double AppInfo::createdTime() const { + return d->createdtime; +} + +void AppInfo::setState(const AppState state) { + d->state = state; +} + +void AppInfo::setProgress(const qreal progress) { + d->progress = progress; +} + +void AppInfo::read(const QJsonObject& json) { + d->id = json["id"].toString(); + d->version = json["version"].toString(); + d->width = json["width"].toInt(); + d->height = json["height"].toInt(); + d->name = json["name"].toString(); + d->description = json["description"].toString(); + d->shortname = json["shortname"].toString(); + d->author = json["author"].toString(); + d->iconPath = json["icon"].toString(); + d->state = Launch; +} + +void AppInfo::readFromServer(const QJsonObject& json) { + d->name = json["appName"].toString(); + d->description = json["appAbstract"].toString(); + d->version = json["versionName"].toString(); + d->id = json["appIdCustom"].toString() + "@" + d->version; + d->serverid = json["appId"].toString(); + d->wgtpath = json["verFilePath"].toString(); + d->filename = json["verFilePath"].toString().section('/', -1); + d->author = json["developerName"].toString(); + d->categoryid = json["typeId"].toString(); + d->categoryname = json["typeName"].toString(); + d->deviceid = json["appDeviceTypeId"].toString(); + d->devicename = json["appDeviceTypeName"].toString(); + d->createdtime = json["updateDate"].toDouble(); + // d->iconPath = json["icon"].toString(); + d->iconPath = "file:///var/local/lib/afm/applications/launcher/0.1/icon.svg"; + d->state = Install; + d->progress = 0.0; +} diff --git a/app/src/appinfo.h b/app/src/appinfo.h new file mode 100644 index 0000000..052cbc9 --- /dev/null +++ b/app/src/appinfo.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2016, 2017 Mentor Graphics Development (Deutschland) GmbH + * Copyright (C) 2016 The Qt Company Ltd. + * Copyright (c) 2018 TOYOTA MOTOR CORPORATION + * + * 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. + */ + +#ifndef APPINFO_H +#define APPINFO_H + +#include <QtCore/QJsonObject> +#include <QtCore/QObject> +#include <QtCore/QSharedDataPointer> + +class AppInfo { + Q_GADGET + Q_PROPERTY(QString id READ id) + Q_PROPERTY(QString version READ version) + Q_PROPERTY(int width READ width) + Q_PROPERTY(int height READ height) + Q_PROPERTY(QString name READ name) + Q_PROPERTY(QString description READ description) + Q_PROPERTY(QString shortname READ shortname) + Q_PROPERTY(QString author READ author) + Q_PROPERTY(QString iconPath READ iconPath) + Q_PROPERTY(AppState state READ state WRITE setState) + Q_PROPERTY(qreal progress READ progress WRITE setProgress) + Q_PROPERTY(QString serverid READ serverId) + Q_PROPERTY(QString wgtpath READ wgtPath) + Q_PROPERTY(QString filename READ fileName) + Q_PROPERTY(QString categoryid READ categoryId) + Q_PROPERTY(QString categoryname READ categoryName) + Q_PROPERTY(QString deviceid READ deviceId) + Q_PROPERTY(QString devicename READ deviceName) + Q_PROPERTY(double createdtime READ createdTime) + public: + enum AppState { Install = 0, Update, Launch, Downloading, Installing }; + Q_ENUM(AppState) + + AppInfo(); + AppInfo(const QString& icon, const QString& name, const QString& id); + AppInfo(const AppInfo& other); + virtual ~AppInfo(); + AppInfo& operator=(const AppInfo& other); + void swap(AppInfo& other) { qSwap(d, other.d); } + + QString id() const; + QString version() const; + int width() const; + int height() const; + QString name() const; + QString description() const; + QString shortname() const; + QString author() const; + QString iconPath() const; + AppState state() const; + qreal progress() const; + + QString serverId() const; + QString wgtPath() const; + QString fileName() const; + QString categoryId() const; + QString categoryName() const; + QString deviceId() const; + QString deviceName() const; + double createdTime() const; + + void setState(const AppState state); + void setProgress(const qreal progress); + + void read(const QJsonObject& json); + void readFromServer(const QJsonObject& json); + + private: + class Private; + QSharedDataPointer<Private> d; +}; + +Q_DECLARE_SHARED(AppInfo) +Q_DECLARE_METATYPE(AppInfo) +Q_DECLARE_METATYPE(QList<AppInfo>) + +#endif // APPINFO_H diff --git a/app/src/config.h b/app/src/config.h new file mode 100644 index 0000000..0f2d5ed --- /dev/null +++ b/app/src/config.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016 The Qt Company Ltd. + * Copyright (c) 2018 TOYOTA MOTOR CORPORATION + * + * 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. + */ +#ifndef CONFIG_H +#define CONFIG_H + +// server url config +//#define SERVER_DOMAIN "192.168.20.161/webservice" +#define SERVER_DOMAIN "202.7.19.45/webservice" +//#define SERVER_DOMAIN "warehouse.tmc-tokai.jp/webservice" +#define SERVER_BASE_URL "/api/v1/app" +#define SERVER_API_LIST "/collection" + +#define getURL(api) \ + QString("http://%1%2%3").arg(SERVER_DOMAIN, SERVER_BASE_URL, api) +#define getUrlWithPage(api, offset, limit) \ + getURL(api).append( \ + QString("?sort=updateDate&order=desc&offset=%1&limit=%2") \ + .arg(QString::number(offset), QString::number(limit))) + +//#define getWgtUrl(path, typeId, appId) \ +// QString("http://%1%2/file/%3/%4/%5") \ +// .arg(SERVER_DOMAIN, SERVER_BASE_URL, path, typeId, appId) +#define getWgtUrl(path) \ + QString("http://%1%2/file?filePath=%3") \ + .arg(SERVER_DOMAIN, SERVER_BASE_URL, path) + +// server app page config +#define PAGE_SIZE 20 + +#define getDownloadFilePath(filename) QString("/tmp/%1").arg(filename) + +#endif // !CONFIG_H diff --git a/app/src/hmi-debug.h b/app/src/hmi-debug.h new file mode 100644 index 0000000..96bbd1f --- /dev/null +++ b/app/src/hmi-debug.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018 TOYOTA MOTOR CORPORATION + * + * 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. + */ + +#ifndef __HMI_DEBUG_H__ +#define __HMI_DEBUG_H__ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +enum LOG_LEVEL { + LOG_LEVEL_NONE = 0, + LOG_LEVEL_ERROR, + LOG_LEVEL_WARNING, + LOG_LEVEL_NOTICE, + LOG_LEVEL_INFO, + LOG_LEVEL_DEBUG, + LOG_LEVEL_MAX = LOG_LEVEL_DEBUG +}; + +#define __FILENAME__ \ + (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) + +#define HMI_ERROR(prefix, args, ...) \ + _HMI_LOG(LOG_LEVEL_ERROR, __FILENAME__, __FUNCTION__, __LINE__, prefix, \ + args, ##__VA_ARGS__) +#define HMI_WARNING(prefix, args, ...) \ + _HMI_LOG(LOG_LEVEL_WARNING, __FILENAME__, __FUNCTION__, __LINE__, prefix, \ + args, ##__VA_ARGS__) +#define HMI_NOTICE(prefix, args, ...) \ + _HMI_LOG(LOG_LEVEL_NOTICE, __FILENAME__, __FUNCTION__, __LINE__, prefix, \ + args, ##__VA_ARGS__) +#define HMI_INFO(prefix, args, ...) \ + _HMI_LOG(LOG_LEVEL_INFO, __FILENAME__, __FUNCTION__, __LINE__, prefix, args, \ + ##__VA_ARGS__) +#define HMI_DEBUG(prefix, args, ...) \ + _HMI_LOG(LOG_LEVEL_DEBUG, __FILENAME__, __FUNCTION__, __LINE__, prefix, \ + args, ##__VA_ARGS__) + +static char ERROR_FLAG[6][20] = {"NONE", "ERROR", "WARNING", + "NOTICE", "INFO", "DEBUG"}; + +static void _HMI_LOG(enum LOG_LEVEL level, + const char* file, + const char* func, + const int line, + const char* prefix, + const char* log, + ...) { + const int log_level = (getenv("USE_HMI_DEBUG") == NULL) + ? LOG_LEVEL_ERROR + : atoi(getenv("USE_HMI_DEBUG")); + if (log_level < level) { + return; + } + + char* message; + struct timespec tp; + unsigned int time; + + clock_gettime(CLOCK_REALTIME, &tp); + time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000); + + va_list args; + va_start(args, log); + if (log == NULL || vasprintf(&message, log, args) < 0) + message = NULL; + fprintf(stderr, "[%10.3f] [%s %s] [%s, %s(), Line:%d] >>> %s \n", + time / 1000.0, prefix, ERROR_FLAG[level], file, func, line, message); + va_end(args); + free(message); +} + +#endif //__HMI_DEBUG_H__ diff --git a/app/src/httpclient.cpp b/app/src/httpclient.cpp new file mode 100644 index 0000000..490fd79 --- /dev/null +++ b/app/src/httpclient.cpp @@ -0,0 +1,267 @@ + +#include "httpclient.h" + +#include <QDebug> +#include <QFile> +#include <QHash> +#include <QHttpMultiPart> +#include <QHttpPart> +#include <QNetworkAccessManager> +#include <QNetworkReply> +#include <QNetworkRequest> +#include <QUrlQuery> + +class HttpClientPrivate { + public: + HttpClientPrivate(const QString& url); + + QString url; + QUrlQuery params; + QHash<QString, QString> headers; + QNetworkAccessManager* manager; + + bool debug; + + enum HttpMethod { GET, POST, PUT, DELETE }; + + static QNetworkAccessManager* getManager(HttpClientPrivate* d, + bool* internal); + + static QNetworkRequest createRequest(HttpClientPrivate* d, HttpMethod method); + + static void get(HttpClientPrivate* d, + HttpMethod method, + std::function<void(const QString&)> successHandler, + std::function<void(const QString&)> errorHandler, + const char* encoding); + + static QString readReply(QNetworkReply* reply, + const char* encoding = "UTF-8"); + + static void handleFinish(bool debug, + const QString& successMessage, + const QString& errorMessage, + std::function<void(const QString&)> successHandler, + std::function<void(const QString&)> errorHandler, + QNetworkReply* reply, + QNetworkAccessManager* manager); +}; + +HttpClientPrivate::HttpClientPrivate(const QString& url) + : url(url), manager(NULL), debug(false) {} + +HttpClient::HttpClient(const QString& url) : d(new HttpClientPrivate(url)) {} + +HttpClient::~HttpClient() { + delete d; +} + +HttpClient& HttpClient::manager(QNetworkAccessManager* manager) { + d->manager = manager; + return *this; +} + +HttpClient& HttpClient::debug(bool debug) { + d->debug = debug; + return *this; +} + +HttpClient& HttpClient::param(const QString& name, const QString& value) { + d->params.addQueryItem(name, value); + return *this; +} + +HttpClient& HttpClient::header(const QString& header, const QString& value) { + d->headers[header] = value; + return *this; +} + +void HttpClient::get(std::function<void(const QString&)> successHandler, + std::function<void(const QString&)> errorHandler, + const char* encoding) { + HttpClientPrivate::get(d, HttpClientPrivate::GET, successHandler, + errorHandler, encoding); +} + +void HttpClient::download( + const QString& savePath, + std::function<void(const QString&)> successHandler, + std::function<void(const QString&)> errorHandler, + std::function<void(const qint64, const qint64)> progressHandler) { + bool debug = d->debug; + QFile* file = new QFile(savePath); + + if (file->open(QIODevice::WriteOnly)) { + download( + [=](const QByteArray& data) { file->write(data); }, + [=](const QString&) { + file->flush(); + file->close(); + file->deleteLater(); + + if (debug) { + qDebug().noquote() + << QString("download finished, save to: %1").arg(savePath); + } + + if (NULL != successHandler) { + successHandler( + QString("download finished, save to: %1").arg(savePath)); + } + }, + errorHandler, progressHandler); + } else { + if (debug) { + qDebug().noquote() << QString("open file error: %1").arg(savePath); + } + + if (NULL != errorHandler) { + errorHandler(QString("open file error: %1").arg(savePath)); + } + } +} + +void HttpClient::download( + std::function<void(const QByteArray&)> readyRead, + std::function<void(const QString&)> successHandler, + std::function<void(const QString&)> errorHandler, + std::function<void(const qint64, const qint64)> progressHandler) { + bool debug = d->debug; + bool internal; + + QNetworkAccessManager* manager = HttpClientPrivate::getManager(d, &internal); + QNetworkRequest request = + HttpClientPrivate::createRequest(d, HttpClientPrivate::GET); + QNetworkReply* reply = manager->get(request); + + QObject::connect(reply, &QNetworkReply::readyRead, + [=] { readyRead(reply->readAll()); }); + + QObject::connect(reply, &QNetworkReply::finished, [=] { + QString successMessage = "download finished"; + QString errorMessage = reply->errorString(); + HttpClientPrivate::handleFinish(debug, successMessage, errorMessage, + successHandler, errorHandler, reply, + internal ? manager : NULL); + }); + + QObject::connect(reply, &QNetworkReply::downloadProgress, + [=](qint64 bytesReceived, qint64 bytesTotal) { + if (NULL != progressHandler) { + progressHandler(bytesReceived, bytesTotal); + } + }); +} + +void HttpClientPrivate::get(HttpClientPrivate* d, + HttpMethod method, + std::function<void(const QString&)> successHandler, + std::function<void(const QString&)> errorHandler, + const char* encoding) { + bool internal; + + QNetworkAccessManager* manager = HttpClientPrivate::getManager(d, &internal); + QNetworkRequest request = + HttpClientPrivate::createRequest(d, HttpClientPrivate::GET); + QNetworkReply* reply = NULL; + + switch (method) { + case HttpClientPrivate::GET: + reply = manager->get(request); + break; + + default: + break; + } + + QObject::connect(reply, &QNetworkReply::finished, [=] { + QString successMessage = HttpClientPrivate::readReply(reply, encoding); + QString errorMessage = reply->errorString(); + HttpClientPrivate::handleFinish(d->debug, successMessage, errorMessage, + successHandler, errorHandler, reply, + internal ? manager : NULL); + }); +} + +QNetworkAccessManager* HttpClientPrivate::getManager(HttpClientPrivate* d, + bool* internal) { + *internal = d->manager == NULL; + return *internal ? new QNetworkAccessManager() : d->manager; +} + +QNetworkRequest HttpClientPrivate::createRequest(HttpClientPrivate* d, + HttpMethod method) { + if (!d->params.isEmpty()) { + d->url += "?" + d->params.toString(QUrl::FullyEncoded); + } + + if (d->debug) { + qDebug().noquote() << "url:" << d->url; + + QList<QPair<QString, QString> > paramItems = d->params.queryItems(); + for (int i = 0; i < paramItems.size(); ++i) { + QString name = paramItems.at(i).first; + QString value = paramItems.at(i).second; + if (0 == i) { + qDebug().noquote() << QString("params: %1=%2").arg(name).arg(value); + } else { + qDebug().noquote() << QString(" %1=%2").arg(name).arg(value); + } + } + } + + QNetworkRequest request(QUrl(d->url)); + QHashIterator<QString, QString> iter(d->headers); + while (iter.hasNext()) { + iter.next(); + request.setRawHeader(iter.key().toUtf8(), iter.value().toUtf8()); + } + + return request; +} + +QString HttpClientPrivate::readReply(QNetworkReply* reply, + const char* encoding) { + QTextStream in(reply); + QString result; + in.setCodec(encoding); + + while (!in.atEnd()) { + result += in.readLine(); + } + + return result; +} + +void HttpClientPrivate::handleFinish( + bool debug, + const QString& successMessage, + const QString& errorMessage, + std::function<void(const QString&)> successHandler, + std::function<void(const QString&)> errorHandler, + QNetworkReply* reply, + QNetworkAccessManager* manager) { + if (reply->error() == QNetworkReply::NoError) { + if (debug) { + qDebug().noquote() + << QString("request successed: %1").arg(successMessage); + } + + if (NULL != successHandler) { + successHandler(successMessage); + } + } else { + if (debug) { + qDebug().noquote() << QString("request failed: %1").arg(errorMessage); + } + + if (NULL != errorHandler) { + errorHandler(errorMessage); + } + } + + reply->deleteLater(); + if (NULL != manager) { + manager->deleteLater(); + } +} diff --git a/app/src/httpclient.h b/app/src/httpclient.h new file mode 100644 index 0000000..42f002c --- /dev/null +++ b/app/src/httpclient.h @@ -0,0 +1,48 @@ + +#ifndef HTTPCLIENT_H +#define HTTPCLIENT_H + +#include <functional> +#include <QtGlobal> + +class QString; +class QByteArray; +class QNetworkRequest; +class QNetworkReply; +class QNetworkAccessManager; +class HttpClientPrivate; + +class HttpClient { + public: + HttpClient(const QString& url); + ~HttpClient(); + + HttpClient& manager(QNetworkAccessManager* manager); + + HttpClient& debug(bool debug); + + HttpClient& param(const QString& name, const QString& value); + + HttpClient& header(const QString& header, const QString& value); + + void get(std::function<void(const QString&)> successHandler, + std::function<void(const QString&)> errorHandler = NULL, + const char* encoding = "UTF-8"); + + void download( + const QString& savePath, + std::function<void(const QString&)> successHandler = NULL, + std::function<void(const QString&)> errorHandler = NULL, + std::function<void(const qint64, const qint64)> progressHandler = NULL); + + void download( + std::function<void(const QByteArray&)> readyRead, + std::function<void(const QString&)> successHandler = NULL, + std::function<void(const QString&)> errorHandler = NULL, + std::function<void(const qint64, const qint64)> progressHandler = NULL); + + private: + HttpClientPrivate* d; +}; + +#endif // !HTTPCLIENT_H diff --git a/app/src/main.cpp b/app/src/main.cpp new file mode 100644 index 0000000..6dc6822 --- /dev/null +++ b/app/src/main.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2018 The Qt Company Ltd. + * + * 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. + */ + +#include <QtQml/qqml.h> +#include <qlibwindowmanager.h> +#include <QQuickWindow> +#include <QtCore/QCommandLineParser> +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QStandardPaths> +#include <QtCore/QUrlQuery> +#include <QtGui/QGuiApplication> +#include <QtQml/QQmlApplicationEngine> +#include <QtQml/QQmlContext> +#include <QtQuickControls2/QQuickStyle> +#include "afm_user_daemon_proxy.h" +#include "nativeappmodel.h" +#include "qlibhomescreen.h" +#include "serverappmodel.h" + +org::AGL::afm::user* afm_user_daemon_proxy; + +namespace { + +struct Cleanup { + static inline void cleanup(org::AGL::afm::user* p) { + delete p; + afm_user_daemon_proxy = Q_NULLPTR; + } +}; + +void noOutput(QtMsgType, const QMessageLogContext&, const QString&) {} + +} // namespace + +int main(int argc, char* argv[]) { + QString role = QString("warehouse"); + QGuiApplication app(argc, argv); + + // use launch process + QScopedPointer<org::AGL::afm::user, Cleanup> afm_user_daemon_proxy( + new org::AGL::afm::user("org.AGL.afm.user", "/org/AGL/afm/user", + QDBusConnection::sessionBus(), 0)); + ::afm_user_daemon_proxy = afm_user_daemon_proxy.data(); + + app.setApplicationName("warehouse"); + + QQuickStyle::setStyle("AGL"); + + QQmlApplicationEngine engine; + QQmlContext* context = engine.rootContext(); + + QCommandLineParser parser; + parser.addPositionalArgument("port", + app.translate("main", "port for binding")); + parser.addPositionalArgument("secret", + app.translate("main", "secret for binding")); + parser.addHelpOption(); + parser.addVersionOption(); + parser.process(app); + QStringList positionalArguments = parser.positionalArguments(); + + if (positionalArguments.length() == 2) { + int port = positionalArguments.takeFirst().toInt(); + QString secret = positionalArguments.takeFirst(); + QUrl bindingAddress; + bindingAddress.setScheme(QStringLiteral("ws")); + bindingAddress.setHost(QStringLiteral("localhost")); + bindingAddress.setPort(port); + bindingAddress.setPath(QStringLiteral("/api")); + QUrlQuery query; + query.addQueryItem(QStringLiteral("token"), secret); + bindingAddress.setQuery(query); + + std::string token = secret.toStdString(); + + // import C++ class to QML + qmlRegisterType<NativeAppModel>("NativeAppModel", 1, 0, "NativeAppModel"); + qmlRegisterType<ServerAppModel>("ServerAppModel", 1, 0, "ServerAppModel"); + + QLibHomeScreen* homescreenHandler = new QLibHomeScreen(); + QLibWindowmanager* qwm = new QLibWindowmanager(); + + // WindowManager + if (qwm->init(port, secret) != 0) { + exit(EXIT_FAILURE); + } + + AGLScreenInfo screenInfo(qwm->get_scale_factor()); + + // Request a surface as described in layers.json windowmanager’s file + if (qwm->requestSurface(role) != 0) { + exit(EXIT_FAILURE); + } + + // Create an event callback against an event type. Here a lambda is + // called when SyncDraw event occurs + qwm->set_event_handler(QLibWindowmanager::Event_SyncDraw, + [qwm, role](json_object* object) { + fprintf(stderr, "Surface got syncDraw!\n"); + + qwm->endDraw(role); + }); + + // HomeScreen + homescreenHandler->init(port, token.c_str()); + // Set the event handler for Event_TapShortcut which will activate the + // surface for windowmanager + homescreenHandler->set_event_handler( + QLibHomeScreen::Event_TapShortcut, [qwm, role](json_object* object) { + qDebug("Surface warehouse got tapShortcut.\n"); + qwm->activateWindow(role); + }); + + context->setContextProperty(QStringLiteral("homescreenHandler"), + homescreenHandler); + context->setContextProperty(QStringLiteral("screenInfo"), &screenInfo); + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + QObject* root = engine.rootObjects().first(); + + QQuickWindow* window = qobject_cast<QQuickWindow*>(root); + QObject::connect(window, SIGNAL(frameSwapped()), qwm, + SLOT(slotActivateSurface())); + } + return app.exec(); +} diff --git a/app/src/nativeappmodel.cpp b/app/src/nativeappmodel.cpp new file mode 100644 index 0000000..a5fa42d --- /dev/null +++ b/app/src/nativeappmodel.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2016 The Qt Company Ltd. + * Copyright (C) 2016, 2017 Mentor Graphics Development (Deutschland) GmbH + * Copyright (c) 2018 TOYOTA MOTOR CORPORATION + * + * 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. + */ + +#include "nativeappmodel.h" +#include <QtDBus/QDBusInterface> +#include <QtDBus/QDBusReply> +#include "afm_user_daemon_proxy.h" +#include "httpclient.h" + +#include "hmi-debug.h" + +extern org::AGL::afm::user* afm_user_daemon_proxy; + +class NativeAppModel::Private { + public: + Private(); + + void getApps(); + + QList<AppInfo> data; +}; + +NativeAppModel::Private::Private() { + // this->getApps(); +} + +void NativeAppModel::Private::getApps() { + QString apps = afm_user_daemon_proxy->runnables(QStringLiteral("")); + QJsonDocument japps = QJsonDocument::fromJson(apps.toUtf8()); + for (auto const& app : japps.array()) { + QJsonObject const& jso = app.toObject(); + + AppInfo appinfo; + appinfo.read(jso); + + this->data.append(appinfo); + } +} + +NativeAppModel::NativeAppModel(QObject* parent) + : QAbstractListModel(parent), d(new Private()) { + connect(afm_user_daemon_proxy, &org::AGL::afm::user::changed, this, + &NativeAppModel::appChanged); +} + +NativeAppModel::~NativeAppModel() { + delete this->d; +} + +int NativeAppModel::rowCount(const QModelIndex& parent) const { + if (parent.isValid()) + return 0; + + return this->d->data.count(); +} + +QVariant NativeAppModel::data(const QModelIndex& index, int role) const { + QVariant ret; + if (!index.isValid()) + return ret; + + switch (role) { + case IconRole: + ret = this->d->data[index.row()].iconPath(); + break; + case NameRole: + ret = this->d->data[index.row()].name(); + break; + case IdRole: + ret = this->d->data[index.row()].id(); + break; + case VersionRole: + ret = this->d->data[index.row()].version(); + break; + case DescriptionRole: + ret = this->d->data[index.row()].description(); + break; + case ShortNameRole: + ret = this->d->data[index.row()].shortname(); + break; + case AuthorRole: + ret = this->d->data[index.row()].author(); + break; + default: + break; + } + + return ret; +} + +QHash<int, QByteArray> NativeAppModel::roleNames() const { + QHash<int, QByteArray> roles; + roles[IconRole] = "icon"; + roles[NameRole] = "name"; + roles[IdRole] = "id"; + roles[VersionRole] = "version"; + roles[DescriptionRole] = "description"; + roles[ShortNameRole] = "shortname"; + roles[AuthorRole] = "author"; + return roles; +} + +QString NativeAppModel::id(int i) const { + return data(index(i), IdRole).toString(); +} + +QString NativeAppModel::name(int i) const { + return data(index(i), NameRole).toString(); +} + +void NativeAppModel::appChanged(const QString& info) { + this->refresh(); +} + +int NativeAppModel::launch(const QString& application) { + int result = -1; + HMI_DEBUG("launch", "ApplicationLauncher launch %s.", + application.toStdString().c_str()); + + result = afm_user_daemon_proxy->start(application).value().toInt(); + HMI_DEBUG("launch", "ApplicationLauncher pid: %d.", result); + + return result; +} + +void NativeAppModel::uninstall(int index) { + const QString& id = this->d->data[index].id(); + QString result = afm_user_daemon_proxy->uninstall(id); + if (result == "null") { + beginRemoveRows(QModelIndex(), index, index); + this->d->data.removeAt(index); + endRemoveRows(); + } +} + +void NativeAppModel::refresh() { + beginResetModel(); + this->d->data.clear(); + this->d->getApps(); + endResetModel(); + emit applistChanged(this->d->data); +} diff --git a/app/src/nativeappmodel.h b/app/src/nativeappmodel.h new file mode 100644 index 0000000..f49121a --- /dev/null +++ b/app/src/nativeappmodel.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016 The Qt Company Ltd. + * Copyright (c) 2018 TOYOTA MOTOR CORPORATION + * + * 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. + */ + +#ifndef NATIVEAPPMODE_H +#define NATIVEAPPMODE_H + +#include <QtCore/QAbstractListModel> +#include "appinfo.h" + +class NativeAppModel : public QAbstractListModel { + Q_OBJECT + + public: + enum ModelRole { + IconRole = Qt::DisplayRole, + NameRole = Qt::DecorationRole, + IdRole = Qt::UserRole, + VersionRole, + DescriptionRole, + ShortNameRole, + AuthorRole + }; + Q_ENUM(ModelRole) + + explicit NativeAppModel(QObject* parent = nullptr); + ~NativeAppModel(); + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + + QVariant data(const QModelIndex& index, + int role = Qt::DisplayRole) const override; + QHash<int, QByteArray> roleNames() const override; + Q_INVOKABLE QString id(int index) const; + Q_INVOKABLE QString name(int index) const; + Q_INVOKABLE int launch(const QString& application); + Q_INVOKABLE void uninstall(int index); + Q_INVOKABLE void refresh(); + + void appChanged(const QString& info); + + signals: + void applistChanged(const QList<AppInfo>& applist); + + private: + class Private; + Private* d; +}; + +#endif // NATIVEAPPMODE_H diff --git a/app/src/serverappmodel.cpp b/app/src/serverappmodel.cpp new file mode 100644 index 0000000..ad79159 --- /dev/null +++ b/app/src/serverappmodel.cpp @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2016 The Qt Company Ltd. + * Copyright (C) 2016, 2017 Mentor Graphics Development (Deutschland) GmbH + * Copyright (c) 2018 TOYOTA MOTOR CORPORATION + * + * 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. + */ + +#include "serverappmodel.h" +#include <QtCore/QJsonArray> +#include <QtCore/QJsonDocument> +#include "afm_user_daemon_proxy.h" +#include "httpclient.h" + +#include "hmi-debug.h" + +extern org::AGL::afm::user* afm_user_daemon_proxy; + +ServerAppModel::ServerAppModel(QObject* parent) : QAbstractListModel(parent) { + // this->getAppPage(0, PAGE_SIZE); + connect(afm_user_daemon_proxy, &org::AGL::afm::user::changed, this, + &ServerAppModel::appChanged); +} + +ServerAppModel::~ServerAppModel() {} + +int ServerAppModel::rowCount(const QModelIndex& parent) const { + if (parent.isValid()) + return 0; + + return this->applist.count(); +} + +QVariant ServerAppModel::data(const QModelIndex& index, int role) const { + QVariant ret; + if (!index.isValid()) + return ret; + + switch (role) { + case IconRole: + ret = this->applist[index.row()].iconPath(); + break; + case NameRole: + ret = this->applist[index.row()].name(); + break; + case IdRole: + ret = this->applist[index.row()].id(); + break; + case VersionRole: + ret = this->applist[index.row()].version(); + break; + case DescriptionRole: + ret = this->applist[index.row()].description(); + break; + case AuthorRole: + ret = this->applist[index.row()].author(); + break; + case ServerIdRole: + ret = this->applist[index.row()].serverId(); + break; + case CategoryNameRole: + ret = this->applist[index.row()].categoryName(); + break; + case CreatedTimeRole: + ret = this->applist[index.row()].createdTime(); + break; + case StateRole: + ret = this->applist[index.row()].state(); + break; + case StateTextRole: + switch (this->applist[index.row()].state()) { + case AppInfo::Install: + ret = "Install"; + break; + case AppInfo::Update: + ret = "Update"; + break; + case AppInfo::Launch: + ret = "Launch"; + break; + case AppInfo::Downloading: + ret = "Downloading"; + break; + case AppInfo::Installing: + ret = "Installing"; + break; + default: + break; + } + break; + case ProgressRole: + ret = this->applist[index.row()].progress(); + break; + default: + break; + } + + return ret; +} + +QHash<int, QByteArray> ServerAppModel::roleNames() const { + QHash<int, QByteArray> roles; + roles[IconRole] = "icon"; + roles[NameRole] = "name"; + roles[IdRole] = "id"; + roles[VersionRole] = "version"; + roles[DescriptionRole] = "description"; + roles[AuthorRole] = "author"; + roles[ServerIdRole] = "appid"; + roles[CategoryNameRole] = "category"; + roles[CreatedTimeRole] = "createdtime"; + roles[StateRole] = "state"; + roles[StateTextRole] = "statetext"; + roles[ProgressRole] = "progress"; + return roles; +} + +QString ServerAppModel::id(int i) const { + return data(index(i), IdRole).toString(); +} + +QString ServerAppModel::name(int i) const { + return data(index(i), NameRole).toString(); +} + +QString ServerAppModel::stateText(int i) const { + return data(index(i), StateTextRole).toString(); +} + +int ServerAppModel::launch(const QString& application) { + int result = -1; + HMI_DEBUG("launch", "ApplicationLauncher launch %s.", + application.toStdString().c_str()); + + result = afm_user_daemon_proxy->start(application).value().toInt(); + HMI_DEBUG("launch", "ApplicationLauncher pid: %d.", result); + + return result; +} + +void ServerAppModel::appChanged(const QString& info) { + QJsonDocument japps = QJsonDocument::fromJson(info.toUtf8()); + QJsonObject const& jso = japps.object(); + QString operation = jso["operation"].toString(); + QString id = jso["data"].toString(); + if (operation == "uninstall") { + for (int i = 0; i < applist.size(); ++i) { + if (applist.at(i).id() == id) { + beginResetModel(); + applist[i].setState(AppInfo::Install); + endResetModel(); + break; + } + } + } +} + +void ServerAppModel::install(int index) { + AppInfo& appinfo = applist[index]; + + beginResetModel(); + appinfo.setState(AppInfo::Downloading); + endResetModel(); + + QString url = + getWgtUrl(appinfo.wgtPath()); + HttpClient client(url); + + client.download( + getDownloadFilePath(appinfo.fileName()), + [this, &appinfo](const QString& result) { + this->beginResetModel(); + appinfo.setState(AppInfo::Installing); + this->endResetModel(); + + QTimer::singleShot(3000, this, [this, &appinfo] { + QString installResult = afm_user_daemon_proxy->install( + getDownloadFilePath(appinfo.fileName())); + + this->beginResetModel(); + appinfo.setState(installResult.isEmpty() ? AppInfo::Install + : AppInfo::Launch); + appinfo.setProgress(0.0); + this->endResetModel(); + }); + }, + [this, &appinfo](const QString& error) { + HMI_ERROR("ERROR", "%s", error.toStdString().c_str()); + this->beginResetModel(); + appinfo.setState(AppInfo::Install); + appinfo.setProgress(0.0); + this->beginResetModel(); + }, + [this, &appinfo](const qint64 bytesReceived, const qint64 bytesTotal) { + qreal progressValue = ((qreal)bytesReceived / bytesTotal) * 100; + this->beginResetModel(); + appinfo.setProgress(progressValue); + this->endResetModel(); + }); +} + +void ServerAppModel::getPrevPage(int pageIndex) { + this->getAppPage(pageIndex, PAGE_SIZE, true); +} + +void ServerAppModel::getNextPage(int pageIndex) { + this->getAppPage(pageIndex, PAGE_SIZE); +} + +void ServerAppModel::setNativeApplist(const QList<AppInfo>& applist) { + nativeApplist.clear(); + nativeApplist = applist; + checkAppState(); +} + +void ServerAppModel::checkAppState() { + if (applist.isEmpty() || nativeApplist.isEmpty()) { + return; + } + + beginResetModel(); + + QList<AppInfo>::iterator it; + for (it = applist.begin(); it != applist.end(); ++it) { + QList<AppInfo>::iterator nit; + for (nit = nativeApplist.begin(); nit != nativeApplist.end(); ++nit) { + if ((*it).id() == (*nit).id()) { + (*it).setState((*it).version() != (*nit).version() ? AppInfo::Update + : AppInfo::Launch); + break; + } + } + } + + endResetModel(); +} + +void ServerAppModel::getAppPage(int pageIndex, int pageSize, bool isPrev) { + // QString url = getUrlWithPage(SERVER_API_LIST, pageIndex, pageSize) + // .append(QString("&appDeviceTypeId=%1&appDeveloper=%2") + // .arg(QString::number(0), "zhang_xu")); + QString url = + getUrlWithPage(SERVER_API_LIST, pageIndex, pageSize) + .append(QString("&appDeviceTypeId=%1").arg(QString::number(0))); + HttpClient client(url); + + client.get( + [=](const QString& result) { + QJsonDocument jresult = QJsonDocument::fromJson(result.toUtf8()); + QJsonObject const& jro = jresult.object(); + QJsonArray const& jappList = jro["pagination_data"].toArray(); + + QList<AppInfo> newList; + for (auto const& app : jappList) { + QJsonObject const& jso = app.toObject(); + + AppInfo appinfo; + appinfo.readFromServer(jso); + newList.append(appinfo); + } + + beginResetModel(); + if (isPrev || jappList.size() >= PAGE_SIZE) { + applist.clear(); + } + applist += newList; + endResetModel(); + + checkAppState(); + + emit requestCompleted(jappList.size(), PAGE_SIZE); + }, + [=](const QString& msg) { + HMI_ERROR("response", "response error: %s", msg.toStdString().c_str()); + }); +} diff --git a/app/src/serverappmodel.h b/app/src/serverappmodel.h new file mode 100644 index 0000000..ea3608f --- /dev/null +++ b/app/src/serverappmodel.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016 The Qt Company Ltd. + * Copyright (c) 2018 TOYOTA MOTOR CORPORATION + * + * 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. + */ + +#ifndef SERVERAPPMODEL_H +#define SERVERAPPMODEL_H + +#include <QtCore/QAbstractListModel> +#include "appinfo.h" +#include "config.h" + +class ServerAppModel : public QAbstractListModel { + Q_OBJECT + + public: + enum ModelRole { + IconRole = Qt::DisplayRole, + NameRole = Qt::DecorationRole, + IdRole = Qt::UserRole, + VersionRole, + DescriptionRole, + AuthorRole, + ServerIdRole, + CategoryNameRole, + CreatedTimeRole, + StateRole, + StateTextRole, + ProgressRole + }; + Q_ENUM(ModelRole) + + explicit ServerAppModel(QObject* parent = nullptr); + ~ServerAppModel(); + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + + QVariant data(const QModelIndex& index, + int role = Qt::DisplayRole) const override; + QHash<int, QByteArray> roleNames() const override; + Q_INVOKABLE QString id(int index) const; + Q_INVOKABLE QString name(int index) const; + Q_INVOKABLE QString stateText(int index) const; + Q_INVOKABLE void install(int index); + Q_INVOKABLE int launch(const QString& application); + + Q_INVOKABLE void getPrevPage(int pageIndex); + Q_INVOKABLE void getNextPage(int pageIndex); + + Q_INVOKABLE void setNativeApplist(const QList<AppInfo>& applist); + + void getAppPage(int pageIndex, int pageSize, bool isPrev = false); + + void appChanged(const QString& info); + + signals: + void requestCompleted(int appsize, int pagesize); + + private: + void checkAppState(); + + QList<AppInfo> applist; + QList<AppInfo> nativeApplist; +}; + +#endif // SERVERAPPMODEL_H diff --git a/app/src/src.pri b/app/src/src.pri new file mode 100644 index 0000000..455ded3 --- /dev/null +++ b/app/src/src.pri @@ -0,0 +1,17 @@ + +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/nativeappmodel.h \ + $$PWD/serverappmodel.h \ + $$PWD/appinfo.h \ + $$PWD/hmi-debug.h \ + $$PWD/httpclient.h \ + $$PWD/config.h + +SOURCES += \ + $$PWD/main.cpp \ + $$PWD/nativeappmodel.cpp \ + $$PWD/serverappmodel.cpp \ + $$PWD/appinfo.cpp \ + $$PWD/httpclient.cpp |