summaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/app.pri13
-rw-r--r--app/app.pro25
-rw-r--r--app/images/Back.svg51
-rw-r--r--app/images/DividingLine.svg58
-rw-r--r--app/images/Top.svg54
-rw-r--r--app/images/black_normal.pngbin9809 -> 0 bytes
-rw-r--r--app/images/black_split.pngbin5011 -> 0 bytes
-rw-r--r--app/images/images.qrc7
-rw-r--r--app/main.cpp107
-rw-r--r--app/main.qml121
-rw-r--r--app/main.qrc7
-rw-r--r--app/pages/DetailPage.qml111
-rw-r--r--app/pages/DownloadBar.qml43
-rw-r--r--app/pages/ListPage.qml186
-rw-r--r--app/pages/ManagementPage.qml156
-rw-r--r--app/pages/SearchPage.qml24
-rw-r--r--app/pkg-config_wrapper.sh6
-rw-r--r--app/resources.qrc10
-rw-r--r--app/src/appinfo.cpp200
-rw-r--r--app/src/appinfo.h94
-rw-r--r--app/src/config.h46
-rw-r--r--app/src/hmi-debug.h89
-rw-r--r--app/src/httpclient.cpp267
-rw-r--r--app/src/httpclient.h48
-rw-r--r--app/src/main.cpp139
-rw-r--r--app/src/nativeappmodel.cpp157
-rw-r--r--app/src/nativeappmodel.h63
-rw-r--r--app/src/serverappmodel.cpp286
-rw-r--r--app/src/serverappmodel.h78
-rw-r--r--app/src/src.pri17
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="&amp;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
deleted file mode 100644
index 378798a..0000000
--- a/app/images/black_normal.png
+++ /dev/null
Binary files differ
diff --git a/app/images/black_split.png b/app/images/black_split.png
deleted file mode 100644
index 9ce95ed..0000000
--- a/app/images/black_split.png
+++ /dev/null
Binary files differ
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