summaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorzheng_wenlong <wenlong_zheng@nexty-ele.com>2019-04-09 10:48:11 +0900
committerZheng Wenlong <wenlong_zheng@nexty-ele.com>2019-04-10 01:47:42 +0000
commit99812e94289a083e9d297af50f5b227e3ad58f52 (patch)
treed9d43dddf03880e6615fcf4cfdaa02cbee5eff54 /app
Add demo3 warehouse source code for cluster mode. [Patch Set 2] Update LICENSE file. Change-Id: Ib129f18ae67f3913cbf899a2be5eed7beaa65976 BUG-AGL: SPEC-2261 Signed-off-by: zheng_wenlong <wenlong_zheng@nexty-ele.com>
Diffstat (limited to 'app')
-rw-r--r--app/app.pro18
-rw-r--r--app/images/Back.svg51
-rw-r--r--app/images/DividingLine.svg58
-rw-r--r--app/images/HMI_Settings_Button_Cancel.svg36
-rw-r--r--app/images/HMI_Settings_Button_Ok.svg36
-rw-r--r--app/images/Top.svg54
-rw-r--r--app/images/blank.svg151
-rw-r--r--app/images/images.qrc10
-rw-r--r--app/main.qml95
-rw-r--r--app/pages/DImage.qml39
-rw-r--r--app/pages/DetailPage.qml114
-rw-r--r--app/pages/DownloadBar.qml44
-rw-r--r--app/pages/ListPage.qml195
-rw-r--r--app/pages/ManagementPage.qml161
-rw-r--r--app/pages/SButton.qml66
-rw-r--r--app/pages/SearchList.qml333
-rw-r--r--app/pages/SearchPage.qml321
-rw-r--r--app/resources.qrc13
-rw-r--r--app/src/appinfo.cpp219
-rw-r--r--app/src/appinfo.h96
-rw-r--r--app/src/config.h49
-rw-r--r--app/src/hmi-debug.h89
-rw-r--r--app/src/httpclient.cpp281
-rw-r--r--app/src/httpclient.h63
-rw-r--r--app/src/main.cpp147
-rw-r--r--app/src/nativeappmodel.cpp180
-rw-r--r--app/src/nativeappmodel.h63
-rw-r--r--app/src/serverappmodel.cpp339
-rw-r--r--app/src/serverappmodel.h92
-rw-r--r--app/src/src.pri17
30 files changed, 3430 insertions, 0 deletions
diff --git a/app/app.pro b/app/app.pro
new file mode 100644
index 0000000..78b861a
--- /dev/null
+++ b/app/app.pro
@@ -0,0 +1,18 @@
+TEMPLATE = app
+TARGET = warehouse
+QT += quickcontrols2 dbus websockets
+
+DESTDIR = $${OUT_PWD}/../package/root/bin
+
+CONFIG += c++11 link_pkgconfig
+PKGCONFIG += qlibhomescreen qlibwindowmanager
+
+include(../interfaces/interfaces.pri)
+include(src/src.pri)
+
+OTHER_FILES += \
+ main.qml
+
+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/HMI_Settings_Button_Cancel.svg b/app/images/HMI_Settings_Button_Cancel.svg
new file mode 100644
index 0000000..6d234fb
--- /dev/null
+++ b/app/images/HMI_Settings_Button_Cancel.svg
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
+ <!ENTITY ns_extend "http://ns.adobe.com/Extensibility/1.0/">
+ <!ENTITY ns_ai "http://ns.adobe.com/AdobeIllustrator/10.0/">
+ <!ENTITY ns_graphs "http://ns.adobe.com/Graphs/1.0/">
+ <!ENTITY ns_vars "http://ns.adobe.com/Variables/1.0/">
+ <!ENTITY ns_imrep "http://ns.adobe.com/ImageReplacement/1.0/">
+ <!ENTITY ns_sfw "http://ns.adobe.com/SaveForWeb/1.0/">
+ <!ENTITY ns_custom "http://ns.adobe.com/GenericCustomNamespace/1.0/">
+ <!ENTITY ns_adobe_xpath "http://ns.adobe.com/XPath/1.0/">
+]>
+<svg version="1.1" id="Layer_1" xmlns:x="&ns_extend;" xmlns:i="&ns_ai;" xmlns:graph="&ns_graphs;"
+ xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 151 51"
+ style="enable-background:new 0 0 151 51;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:none;stroke:#0DF9FF;stroke-miterlimit:10;}
+ .st1{opacity:0.3;fill:url(#SVGID_1_);stroke:#0DF9FF;stroke-miterlimit:10;}
+ .st2{fill:#FFFFFF;}
+ .st3{font-family:'Roboto-Regular';}
+ .st4{font-size:20px;}
+ .st5{letter-spacing:3;}
+</style>
+<switch>
+ <g i:extraneous="self">
+ <g>
+ <rect x="0.5" y="0.5" class="st0" width="150" height="50"/>
+ <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-25.8746" y1="126.14" x2="190.2191" y2="-88.3878">
+ <stop offset="0" style="stop-color:#00ADDC"/>
+ <stop offset="1" style="stop-color:#6BFBFF"/>
+ </linearGradient>
+ <rect x="0.5" y="0.5" class="st1" width="150" height="50"/>
+ </g>
+ </g>
+</switch>
+</svg>
diff --git a/app/images/HMI_Settings_Button_Ok.svg b/app/images/HMI_Settings_Button_Ok.svg
new file mode 100644
index 0000000..c7831ff
--- /dev/null
+++ b/app/images/HMI_Settings_Button_Ok.svg
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
+ <!ENTITY ns_extend "http://ns.adobe.com/Extensibility/1.0/">
+ <!ENTITY ns_ai "http://ns.adobe.com/AdobeIllustrator/10.0/">
+ <!ENTITY ns_graphs "http://ns.adobe.com/Graphs/1.0/">
+ <!ENTITY ns_vars "http://ns.adobe.com/Variables/1.0/">
+ <!ENTITY ns_imrep "http://ns.adobe.com/ImageReplacement/1.0/">
+ <!ENTITY ns_sfw "http://ns.adobe.com/SaveForWeb/1.0/">
+ <!ENTITY ns_custom "http://ns.adobe.com/GenericCustomNamespace/1.0/">
+ <!ENTITY ns_adobe_xpath "http://ns.adobe.com/XPath/1.0/">
+]>
+<svg version="1.1" id="Layer_1" xmlns:x="&ns_extend;" xmlns:i="&ns_ai;" xmlns:graph="&ns_graphs;"
+ xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 151 51"
+ style="enable-background:new 0 0 151 51;" xml:space="preserve">
+<style type="text/css">
+ .st0{opacity:0.84;fill:url(#SVGID_1_);}
+ .st1{fill:none;stroke:#0DF9FF;stroke-miterlimit:10;}
+ .st2{fill:#27232B;}
+ .st3{font-family:'Roboto-Regular';}
+ .st4{font-size:20px;}
+ .st5{letter-spacing:3;}
+</style>
+<switch>
+ <g i:extraneous="self">
+ <g>
+ <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-25.8746" y1="125.9981" x2="190.2191" y2="-88.5298">
+ <stop offset="0" style="stop-color:#00ADDC"/>
+ <stop offset="1" style="stop-color:#6BFBFF"/>
+ </linearGradient>
+ <rect x="0.5" y="0.4" class="st0" width="150" height="50"/>
+ <rect x="0.5" y="0.4" class="st1" width="150" height="50"/>
+ </g>
+ </g>
+</switch>
+</svg>
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/blank.svg b/app/images/blank.svg
new file mode 100644
index 0000000..d5e3472
--- /dev/null
+++ b/app/images/blank.svg
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<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:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="320"
+ height="320"
+ viewBox="0 0 320 320.00001"
+ id="svg5033"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="bg2.svg">
+ <defs
+ id="defs5035">
+ <linearGradient
+ gradientTransform="matrix(1.0123741,0,0,1.0052855,226.82647,753.70831)"
+ inkscape:collect="always"
+ xlink:href="#SVGID_36_"
+ id="linearGradient5005"
+ x1="-125.27629"
+ y1="217.50423"
+ x2="-4.2735882"
+ y2="57.848095"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ id="SVGID_36_"
+ gradientUnits="userSpaceOnUse"
+ x1="4.0481"
+ y1="287.94919"
+ x2="320.4859"
+ y2="-15.4029"
+ gradientTransform="matrix(1,0.00546456,-0.00546456,1,-2.0192,-3.0212)">
+ <stop
+ offset="0"
+ style="stop-color:#54d187;stop-opacity:0.90588236"
+ id="stop300" />
+ <stop
+ id="stop9348"
+ style="stop-color:#12c1bb;stop-opacity:0.65882355"
+ offset="0.51862437" />
+ <stop
+ offset="1"
+ style="stop-color:#055658;stop-opacity:0.38431373"
+ id="stop302" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient9308"
+ id="linearGradient9314"
+ x1="414.01102"
+ y1="157.36543"
+ x2="484.72171"
+ y2="80.290802"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-562,703.36214)" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient9308">
+ <stop
+ style="stop-color:#55d186;stop-opacity:0.90980393"
+ offset="0"
+ id="stop9310" />
+ <stop
+ id="stop9316"
+ offset="0.50955158"
+ style="stop-color:#12c0bc;stop-opacity:0.65490198" />
+ <stop
+ style="stop-color:#055457;stop-opacity:0.38039216"
+ offset="1"
+ id="stop9312" />
+ </linearGradient>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffa4ff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2"
+ inkscape:cx="49.388499"
+ inkscape:cy="181.12516"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1615"
+ inkscape:window-height="917"
+ inkscape:window-x="65"
+ inkscape:window-y="24"
+ inkscape:window-maximized="1"
+ units="px"
+ showguides="true"
+ inkscape:guide-bbox="true">
+ <sodipodi:guide
+ position="160,377.00001"
+ orientation="1,0"
+ id="guide5666" />
+ <sodipodi:guide
+ position="60,448.00001"
+ orientation="1,0"
+ id="guide5668" />
+ <sodipodi:guide
+ position="260,414.00001"
+ orientation="1,0"
+ id="guide5670" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata5038">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-732.36216)">
+ <path
+ style="fill:#00ffbd;fill-opacity:1;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none"
+ d="M 160.42857,785.99962 A 104.88141,105.22039 0 0 0 55.54771,891.21837 104.88141,105.22039 0 0 0 160.42857,996.43908 104.88141,105.22039 0 0 0 265.30943,891.21837 104.88141,105.22039 0 0 0 160.42857,785.99962 Z m -0.33984,3.38867 A 101.83055,101.66108 0 0 1 261.92076,891.0504 101.83055,101.66108 0 0 1 160.08873,992.71056 101.83055,101.66108 0 0 1 58.25865,891.0504 101.83055,101.66108 0 0 1 160.08873,789.38829 Z"
+ id="path4374"
+ inkscape:connector-curvature="0" />
+ <ellipse
+ style="opacity:0.77;fill:url(#linearGradient5005);fill-opacity:1;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;fill-rule:nonzero"
+ id="path4417"
+ cx="160.24997"
+ cy="891.04053"
+ rx="102.26696"
+ ry="101.89165" />
+ <rect
+ style="opacity:1;fill:url(#linearGradient9314);fill-opacity:1;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none"
+ id="rect9306"
+ width="72"
+ height="78"
+ x="-149"
+ y="783.36218" />
+ </g>
+</svg>
diff --git a/app/images/images.qrc b/app/images/images.qrc
new file mode 100644
index 0000000..a7b97ec
--- /dev/null
+++ b/app/images/images.qrc
@@ -0,0 +1,10 @@
+<RCC>
+ <qresource prefix="/images">
+ <file>DividingLine.svg</file>
+ <file>Back.svg</file>
+ <file>Top.svg</file>
+ <file>blank.svg</file>
+ <file>HMI_Settings_Button_Ok.svg</file>
+ <file>HMI_Settings_Button_Cancel.svg</file>
+ </qresource>
+</RCC>
diff --git a/app/main.qml b/app/main.qml
new file mode 100644
index 0000000..da28271
--- /dev/null
+++ b/app/main.qml
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 The Qt Company Ltd.
+ * Copyright (c) 2018-2019 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.
+ */
+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
+
+ width: 1080 * screenInfo.scale_factor()
+ height: 1487 * screenInfo.scale_factor()
+
+ SwipeView {
+ id: stackLayout
+ anchors.fill: parent
+ currentIndex: tabBar.currentIndex
+
+ ListPage {
+ id: listPage
+
+ model: ServerAppModel {
+ id: listModel
+ }
+ }
+ SearchList {
+ id: searchPage
+
+ model:ServerAppModel {
+ id: searchModel
+ }
+ }
+
+ ManagementPage {
+ id: managementPage
+
+ model: NativeAppModel {
+ id: managementModel
+ }
+ }
+
+ Connections {
+ target: managementPage.model
+ onApplistChanged: {
+ listPage.model.setNativeApplist(applist)
+ searchPage.model.setNativeApplist(applist)
+ }
+ }
+
+ Component.onCompleted: {
+ listPage.model.getPrevPage(0)
+ managementPage.model.refresh()
+ }
+ }
+
+
+ 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: "Installed Application"
+ font.pixelSize: 20
+ height: parent.height
+ }
+ }
+}
diff --git a/app/pages/DImage.qml b/app/pages/DImage.qml
new file mode 100644
index 0000000..dc80585
--- /dev/null
+++ b/app/pages/DImage.qml
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 The Qt Company Ltd.
+ * Copyright (c) 2018-2019 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.
+ */
+
+import QtQuick 2.6
+import QtQuick.Controls 2.0
+
+Image {
+ id: imageicon
+ property string icon: ""
+ property string name: ""
+
+ anchors.fill: parent
+ source: icon != ""?icon : 'qrc:/images/blank.svg'
+ property string initial: name.substring(0,1).toUpperCase()
+
+ Label {
+ style: Text.Outline
+ styleColor: "#66FF99"
+ color: 'transparent'
+ font.pixelSize: parent.height * 0.4
+ anchors.centerIn: parent
+ text: parent.initial
+ visible: icon == ""
+ }
+}
diff --git a/app/pages/DetailPage.qml b/app/pages/DetailPage.qml
new file mode 100644
index 0000000..38b7ac2
--- /dev/null
+++ b/app/pages/DetailPage.qml
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2018 The Qt Company Ltd.
+ * Copyright (c) 2018-2019 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.
+ */
+
+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
+
+ DImage {
+ id: imageicon
+ icon: model.icon
+ name: model.name
+ }
+ }
+
+ 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
+ }
+ }
diff --git a/app/pages/DownloadBar.qml b/app/pages/DownloadBar.qml
new file mode 100644
index 0000000..6ac82ee
--- /dev/null
+++ b/app/pages/DownloadBar.qml
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Qt Company Ltd.
+ * Copyright (c) 2018-2019 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.
+ */
+
+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..d3295ff
--- /dev/null
+++ b/app/pages/ListPage.qml
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2018 The Qt Company Ltd.
+ * Copyright (c) 2018-2019 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.
+ */
+
+import QtQuick 2.6
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.0
+
+Page {
+ id: root
+ property alias model: listView.model
+
+ property int currentPageIndex: 0
+
+ property int nPullHeight: 100
+
+ property int oldContentY: 0
+
+ BusyIndicator {
+ id: prevBusyIndicator
+ anchors.horizontalCenter: parent.horizontalCenter
+ implicitWidth: 60
+ implicitHeight: 60
+ running: false
+ }
+ BusyIndicator {
+ id: nextBusyIndicator
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ 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: 80
+ Layout.preferredHeight: 80
+
+ MouseArea{
+ anchors.fill: parent
+ z: 2
+ onClicked: {
+ stack.push("qrc:/pages/DetailPage.qml", {stack: stack, model: model}, StackView.Immediate)
+ }
+ }
+
+ DImage {
+ id: imageicon
+ icon: model.icon
+ name: model.name
+ }
+ }
+
+ 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 {
+ anchors.right: parent.right
+ anchors.rightMargin: 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
+ }
+ }
+ }
+
+ 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
+ }
+ }
+
+ onMovementStarted: {
+ oldContentY = contentY
+ }
+
+ onFlickStarted: {
+ if (contentY < -nPullHeight && prevBusyIndicator.running == false) {
+ prevBusyIndicator.running = true
+ listView.model.getPrevPage(currentPageIndex)
+ } else if (nextBusyIndicator.running == false) {
+ if ((contentHeight > 0) && (contentY > (contentHeight - height + nPullHeight))) {
+ nextBusyIndicator.running = true
+ listView.model.getNextPage(currentPageIndex)
+ }
+ }
+ }
+ }
+
+ Connections {
+ target: listView.model
+ onRequestCompleted: {
+ if (isPrev) {
+ if (pageIndex > 0 && offsetSize == 0) {
+ listView.model.getPrevPage(pageIndex - 1)
+ return
+ }
+ currentPageIndex = pageIndex
+ prevBusyIndicator.running = false
+ } else {
+ nextBusyIndicator.running = false
+
+ if (listView.contentHeight > listView.height){
+ var itemHeight = listView.contentHeight / listView.count
+
+ if (offsetSize <= 0) {
+ listView.contentY = listView.contentHeight - listView.height
+ } else if (offsetSize < pageSize){
+ var viewCount = listView.height / itemHeight
+
+ if (offsetSize <= (viewCount / 2)) {
+ listView.contentY = oldContentY + (offsetSize * itemHeight)
+ } else {
+ listView.contentY = oldContentY + (listView.height / 2)
+ }
+ } else {
+ currentPageIndex = pageIndex
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/pages/ManagementPage.qml b/app/pages/ManagementPage.qml
new file mode 100644
index 0000000..b6e55ad
--- /dev/null
+++ b/app/pages/ManagementPage.qml
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2018 The Qt Company Ltd.
+ * Copyright (c) 2018-2019 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.
+ */
+
+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
+
+ DImage {
+ id: imageicon
+ icon: model.icon != ""?'file://' + model.icon : ''
+ name: model.name
+ }
+ }
+ 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 {
+ anchors.right: parent.right
+ anchors.rightMargin: 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 {
+ anchors.right: parent.right
+ anchors.rightMargin: 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/SButton.qml b/app/pages/SButton.qml
new file mode 100644
index 0000000..d91a9a4
--- /dev/null
+++ b/app/pages/SButton.qml
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Qt Company Ltd.
+ * Copyright (c) 2018-2019 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.
+ */
+
+import QtQuick 2.6
+import QtQuick.Window 2.0
+import QtQuick.Templates 2.0 as T
+
+T.Button {
+ id: root
+ implicitWidth: background.implicitWidth
+ implicitHeight: background.implicitHeight
+ font.family: 'Roboto'
+ font.pixelSize: Math.min(Screen.width, Screen.height) / 50
+
+ Translate {
+ id: translate
+ }
+
+ contentItem: Text {
+ text: root.text
+ font: root.font
+ opacity: enabled ? 1.0 : 0.3
+ color: 'white'
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ transform: translate
+ }
+
+ background: Image {
+ anchors.fill:parent
+ source: root.highlighted ? 'qrc:/images/HMI_Settings_Button_Ok.svg' : 'qrc:/images/HMI_Settings_Button_Cancel.svg'
+ fillMode: Image.Stretch
+ transform: translate
+ }
+
+ states: [
+ State {
+ name: 'pressed'
+ when: root.pressed
+ PropertyChanges {
+ target: translate
+ x: 3
+ y: 3
+ }
+ PropertyChanges {
+ target: background
+ opacity: 0.75
+ }
+ }
+ ]
+}
diff --git a/app/pages/SearchList.qml b/app/pages/SearchList.qml
new file mode 100644
index 0000000..becb43c
--- /dev/null
+++ b/app/pages/SearchList.qml
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2018 The Qt Company Ltd.
+ * Copyright (c) 2018-2019 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.
+ */
+
+import QtQuick 2.6
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.0
+
+Page {
+ id: root
+
+ property alias model: listView.model
+
+ property int appSearchType: -1
+ property string appSearchKeyword: ""
+ property int appSize: 0
+ property int pageSize: 0
+
+ property int currentPageIndex: 0
+ property int nPullHeight: 100
+
+ property int oldContentY: 0
+
+ BusyIndicator {
+ id: prevBusyIndicator
+ anchors.horizontalCenter: parent.horizontalCenter
+ implicitWidth: 60
+ implicitHeight: 60
+ running: false
+ }
+ BusyIndicator {
+ id: nextBusyIndicator
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ implicitWidth: 60
+ implicitHeight: 60
+ running: false
+ }
+
+ StackView {
+ id: stack
+ initialItem: listPage
+ anchors.fill: parent
+ anchors.leftMargin: root.width * 0.04
+ anchors.rightMargin: root.width * 0.04
+ anchors.topMargin: root.width * 0.04
+ Component.onCompleted: {
+ listView.model.clearList();
+ stack.push("qrc:/pages/SearchPage.qml",
+ {appSearchKeyword: appSearchKeyword,
+ appSearchType: appSearchType,
+ parentPage: root}, StackView.Immediate)
+ console.log("depth=", depth.toString())
+ }
+ }
+ Page {
+ id: listPage
+ anchors.fill: parent
+ RowLayout {
+ id: keywordLayout
+ //height: parent.width / 3
+ width: parent.width
+ //anchors.top: root.top
+ //anchors.topMargin: 10
+
+ spacing: 20
+ ColumnLayout {
+ spacing: 5
+ RowLayout {
+ Label {
+ Layout.fillWidth: true
+ text: qsTr("KeyWord")
+ font.pixelSize: 32
+ font.bold: true
+ color: '#00ADDC'
+ }
+ Item {
+ id: backItem
+ width: 80
+ height: 40
+ Layout.alignment: Qt.AlignRight | Qt.AlignTop
+ Image {
+ //Layout.alignment: Qt.AlignRight | At.AlignVCenter
+ id: backBtn
+ anchors.fill: parent
+ source: 'qrc:/images/Back.svg'
+ }
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ listView.model.clearList();
+ stack.push("qrc:/pages/SearchPage.qml",
+ {appSearchKeyword: appSearchKeyword,
+ appSearchType: appSearchType,
+ parentPage: root}, StackView.Immediate)
+ }
+ }
+ }
+ }
+ RowLayout {
+ width: root.width
+ spacing: 10
+ TextEdit {
+ id: textInputKeyWord
+ Layout.fillWidth: true
+ text: appSearchKeyword == "" ? qsTr("Please input keyword!") : appSearchKeyword
+ font.pixelSize: 32
+ font.bold: true
+ font.wordSpacing: -1
+ font.letterSpacing: 0
+ clip: true
+ font.weight: Font.Normal
+ font.capitalization: Font.MixedCase
+ onFocusChanged: {
+ if(focus === false){
+ if(text === ""){
+ text = qsTr("Please input keyword!")
+ }
+ }
+ }
+ Rectangle {
+ anchors.fill: parent
+ color:"transparent"
+ border.color: "#66FF99"
+ border.width: 2
+ z: -1
+ }
+ }
+ SButton {
+ text: 'X'
+ anchors.rightMargin: 100
+
+ visible: textInputKeyWord.text != ""
+ onClicked: {
+ textInputKeyWord.text = "";
+ textInputKeyWord.forceActiveFocus();
+ }
+ implicitWidth: 40
+ implicitHeight: 40
+ }
+ Item {
+ id: searchBtn
+ width: 140
+ height: 40
+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
+ SButton {
+ anchors.fill:parent
+ enabled: (textInputKeyWord.text != ""
+ && textInputKeyWord.text != "Please input keyword!")
+ text: 'Search'
+ onClicked: {
+ listView.model.clearList();
+ if(textInputKeyWord.text != "Please input keyword!") {
+ appSearchKeyword = textInputKeyWord.text;
+ } else {
+ appSearchKeyword = "";
+ }
+ popBack(appSearchKeyword, appSearchType);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ListView {
+ id: listView
+ width: parent.width
+ height: parent.height - keywordLayout.height
+ anchors.top: keywordLayout.bottom
+
+ clip: true
+
+ delegate: MouseArea {
+ id: delegate
+ width: listView.width
+ height: width / 6
+
+ RowLayout {
+ anchors.fill: parent
+
+ Item {
+ Layout.preferredWidth: 80
+ Layout.preferredHeight: 80
+
+ MouseArea{
+ anchors.fill: parent
+ z: 2
+ onClicked: {
+ stack.push("qrc:/pages/DetailPage.qml", {stack: stack, model: model}, StackView.Immediate)
+ }
+ }
+
+ DImage {
+ id: imageicon
+ icon: model.icon
+ name: model.name
+ }
+ }
+
+ 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 {
+ anchors.right: parent.right
+ anchors.rightMargin: 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
+ }
+ }
+ }
+
+ 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
+ }
+ }
+
+ onMovementStarted: {
+ oldContentY = contentY
+ }
+
+ onFlickStarted: {
+ if (contentY < -nPullHeight && prevBusyIndicator.running == false) {
+ prevBusyIndicator.running = true
+ listView.model.getPrevPage(currentPageIndex)
+ } else if (nextBusyIndicator.running == false) {
+ if ((contentHeight > 0) && (contentY > (contentHeight - height + nPullHeight))) {
+ nextBusyIndicator.running = true
+ listView.model.getNextPage(currentPageIndex)
+ }
+ }
+ }
+ }
+
+ Connections {
+ target: listView.model
+ onRequestCompleted: {
+ if (isPrev) {
+ if (pageIndex > 0 && offsetSize == 0) {
+ listView.model.getPrevPage(pageIndex - 1)
+ return
+ }
+ currentPageIndex = pageIndex
+ prevBusyIndicator.running = false
+ } else {
+ nextBusyIndicator.running = false
+
+ if (listView.contentHeight > listView.height){
+ var itemHeight = listView.contentHeight / listView.count
+
+ if (offsetSize <= 0) {
+ listView.contentY = listView.contentHeight - listView.height
+ } else if (offsetSize < pageSize){
+ var viewCount = listView.height / itemHeight
+
+ if (offsetSize <= (viewCount / 2)) {
+ listView.contentY = oldContentY + (offsetSize * itemHeight)
+ } else {
+ listView.contentY = oldContentY + (listView.height / 2)
+ }
+ } else {
+ currentPageIndex = pageIndex
+ }
+ }
+ }
+ }
+ }
+ }
+
+ function popBack(name, typeid)
+ {
+ appSearchKeyword = name;
+ appSearchType = typeid;
+
+ currentPageIndex = 0;
+ listView.model.getPrevPage(currentPageIndex, appSearchKeyword, appSearchType);
+ stack.pop(StackView.Immediate);
+ console.log("SearchList:Keyword=", appSearchKeyword, "typeid=", appSearchType.toString());
+ }
+}
diff --git a/app/pages/SearchPage.qml b/app/pages/SearchPage.qml
new file mode 100644
index 0000000..847b1f7
--- /dev/null
+++ b/app/pages/SearchPage.qml
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2018 The Qt Company Ltd.
+ * Copyright (c) 2018-2019 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.
+ */
+
+import QtQuick 2.6
+import QtQuick.Controls 2.0
+import QtQuick.Layouts 1.1
+
+Page {
+ id: root
+
+ property variant parentPage: null
+ property int appSearchType: -1
+ property string appSearchKeyword: ""
+ property variant appTypeSelectlist:[
+ "Automotive",
+ "Operation System",
+ "Connectivity",
+ "Graphics",
+ "Navigation",
+ "Multimedia",
+ "Nativate APP",
+ "AGL APP",
+ "Web APP",
+ "Other"]
+
+ RowLayout {
+ id: keywordLayout
+ //height: parent.width / 3
+ width: parent.width
+ //anchors.top: root.top
+ //anchors.topMargin: 10
+
+ spacing: 20
+ ColumnLayout {
+ spacing: 5
+ Label {
+ Layout.fillWidth: true
+ text: qsTr("KeyWord:")
+ font.pixelSize: 32
+ font.bold: true
+ color: '#00ADDC'
+ }
+ RowLayout {
+ width: root.width
+ spacing: 10
+ TextEdit {
+ id: textInputKeyWord
+ Layout.fillWidth: true
+ text: appSearchKeyword == "" ? qsTr("Please input keyword!") : appSearchKeyword
+ font.pixelSize: 32
+ font.bold: true
+ font.wordSpacing: -1
+ font.letterSpacing: 0
+ clip: true
+ font.weight: Font.Normal
+ font.capitalization: Font.MixedCase
+ onFocusChanged: {
+ if(focus === false){
+ if(text === ""){
+ text = qsTr("Please input keyword!")
+ }
+ }
+ }
+ Rectangle {
+ anchors.fill: parent
+ color:"transparent"
+ border.color: "#66FF99"
+ border.width: 2
+ z: -1
+ }
+ }
+ SButton {
+ text: 'X'
+ anchors.rightMargin: 100
+
+ visible: textInputKeyWord.text != ""
+ onClicked: {
+ textInputKeyWord.text = "";
+ textInputKeyWord.forceActiveFocus();
+ }
+
+
+ implicitWidth: 40
+ implicitHeight: 40
+ }
+ Item {
+ id: searchBtn
+ width: 140
+ height: 40
+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
+ SButton {
+ anchors.fill:parent
+ enabled: (textInputKeyWord.text != ""
+ && textInputKeyWord.text != "Please input keyword!")
+ || appSearchType != -1
+ text: 'Search'
+ onClicked: {
+ getSearchList();
+ }
+ }
+ }
+ }
+ Label {
+ Layout.fillWidth: true
+ text: qsTr("App Type:")
+ font.pixelSize: 32
+ font.bold: true
+ color: '#00ADDC'
+ }
+ }
+ }
+
+ RowLayout {
+ id: infoLayout
+ height: root.width / 3 * 2
+ width: root.width
+ anchors.top: keywordLayout.bottom
+ anchors.topMargin: 10
+ spacing: 20
+ ColumnLayout {
+ Layout.alignment: Qt.AlignCenter | Qt.AlignVCenter
+ spacing: 60
+
+ SButton {
+ text: appTypeSelectlist[0]
+ highlighted: appSearchType == getSelectAppTypeId(text)
+ onClicked:{
+ var appTypeid = getSelectAppTypeId(text);
+ if(appSearchType === appTypeid) {
+ appSearchType = -1;
+ } else {
+ appSearchType = appTypeid;
+ }
+ console.log("appSearchType=", appSearchType.toString(), appTypeid.toString());
+ }
+ implicitWidth: 320
+ implicitHeight: 60
+ }
+ SButton {
+ text: appTypeSelectlist[2]
+ highlighted: appSearchType == getSelectAppTypeId(text)
+ onClicked:{
+ var appTypeid = getSelectAppTypeId(text);
+ if(appSearchType == appTypeid) {
+ appSearchType = -1;
+ } else {
+ appSearchType = appTypeid;
+ }
+ console.log("appSearchType=", appSearchType.toString(), appSearchType.toString());
+ }
+ implicitWidth: 320
+ implicitHeight: 60
+ }
+ SButton {
+ text: appTypeSelectlist[4]//qsTr("Navigation")
+ highlighted: appSearchType == getSelectAppTypeId(text)
+ onClicked:{
+ var appTypeid = getSelectAppTypeId(text);
+ if(appSearchType == appTypeid) {
+ appSearchType = -1;
+ } else {
+ appSearchType = appTypeid;
+ }
+ console.log("appSearchType=", appSearchType.toString(), appSearchType.toString());
+ }
+ implicitWidth: 320
+ implicitHeight: 60
+ }
+ SButton {
+ text: appTypeSelectlist[6]//qsTr("Nativate APP")
+ highlighted: appSearchType == getSelectAppTypeId(text)
+ onClicked:{
+ var appTypeid = getSelectAppTypeId(text);
+ if(appSearchType == appTypeid) {
+ appSearchType = -1;
+ } else {
+ appSearchType = appTypeid;
+ }
+ }
+ implicitWidth: 320
+ implicitHeight: 60
+ }
+ SButton {
+ text: appTypeSelectlist[8]//qsTr("Web APP")
+ highlighted: appSearchType == getSelectAppTypeId(text)
+ onClicked:{
+ var appTypeid = getSelectAppTypeId(text);
+ if(appSearchType == appTypeid) {
+ appSearchType = -1;
+ } else {
+ appSearchType = appTypeid;
+ }
+ }
+ implicitWidth: 320
+ implicitHeight: 60
+ }
+ }
+ ColumnLayout {
+ Layout.alignment: Qt.AlignCenter | Qt.AlignVCenter
+ spacing: 60
+
+ SButton {
+ text: appTypeSelectlist[1]//qsTr("Operation System")
+ highlighted: appSearchType == getSelectAppTypeId(text)
+ onClicked:{
+ var appTypeid = getSelectAppTypeId(text);
+ if(appSearchType == appTypeid) {
+ appSearchType = -1;
+ } else {
+ appSearchType = appTypeid;
+ }
+ }
+ implicitWidth: 320
+ implicitHeight: 60
+ }
+ SButton {
+ text: appTypeSelectlist[3]//qsTr("Graphics")
+ highlighted: appSearchType == getSelectAppTypeId(text)
+ onClicked:{
+ var appTypeid = getSelectAppTypeId(text);
+ if(appSearchType == appTypeid) {
+ appSearchType = -1;
+ } else {
+ appSearchType = appTypeid;
+ }
+ }
+ implicitWidth: 320
+ implicitHeight: 60
+ }
+ SButton {
+ text: appTypeSelectlist[5]//qsTr("Multimedia")
+ highlighted: appSearchType === getSelectAppTypeId(text)
+ onClicked:{
+ var appTypeid = getSelectAppTypeId(text);
+ if(appSearchType == appTypeid) {
+ appSearchType = -1;
+ } else {
+ appSearchType = appTypeid;
+ }
+ }
+ implicitWidth: 320
+ implicitHeight: 60
+ }
+ SButton {
+ text: appTypeSelectlist[7]//qsTr("AGL APP")
+ highlighted: appSearchType === getSelectAppTypeId(text)
+ onClicked:{
+ var appTypeid = getSelectAppTypeId(text);
+ if(appSearchType == appTypeid) {
+ appSearchType = -1;
+ } else {
+ appSearchType = appTypeid;
+ }
+ }
+ implicitWidth: 320
+ implicitHeight: 60
+ }
+ SButton {
+ text: appTypeSelectlist[9]//qsTr("Other")
+ highlighted: appSearchType === getSelectAppTypeId(text)
+ onClicked:{
+ var appTypeid = getSelectAppTypeId(text);
+ if(appSearchType === appTypeid) {
+ appSearchType = -1;
+ } else {
+ appSearchType = appTypeid;
+ }
+ }
+ implicitWidth: 320
+ implicitHeight: 60
+ }
+ }
+
+
+ }
+
+
+ Rectangle {
+ anchors.fill: infoLayout
+ color: 'transparent'
+ border.color: 'grey'
+ z: -1
+ }
+
+
+ function getSelectAppTypeId(name){
+ for(var key in appTypeSelectlist){
+ if(name == appTypeSelectlist[key]){
+ return Number(key);
+ }
+ }
+ return -1;
+ }
+
+ function getSearchList()
+ {
+ if(textInputKeyWord.text != "Please input keyword!") {
+ appSearchKeyword = textInputKeyWord.text;
+ } else {
+ appSearchKeyword = "";
+ }
+
+ console.log("SearchPage:KeyWord=", appSearchKeyword,
+ "appTypeid=", appSearchType.toString());
+ parentPage.popBack(appSearchKeyword, appSearchType);
+ }
+}
diff --git a/app/resources.qrc b/app/resources.qrc
new file mode 100644
index 0000000..1cdf36e
--- /dev/null
+++ b/app/resources.qrc
@@ -0,0 +1,13 @@
+<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>
+ <file>pages/SearchList.qml</file>
+ <file>pages/SButton.qml</file>
+ <file>pages/DImage.qml</file>
+ </qresource>
+</RCC>
diff --git a/app/src/appinfo.cpp b/app/src/appinfo.cpp
new file mode 100644
index 0000000..bf51ba4
--- /dev/null
+++ b/app/src/appinfo.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2016, 2017 Mentor Graphics Development (Deutschland) GmbH
+ * Copyright (C) 2016 The Qt Company Ltd.
+ * Copyright (c) 2018-2019 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 <QFileInfo>
+#include "config.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;
+}
+
+bool AppInfo::operator==(const AppInfo& other) {
+ return d->id == other.id();
+}
+
+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::setState(const int state) {
+ d->state = (enum AppState)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();
+ QFileInfo fi(d->iconPath);
+ if (!fi.isFile()) {
+ d->iconPath = "";
+ }
+
+ 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["imagePath"].toString();
+
+ if (json["imagePath"].toString().isEmpty()) {
+ d->iconPath = "";
+ } else {
+ d->iconPath = getIconUrl(json["imagePath"].toString());
+ }
+ 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..fc5fc53
--- /dev/null
+++ b/app/src/appinfo.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016, 2017 Mentor Graphics Development (Deutschland) GmbH
+ * Copyright (C) 2016 The Qt Company Ltd.
+ * Copyright (c) 2018-2019 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);
+ bool 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 setState(const int 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..5f66a6a
--- /dev/null
+++ b/app/src/config.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 The Qt Company Ltd.
+ * Copyright (c) 2018-2019 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 "warehouse.tmc-tokai.jp"
+
+#define SERVER_DOMAIN_SERVICE QString("%1/webservice").arg(SERVER_DOMAIN)
+
+#define SERVER_BASE_URL "/api/v1/app"
+#define SERVER_API_LIST "/collection"
+
+#define getURL(api) \
+ QString("http://%1%2%3").arg(SERVER_DOMAIN_SERVICE, SERVER_BASE_URL, api)
+#define getUrlWithPage(api, offset, limit) \
+ getURL(api).append( \
+ QString("?sort=createDate&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_SERVICE, SERVER_BASE_URL, path, typeId, appId)
+#define getWgtUrl(path) \
+ QString("http://%1%2/file?filePath=%3") \
+ .arg(SERVER_DOMAIN_SERVICE, SERVER_BASE_URL, path)
+
+#define getIconUrl(path) QString("http://%1%2").arg(SERVER_DOMAIN, 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..1ec8ffb
--- /dev/null
+++ b/app/src/hmi-debug.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2018-2019 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..c9bad50
--- /dev/null
+++ b/app/src/httpclient.cpp
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2018-2019 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 "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..88dbbb1
--- /dev/null
+++ b/app/src/httpclient.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2018-2019 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 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..986ebc1
--- /dev/null
+++ b/app/src/main.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2018 The Qt Company Ltd.
+ * Copyright (c) 2018-2019 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 <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");
+ struct json_object *obj_param = nullptr, *obj_area = nullptr;
+ if(json_object_object_get_ex(object, "parameter", &obj_param)
+ && json_object_object_get_ex(obj_param, "area", &obj_area)) {
+ qwm->activateWindow(role.toStdString().c_str(), json_object_get_string(obj_area));
+ }
+ else {
+ qwm->activateWindow(role.toStdString().c_str(), "normal");
+ }
+ });
+
+ 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..7cd06b2
--- /dev/null
+++ b/app/src/nativeappmodel.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2016 The Qt Company Ltd.
+ * Copyright (C) 2016, 2017 Mentor Graphics Development (Deutschland) GmbH
+ * Copyright (c) 2018-2019 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();
+ QStringList list = id.split("@");
+ QString& name = list[0];
+
+ //afm_user_daemon_proxy->terminate(id);
+
+ QString result = afm_user_daemon_proxy->uninstall(id);
+
+ if (result == "null") {
+ QString pathName = "~/app-data/" + name + "/";
+
+ if(!name.isEmpty()){
+ QString cmd = "rm -r " + pathName;
+ qDebug() <<"CMD:"<< cmd;
+ system(cmd.toStdString().c_str());
+ }
+
+ pathName = "/var/local/lib/afm/applications/" + name + "/";
+ QFileInfo file(pathName);
+ if(file.isDir() && !name.isEmpty())
+ {
+ QString cmd = "rm -r " + pathName;
+ qDebug() <<"CMD:"<< cmd;
+ system(cmd.toStdString().c_str());
+ }
+
+ 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..b7c93e6
--- /dev/null
+++ b/app/src/nativeappmodel.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 The Qt Company Ltd.
+ * Copyright (c) 2018-2019 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..ac0db62
--- /dev/null
+++ b/app/src/serverappmodel.cpp
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2016 The Qt Company Ltd.
+ * Copyright (C) 2016, 2017 Mentor Graphics Development (Deutschland) GmbH
+ * Copyright (c) 2018-2019 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;
+}
+
+bool ServerAppModel::setData(const QModelIndex& index,
+ const QVariant& value,
+ int role) {
+ if (!index.isValid())
+ return false;
+
+ switch (role) {
+ case StateRole:
+ applist[index.row()].setState(value.toInt());
+ break;
+ case ProgressRole:
+ applist[index.row()].setProgress(value.toReal());
+ break;
+ default:
+ return false;
+ break;
+ }
+
+ emit dataChanged(index, index);
+ return true;
+}
+
+bool ServerAppModel::removeRows(int row, int count, const QModelIndex&) {
+ QList<AppInfo>::iterator begin = applist.begin() + row;
+ QList<AppInfo>::iterator end = begin + count;
+
+ applist.erase(begin, end);
+
+ return true;
+}
+
+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) {
+ setData(index(i), AppInfo::Install, StateRole);
+ break;
+ }
+ }
+ }
+}
+
+void ServerAppModel::install(int i) {
+ setData(index(i), AppInfo::Downloading, StateRole);
+
+ QString url = getWgtUrl(applist[i].wgtPath());
+ HttpClient client(url);
+
+ client.download(
+ getDownloadFilePath(applist[i].fileName()),
+ [this, i](const QString&) {
+ setData(index(i), AppInfo::Installing, StateRole);
+
+ QTimer::singleShot(3000, this, [this, i] {
+ QString installResult = afm_user_daemon_proxy->install(
+ getDownloadFilePath(applist[i].fileName()));
+
+ setData(index(i),
+ installResult.isEmpty() ? AppInfo::Install : AppInfo::Launch,
+ StateRole);
+ setData(index(i), 0.0, ProgressRole);
+ });
+ },
+ [this, i](const QString& error) {
+ HMI_ERROR("ERROR", "%s", error.toStdString().c_str());
+
+ setData(index(i), AppInfo::Install, StateRole);
+ setData(index(i), 0.0, ProgressRole);
+ },
+ [this, i](const qint64 bytesReceived, const qint64 bytesTotal) {
+ qreal progressValue = ((qreal)bytesReceived / bytesTotal) * 100;
+ setData(index(i), progressValue, ProgressRole);
+ });
+}
+
+void ServerAppModel::clearList() {
+ beginResetModel();
+ applist.clear();
+ endResetModel();
+}
+
+void ServerAppModel::getPrevPage(int PageIndex, QString name, int type) {
+ this->getAppPage(PageIndex > 0 ? PageIndex - 1 : 0, PAGE_SIZE, name, type,
+ true);
+}
+
+void ServerAppModel::getNextPage(int PageIndex, QString name, int type) {
+ this->getAppPage(applist.count() >= PAGE_SIZE ? PageIndex + 1 : PageIndex,
+ PAGE_SIZE, name, type);
+}
+
+void ServerAppModel::setNativeApplist(const QList<AppInfo>& applist) {
+ nativeApplist.clear();
+ nativeApplist = applist;
+ checkAppState();
+}
+
+void ServerAppModel::checkAppState() {
+ if (applist.isEmpty() || nativeApplist.isEmpty()) {
+ return;
+ }
+
+ QList<AppInfo>::iterator it;
+ int idx = 0;
+ 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()) {
+ setData(index(idx),
+ (*it).version() != (*nit).version() ? AppInfo::Update
+ : AppInfo::Launch,
+ StateRole);
+ break;
+ }
+ }
+ idx++;
+ }
+}
+
+void ServerAppModel::getAppPage(int pageIndex,
+ int pageSize,
+ QString name,
+ int type,
+ bool isPrev) {
+ QString url =
+ getUrlWithPage(SERVER_API_LIST, pageIndex * pageSize, pageSize)
+ .append(QString("&appDeviceTypeId=%1").arg(QString::number(0)));
+ if (!name.isEmpty()) {
+ // get search list by name
+ url.append(QString("&keyWord=%1").arg(name));
+ }
+ if (type != -1) {
+ // get search list by type
+ url.append(QString("&appTypeId=%1").arg(QString::number(type)));
+ }
+ HMI_ERROR("Message", "%s", url.toStdString().c_str());
+
+ 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();
+
+ int offsetSize = 0;
+ bool distinct = true;
+
+ beginResetModel();
+ if (isPrev || applist.size() < PAGE_SIZE || jappList.size() == PAGE_SIZE) {
+ applist.clear();
+ distinct = false;
+ } else {
+ //offsetSize = 0;
+ if (applist.size() > PAGE_SIZE) {
+ for (int i = 0; i < (applist.size() - PAGE_SIZE); i++) {
+ applist.removeLast();
+ offsetSize--;
+ }
+ }
+ }
+
+ for (auto const& app : jappList) {
+ QJsonObject const& jso = app.toObject();
+
+ AppInfo appinfo;
+ appinfo.readFromServer(jso);
+ if (distinct) {
+ if (applist.contains(appinfo)) {
+ continue;
+ } else {
+ offsetSize++;
+ }
+ }
+ applist.append(appinfo);
+ }
+ endResetModel();
+ checkAppState();
+
+ emit requestCompleted(offsetSize, pageIndex, PAGE_SIZE, isPrev);
+ },
+ [=](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..b60571f
--- /dev/null
+++ b/app/src/serverappmodel.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2016 The Qt Company Ltd.
+ * Copyright (c) 2018-2019 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;
+ bool setData(const QModelIndex& index,
+ const QVariant& value,
+ int role) override;
+ bool removeRows(int row,
+ int count,
+ const QModelIndex& parent = QModelIndex()) 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 clearList();
+ Q_INVOKABLE void getPrevPage(int PageIndex, QString name = "", int type = -1);
+ Q_INVOKABLE void getNextPage(int PageIndex, QString name = "", int type = -1);
+
+ Q_INVOKABLE void setNativeApplist(const QList<AppInfo>& applist);
+
+ void getAppPage(int pageIndex,
+ int pageSize,
+ QString name,
+ int type,
+ bool isPrev = false);
+
+ void appChanged(const QString& info);
+
+ signals:
+ void requestCompleted(int offsetSize,
+ int pageIndex,
+ int pageSize,
+ bool isPrev);
+
+ 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