diff options
author | zheng_wenlong <wenlong_zheng@nexty-ele.com> | 2019-04-09 10:48:11 +0900 |
---|---|---|
committer | Zheng Wenlong <wenlong_zheng@nexty-ele.com> | 2019-04-10 01:47:42 +0000 |
commit | 99812e94289a083e9d297af50f5b227e3ad58f52 (patch) | |
tree | d9d43dddf03880e6615fcf4cfdaa02cbee5eff54 /app |
Add demo3 warehouse source code for cluster modeicefish_8.99.5icefish_8.99.4icefish_8.99.3icefish_8.99.2icefish_8.99.1icefish/8.99.5icefish/8.99.4icefish/8.99.3icefish/8.99.2icefish/8.99.1halibut_8.0.6halibut_8.0.5halibut_8.0.4halibut_8.0.3halibut_8.0.2halibut_8.0.1halibut_8.0.0halibut_7.99.3halibut_7.99.2halibut_7.99.1halibut/8.0.6halibut/8.0.5halibut/8.0.4halibut/8.0.3halibut/8.0.2halibut/8.0.1halibut/8.0.0halibut/7.99.3halibut/7.99.2halibut/7.99.18.99.58.99.48.99.38.99.28.99.18.0.68.0.58.0.48.0.38.0.28.0.18.0.07.99.37.99.27.99.1halibut
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')
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="&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 |