From 31e4ddecb2e704ab81303568102f786ea727d0cf Mon Sep 17 00:00:00 2001 From: zheng_wenlong Date: Wed, 7 Nov 2018 11:09:54 +0900 Subject: add warehouse source --- app/CameraControl.qml | 133 -- app/app.pri | 20 - app/app.pro | 25 +- app/camera.cpp | 139 -- app/camera.h | 70 - app/config.tests/libhomescreen/libhomescreen.cpp | 8 - app/config.tests/libhomescreen/libhomescreen.pro | 5 - .../qlibwindowmanager/qlibwindowmanager.cpp | 8 - .../qlibwindowmanager/qlibwindowmanager.pro | 5 - app/images/Back.svg | 51 + app/images/DividingLine.svg | 58 + app/images/Top.svg | 54 + app/images/camera/camera_bg.svg | 484 ------- app/images/camera/camerainfo_bg.svg | 93 -- app/images/homescreen/homebg_bottom.svg | 1397 -------------------- app/images/images.qrc | 8 +- app/main.cpp | 138 -- app/main.qml | 151 ++- app/main.qrc | 6 - app/pages/DetailPage.qml | 111 ++ app/pages/DownloadBar.qml | 43 + app/pages/ListPage.qml | 186 +++ app/pages/ManagementPage.qml | 156 +++ app/pages/SearchPage.qml | 24 + app/resources.qrc | 10 + app/src/appinfo.cpp | 200 +++ app/src/appinfo.h | 94 ++ app/src/config.h | 46 + app/src/hmi-debug.h | 89 ++ app/src/httpclient.cpp | 267 ++++ app/src/httpclient.h | 48 + app/src/main.cpp | 139 ++ app/src/nativeappmodel.cpp | 157 +++ app/src/nativeappmodel.h | 63 + app/src/serverappmodel.cpp | 286 ++++ app/src/serverappmodel.h | 78 ++ app/src/src.pri | 17 + 37 files changed, 2284 insertions(+), 2583 deletions(-) delete mode 100644 app/CameraControl.qml delete mode 100644 app/app.pri delete mode 100644 app/camera.cpp delete mode 100644 app/camera.h delete mode 100644 app/config.tests/libhomescreen/libhomescreen.cpp delete mode 100644 app/config.tests/libhomescreen/libhomescreen.pro delete mode 100644 app/config.tests/qlibwindowmanager/qlibwindowmanager.cpp delete mode 100644 app/config.tests/qlibwindowmanager/qlibwindowmanager.pro create mode 100644 app/images/Back.svg create mode 100644 app/images/DividingLine.svg create mode 100644 app/images/Top.svg delete mode 100644 app/images/camera/camera_bg.svg delete mode 100644 app/images/camera/camerainfo_bg.svg delete mode 100644 app/images/homescreen/homebg_bottom.svg delete mode 100644 app/main.cpp delete mode 100644 app/main.qrc create mode 100644 app/pages/DetailPage.qml create mode 100644 app/pages/DownloadBar.qml create mode 100644 app/pages/ListPage.qml create mode 100644 app/pages/ManagementPage.qml create mode 100644 app/pages/SearchPage.qml create mode 100644 app/resources.qrc create mode 100644 app/src/appinfo.cpp create mode 100644 app/src/appinfo.h create mode 100644 app/src/config.h create mode 100644 app/src/hmi-debug.h create mode 100644 app/src/httpclient.cpp create mode 100644 app/src/httpclient.h create mode 100644 app/src/main.cpp create mode 100644 app/src/nativeappmodel.cpp create mode 100644 app/src/nativeappmodel.h create mode 100644 app/src/serverappmodel.cpp create mode 100644 app/src/serverappmodel.h create mode 100644 app/src/src.pri (limited to 'app') diff --git a/app/CameraControl.qml b/app/CameraControl.qml deleted file mode 100644 index 4e0b94d..0000000 --- a/app/CameraControl.qml +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2017 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.3 -import QtQuick.Controls 2.0 - -RowLayout { - property var device - property var listWH - property var number: [] - property var fps: ["10", "20", "30", "40", "50", "60"] - property var resolution: listWH ? ["320*240", "640*480", "1280*720", "1920*1080"] : ["320*240"] - property var models: [number, fps, resolution] - property real back: 1 - property bool switchstatus: true - property bool switchChecked: false - - Repeater { - id: info - model: ["No.:", "FPS:", "W*H:"] - delegate: RowLayout{ - Label { - id: label - text: model.modelData - color: "#59FF7F" - } - ComboBox { - id: camerainfo - implicitWidth: index != 2 ? 100 : 245 - font: label.font - model: models[index] - contentItem: Text { - text: camerainfo.displayText - font: camerainfo.font - color: camerainfo.pressed ? "#17a81a" : "white" - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - indicator: Canvas { - id: canvas - x: camerainfo.width - width - camerainfo.rightPadding - y: camerainfo.topPadding + (camerainfo.availableHeight - height) / 2 - width: 20 - height: 12 - contextType: "2d" - - Connections { - target: camerainfo - onPressedChanged: canvas.requestPaint() - } - - onPaint: { - context.reset(); - context.moveTo(0, 0); - context.lineTo(width, 0); - context.lineTo(width / 2, height); - context.closePath(); - context.fillStyle = camerainfo.pressed ? "#17a81a" : "white"; - context.fill(); - } - } - popup: Popup { - id: popup - y: camerainfo.height - 1 - implicitWidth: camerainfo.width - implicitHeight: listview.count > 6 ? (listview.contentHeight/3.3) : listview.contentHeight - padding: 0 - - contentItem: ListView { - id: listview - clip: true - model: camerainfo.visible ? camerainfo.delegateModel : null - currentIndex: camerainfo.highlightedIndex - ScrollIndicator.vertical: ScrollIndicator { } - } - background: Image { source: "images/camera/camerainfo_bg.svg" } - } - delegate: ItemDelegate { - id: popupdelegate - width: camerainfo.width - contentItem: Item { - implicitHeight: 30 - Text { - text: modelData - color: popupdelegate.pressed || highlighted ? "#21be2b" : "white" - font: camerainfo.font - elide: Text.ElideRight - verticalAlignment: Text.AlignVCenter - } - } - highlighted: camerainfo.highlightedIndex == index - } - - background: Image { source: "images/camera/camerainfo_bg.svg" } - } - } - } - Switch { - id: cameraswitch - enabled: switchstatus - checked: switchChecked - onCheckedChanged: { - if (checked && device){ - device.start(info.itemAt(0).children[1].currentText, info.itemAt(1).children[1].currentText, info.itemAt(2).children[1].currentText) - }else if (!checked && device){ - device.stop() - } - } - } - - Component.onCompleted: { - device.enumerateCameras(); - number = device.camranum(); - - if (device.cameraCnt() === 0) - switchstatus = false; - } -} diff --git a/app/app.pri b/app/app.pri deleted file mode 100644 index 590c154..0000000 --- a/app/app.pri +++ /dev/null @@ -1,20 +0,0 @@ -TEMPLATE = app - -load(configure) - -qtCompileTest(libhomescreen) -qtCompileTest(qlibwindowmanager) - -config_libhomescreen { - CONFIG += link_pkgconfig - PKGCONFIG += libhomescreen - DEFINES += HAVE_LIBHOMESCREEN -} - -config_qlibwindowmanager { - CONFIG += link_pkgconfig - PKGCONFIG += qlibwindowmanager - DEFINES += HAVE_QLIBWINDOWMANAGER -} - -DESTDIR = $${OUT_PWD}/../package/root/bin diff --git a/app/app.pro b/app/app.pro index b9d7bc5..78b861a 100644 --- a/app/app.pro +++ b/app/app.pro @@ -1,21 +1,18 @@ -TARGET = camapp -QT = quickcontrols2 +TEMPLATE = app +TARGET = warehouse +QT += quickcontrols2 dbus websockets -equals(QT_ARCH, "arm") { - QMAKE_CXXFLAGS += -mfp16-format=ieee -} +DESTDIR = $${OUT_PWD}/../package/root/bin -HEADERS += \ - camera.h +CONFIG += c++11 link_pkgconfig +PKGCONFIG += qlibhomescreen qlibwindowmanager -SOURCES += \ - main.cpp \ - camera.cpp +include(../interfaces/interfaces.pri) +include(src/src.pri) -LIBS += -lopencv_core -lopencv_videoio +OTHER_FILES += \ + main.qml RESOURCES += \ - main.qrc \ + resources.qrc \ images/images.qrc - -include(app.pri) diff --git a/app/camera.cpp b/app/camera.cpp deleted file mode 100644 index 8ee99c9..0000000 --- a/app/camera.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (c) 2017 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 "camera.h" -#include -#include - -#include -#include -#include -#include - -using namespace cv; - -Camera::Camera() { - running = false; - timer = new QTimer(this); - capture = new VideoCapture(); - - connect(timer, SIGNAL(timeout()), this, SLOT(grab())); -} - -Camera::~Camera() { - if (timer->isActive()) - timer->stop(); - if (capture && capture->isOpened()) - capture->release(); - delete timer; - delete capture; -} - -void Camera::paint(QPainter *painter) { - painter->drawImage(0, 0, image); -} - -void Camera::enumerateCameras() { - int maxID = 20; - for (int idx = 2; idx " << (char*)(cap.card) << ">" << (char*)(cap.bus_info);// << (cap.version>>16)&0xFF << (cap.version>>8)&0XFF << cap.version&0xFF; - cam_arr.push_back(idx); // vector of all available cameras - } - } - close(fd); - } - } - - camcnt = cam_arr.length(); - cam_arr_bak = cam_arr; -} - -void Camera::start(int no, int fps, QString res) { - try{ - for (QVariantList::iterator iter = cam_arr_bak.begin(); iter != cam_arr_bak.end(); ++iter){ - if (*iter == no){ - cam_arr_bak.erase(iter); - break; - } - } - - if (capture && capture->open(no)){ - capture->set(CAP_PROP_FRAME_WIDTH, res.section("*", 0, 0).toInt()); - capture->set(CAP_PROP_FRAME_HEIGHT, res.section("*", 1, 1).toInt()); - - if (fps > 0){ - timer->start(1000/fps); - running = true; - emit isrunningChanged(running); - - emit camraCntChanged(cam_arr_bak); - } - camnumbackup = no; - } - } - catch(cv::Exception & e) { - qDebug() << "als-meter-demo open device error: " << e.msg.c_str(); - } -} - -void Camera::stop() { - cam_arr_bak.push_back(camnumbackup); - - if (timer->isActive()) - timer->stop(); - if (capture && capture->isOpened()) - capture->release(); - image = QImage(); - update(); - running = false; - emit isrunningChanged(running); - - emit camraCntChanged(cam_arr_bak); -} - -QVariantList Camera::camranum() const{ - return cam_arr_bak; -} - -int Camera::cameraCnt() { - return camcnt; -} - -bool Camera::isrunning() const{ - return running; -} - -void Camera::grab() { - if (capture && capture->isOpened()){ - Mat frame; - capture->read(frame); - - if (!frame.empty()){ - image = QImage((const uchar*)(frame.data), frame.cols, frame.rows, frame.step, QImage::Format_RGB888).rgbSwapped(); - image = image.scaled(this->width(), this->height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - update(); - } - } -} \ No newline at end of file diff --git a/app/camera.h b/app/camera.h deleted file mode 100644 index 1b85d27..0000000 --- a/app/camera.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2017 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 CAMERA_H -#define CAMERA_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; - -static QVariantList cam_arr_bak; - -class Camera : public QQuickPaintedItem -{ - Q_OBJECT - Q_PROPERTY(bool isrunning READ isrunning NOTIFY isrunningChanged) -public: - Camera(); - ~Camera(); - void paint(QPainter *painter); - bool isrunning() const; - Q_INVOKABLE QVariantList camranum() const; - - Q_INVOKABLE void start(int no, int fps, QString res); - Q_INVOKABLE void stop(); - - Q_INVOKABLE void enumerateCameras(); - Q_INVOKABLE int cameraCnt(); - -signals: - void isrunningChanged(const bool& isrunning); - void camraCntChanged(const QVariantList& camcnt); - -public slots: - void grab(); - -private: - bool running; - QImage image; - QTimer* timer; - cv::VideoCapture* capture; - - QVariantList cam_arr; - - int camnumbackup; - int camcnt; -}; - -#endif // CAMERA_H diff --git a/app/config.tests/libhomescreen/libhomescreen.cpp b/app/config.tests/libhomescreen/libhomescreen.cpp deleted file mode 100644 index d698b05..0000000 --- a/app/config.tests/libhomescreen/libhomescreen.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include - -int main(int argc,char **argv) -{ - LibHomeScreen libHomeScreen; - return 0; -} - diff --git a/app/config.tests/libhomescreen/libhomescreen.pro b/app/config.tests/libhomescreen/libhomescreen.pro deleted file mode 100644 index 7d43112..0000000 --- a/app/config.tests/libhomescreen/libhomescreen.pro +++ /dev/null @@ -1,5 +0,0 @@ -SOURCES = libhomescreen.cpp - -CONFIG -= qt -CONFIG += link_pkgconfig -PKGCONFIG += libhomescreen diff --git a/app/config.tests/qlibwindowmanager/qlibwindowmanager.cpp b/app/config.tests/qlibwindowmanager/qlibwindowmanager.cpp deleted file mode 100644 index bb95c93..0000000 --- a/app/config.tests/qlibwindowmanager/qlibwindowmanager.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include - -int main(int argc,char **argv) -{ - QLibWindowmanager qwm; - return 0; -} - diff --git a/app/config.tests/qlibwindowmanager/qlibwindowmanager.pro b/app/config.tests/qlibwindowmanager/qlibwindowmanager.pro deleted file mode 100644 index cb51d98..0000000 --- a/app/config.tests/qlibwindowmanager/qlibwindowmanager.pro +++ /dev/null @@ -1,5 +0,0 @@ -SOURCES = qlibwindowmanager.cpp - -CONFIG += qt -CONFIG += link_pkgconfig -PKGCONFIG += qlibwindowmanager 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 @@ + + + + + Svg Vector Icons : http://www.sfont.cn image/svg+xml + + \ 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 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/app/images/Top.svg b/app/images/Top.svg new file mode 100644 index 0000000..0fffbe8 --- /dev/null +++ b/app/images/Top.svg @@ -0,0 +1,54 @@ + + + + + Svg Vector Icons : http://www.sfont.cn image/svg+xml + + \ No newline at end of file diff --git a/app/images/camera/camera_bg.svg b/app/images/camera/camera_bg.svg deleted file mode 100644 index 458f81c..0000000 --- a/app/images/camera/camera_bg.svg +++ /dev/null @@ -1,484 +0,0 @@ - - - - - - - - image/svg+xml - - - - - - - diff --git a/app/images/camera/camerainfo_bg.svg b/app/images/camera/camerainfo_bg.svg deleted file mode 100644 index 4251412..0000000 --- a/app/images/camera/camerainfo_bg.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - -image/svg+xml \ No newline at end of file diff --git a/app/images/homescreen/homebg_bottom.svg b/app/images/homescreen/homebg_bottom.svg deleted file mode 100644 index 78bf678..0000000 --- a/app/images/homescreen/homebg_bottom.svg +++ /dev/null @@ -1,1397 +0,0 @@ - - - - - - - - image/svg+xml - - - - - - - diff --git a/app/images/images.qrc b/app/images/images.qrc index 24782e3..867a055 100644 --- a/app/images/images.qrc +++ b/app/images/images.qrc @@ -1,7 +1,7 @@ - homescreen/homebg_bottom.svg - camera/camera_bg.svg - camera/camerainfo_bg.svg + DividingLine.svg + Back.svg + Top.svg - + \ No newline at end of file diff --git a/app/main.cpp b/app/main.cpp deleted file mode 100644 index 6fd928d..0000000 --- a/app/main.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2017 TOYOTA MOTOR CORPORATION - * Copyright (C) 2016 The Qt Company Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "camera.h" - -#include - -#ifdef HAVE_LIBHOMESCREEN -#include -#endif -#ifdef HAVE_QLIBWINDOWMANAGER -#include -#endif - -int main(int argc, char *argv[]) -{ - QString myname = QString("camapp"); - - QGuiApplication app(argc, argv); - app.setApplicationName(myname); - app.setApplicationVersion(QStringLiteral("0.1.0")); - app.setOrganizationDomain(QStringLiteral("automotivelinux.org")); - app.setOrganizationName(QStringLiteral("AutomotiveGradeLinux")); - - QQuickStyle::setStyle("AGL"); - - 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(); - - qmlRegisterType("Camera", 1, 0, "Camera"); - - QQmlApplicationEngine engine; - QQmlContext *context = engine.rootContext(); - QUrl bindingAddress; - int port = 0; - QString secret; - if (positionalArguments.length() == 2) { - port = positionalArguments.takeFirst().toInt(); - secret = positionalArguments.takeFirst(); - 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); - context->setContextProperty(QStringLiteral("bindingAddress"), bindingAddress); - } else { - context->setContextProperty(QStringLiteral("bindingAddress"), bindingAddress); - } - -#ifdef HAVE_QLIBWINDOWMANAGER - // WindowManager - QLibWindowmanager* qwm = new QLibWindowmanager(); - if(qwm->init(port,secret) != 0){ - exit(EXIT_FAILURE); - } - // Request a surface as described in layers.json windowmanager’s file - if (qwm->requestSurface(myname) != 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, myname](json_object *object) { - fprintf(stderr, "Surface got syncDraw!\n"); - qwm->endDraw(myname); - }); -#endif - -#ifdef HAVE_LIBHOMESCREEN - // HomeScreen - LibHomeScreen* hs = new LibHomeScreen(); - std::string token = secret.toStdString(); - hs->init(port, token.c_str()); - // Set the event handler for Event_TapShortcut which will activate the surface for windowmanager - hs->set_event_handler(LibHomeScreen::Event_TapShortcut, [qwm, myname](json_object *object){ - json_object *appnameJ = nullptr; - if(json_object_object_get_ex(object, "application_name", &appnameJ)) - { - const char *appname = json_object_get_string(appnameJ); - if(QString::compare(myname, appname, Qt::CaseInsensitive) == 0) - { - qDebug("Surface %s got tapShortcut\n", appname); - qwm->activateSurface(myname); - } - } - }); -#endif - engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); - - QObject *root = engine.rootObjects().first(); - QQuickWindow *window = qobject_cast(root); -#ifdef HAVE_QLIBWINDOWMANAGER - QObject::connect(window, SIGNAL(frameSwapped()), qwm, SLOT(slotActivateSurface())); - - qwm->set_event_handler(QLibWindowmanager::Event_Visible, [qwm, root](json_object *object) { - fprintf(stderr, "Surface got Visible!\n"); - QMetaObject::invokeMethod(root, "changeVisible", Q_ARG(QVariant, true)); - }); - - qwm->set_event_handler(QLibWindowmanager::Event_Invisible, [qwm, root](json_object *object) { - fprintf(stderr, "Surface got Invisible!\n"); - QMetaObject::invokeMethod(root, "changeVisible", Q_ARG(QVariant, false)); - }); -#else - window->resize(1280, 720); - window->setVisible(true); -#endif - - return app.exec(); -} diff --git a/app/main.qml b/app/main.qml index 9710c99..a481320 100644 --- a/app/main.qml +++ b/app/main.qml @@ -1,59 +1,92 @@ -/* - * Copyright (c) 2017 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.3 -import QtQuick.Controls 2.0 -import QtQuick.Window 2.2 -import Camera 1.0 -import QtWebSockets 1.0 - -ApplicationWindow { - id: root - width: 1920 - height: 720 - - CameraControl { - id:cameracontrol - listWH: true - device: camdev_device - width: root.width - height:80 - } - - Camera { - id: camdev_device - width: root.width - height: root.height - cameracontrol.height - anchors.top: cameracontrol.bottom - onIsrunningChanged: { - camerabg.visible = !isrunning - } - - Image { - id: camerabg - anchors.centerIn: parent - width: 200 - height: 200 - source: "images/camera/camera_bg.svg" - } - } - - function changeVisible(visible) { - console.log("camapp visible is " + visible) - cameracontrol.switchChecked = visible - } -} +/* + * Copyright (C) 2018 The Qt Company Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import QtQuick 2.6 +import QtQuick.Controls 2.0 +import AGL.Demo.Controls 1.0 +import NativeAppModel 1.0 +import ServerAppModel 1.0 + +import 'pages' + +ApplicationWindow { + id: root + + 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 + } + } + + SearchPage { + id: searchPage + + title: 'SearchPage' + } + + ManagementPage { + id: managementPage + + model: NativeAppModel { + id: managementModel + } + } + + Connections { + target: managementPage.model + onApplistChanged: { + listPage.model.setNativeApplist(applist) + } + } + + Component.onCompleted: { + listPage.model.getNextPage(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: "Management" + font.pixelSize: 20 + height: parent.height + } + } +} diff --git a/app/main.qrc b/app/main.qrc deleted file mode 100644 index 4a5aac9..0000000 --- a/app/main.qrc +++ /dev/null @@ -1,6 +0,0 @@ - - - main.qml - CameraControl.qml - - diff --git a/app/pages/DetailPage.qml b/app/pages/DetailPage.qml new file mode 100644 index 0000000..043714d --- /dev/null +++ b/app/pages/DetailPage.qml @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2018 The Qt Company Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import QtQuick 2.6 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.0 + + Page { + id: detailpage + visible: true + + property StackView stack: null + property var model + + Item { + id: backItem + width: 80 + height: 40 + + Image { + anchors.fill: parent + source: 'qrc:/images/Back.svg' + } + + MouseArea { + anchors.fill: parent + onClicked: { + stack.pop(StackView.Immediate) + } + } + } + + RowLayout { + id: infoLayout + + height: parent.width / 3 + width: parent.width + anchors.top: backItem.bottom + anchors.topMargin: 10 + + spacing: 20 + + Item { + Layout.preferredWidth: 200 + Layout.preferredHeight: 200 + Image { + anchors.fill: parent + source: model.icon + } + } + + ColumnLayout { + spacing: 5 + Label { + Layout.fillWidth: true + text: model.name.toUpperCase() + font.pixelSize: 32 + color: '#00ADDC' + } + Label { + text: 'Author: ' + model.author + font.pixelSize: 24 + } + Label { + text: 'Version: ' + model.version + font.pixelSize: 16 + font.italic: true + } + Label { + text: 'Category: ' + model.category + font.pixelSize: 16 + } + Label { + text: 'UpdateTime: ' + new Date(model.createdtime) + font.pixelSize: 16 + } + } + } + + Image { + id: dividingLine + width: parent.width + anchors.top: infoLayout.bottom + anchors.topMargin: 10 + anchors.horizontalCenter: parent.horizontalCenter + source: 'qrc:/images/DividingLine.svg' + } + + Label { + width: parent.width + anchors.top: dividingLine.bottom + anchors.topMargin: 20 + + text: model.description + wrapMode: Text.Wrap + font.pixelSize: 40 + } + } \ No newline at end of file diff --git a/app/pages/DownloadBar.qml b/app/pages/DownloadBar.qml new file mode 100644 index 0000000..38139e6 --- /dev/null +++ b/app/pages/DownloadBar.qml @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 The Qt Company Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import QtQuick 2.6 +import QtQuick.Controls 2.0 + +ProgressBar { + property real progress: 0 + + id: progressbar + z: -1 + value: (progress/100) + + background: Rectangle { + anchors.fill: parent + color: "transparent" + opacity: 0 + } + + contentItem: Item { + anchors.fill: parent + + Rectangle { + width: progressbar.value * parent.width + height: parent.height + opacity: 0.3 + color: "#32d0eb" + } + } +} \ No newline at end of file diff --git a/app/pages/ListPage.qml b/app/pages/ListPage.qml new file mode 100644 index 0000000..f4b78da --- /dev/null +++ b/app/pages/ListPage.qml @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2018 The Qt Company Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import QtQuick 2.6 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.0 + +Page { + id: root + property alias model: listView.model + + property int appSize: 0 + property int pageSize: 0 + property int currentPageIndex: 0 + + property int nPullHeight: 100 + + BusyIndicator { + id: prevBusyIndicator + anchors.horizontalCenter: parent.horizontalCenter + implicitWidth: 60 + implicitHeight: 60 + running: false + } + + StackView { + id: stack + initialItem: listView + anchors.fill: parent + anchors.margins: root.width * 0.075 + } + + ListView { + id: listView + //anchors.fill: parent + //anchors.margins: root.width * 0.075 + clip: true + + delegate: MouseArea { + id: delegate + width: listView.width + height: width / 6 + + RowLayout { + anchors.fill: parent + + Item { + Layout.preferredWidth: 100 + Layout.preferredHeight: 100 + + MouseArea{ + anchors.fill: parent + z: 2 + onClicked: { + stack.push("qrc:/pages/DetailPage.qml", {stack: stack, model: model}, StackView.Immediate) + } + } + Image { + anchors.fill: parent + source: model.icon + } + } + ColumnLayout { + Label { + Layout.fillWidth: true + text: model.name.toUpperCase() + color: '#00ADDC' + } + Label { + text: 'Version: ' + model.version + font.pixelSize: 16 + font.italic: true + } + Label { + text: 'Description: ' + model.description + font.pixelSize: 16 + wrapMode: Text.Wrap + elide: Text.ElideRight + Layout.preferredWidth: 400 + Layout.preferredHeight: 40 + } + } + ColumnLayout { + spacing: 5 + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + + Button { + x: x - 6 + text: model.statetext + onClicked: { + if (model.statetext === 'Install' || model.statetext === 'Update') { + listView.model.install(model.index) + } else if (model.statetext === 'Launch') { + if (listView.model.launch(model.id) > 1) { + homescreenHandler.tapShortcut(model.name) + } else { + console.warn('app cannot be launched') + } + } + } + implicitWidth: 140 + implicitHeight: 40 + //Layout.rightMargin: 5 + } + } + + } + DownloadBar { + anchors.fill: parent + progress: model.progress + } + Image { + source: 'qrc:/images/DividingLine.svg' + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + visible: model.index > 0 + } + + + } + + states: [ + State { + name: "prevPageState"; when: listView.contentY < -nPullHeight + StateChangeScript { + name: "prevPageScript" + script: getPrevPage() + } + } + ] + } + + function getPrevPage() { + listView.y = nPullHeight; + + currentPageIndex = currentPageIndex > 0 ? currentPageIndex-1 : 0; + prevBusyIndicator.running = true + prevPageTimer.start(); + } + + Timer { + id: prevPageTimer + interval: 1000 + repeat: false + running: false + onTriggered: { + listView.model.getPrevPage(currentPageIndex) + prevPageAnimation.start(); + } + } + + NumberAnimation { + id: prevPageAnimation + target: listView + property: "y" + duration: 100 + from: nPullHeight + to: 0 + onStopped: { + prevBusyIndicator.running = false + listView.y = 0; + } + } + + Connections { + target: listView.model + onRequestCompleted: { + appSize = appsize + pageSize = pagesize + console.log("request completed!", appSize, pageSize) + } + } +} diff --git a/app/pages/ManagementPage.qml b/app/pages/ManagementPage.qml new file mode 100644 index 0000000..9a4cf31 --- /dev/null +++ b/app/pages/ManagementPage.qml @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2018 The Qt Company Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import QtQuick 2.6 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.0 + +Page { + id: root + property alias model: listView.model + + property int nPullHeight: 300 + + BusyIndicator { + id: busyIndicator + anchors.horizontalCenter: parent.horizontalCenter + implicitWidth: 60 + implicitHeight: 60 + running: false + } + + ListView { + id: listView + anchors.fill: parent + anchors.margins: root.width * 0.075 + clip: true + + delegate: MouseArea { + id: delegate + width: listView.width + height: width / 6 + RowLayout { + anchors.fill: parent + Item { + Layout.preferredWidth: 100 + Layout.preferredHeight: 100 + Image { + anchors.fill: parent + source: 'file://' + model.icon + } + } + ColumnLayout { + Label { + Layout.fillWidth: true + text: model.name.toUpperCase() + color: '#00ADDC' + } + Label { + text: 'Version: ' + model.version + font.pixelSize: 16 + font.italic: true + } + Label { + text: 'Description: ' + model.description + font.pixelSize: 16 + wrapMode: Text.Wrap + elide: Text.ElideRight + Layout.preferredWidth: 400 + Layout.preferredHeight: 40 + } + } + ColumnLayout { + spacing: 5 + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + + Button { + x: x - 6 + text: 'Launch' + onClicked: { + if (listView.model.launch(model.id) > 1) { + homescreenHandler.tapShortcut(model.name) + } else { + console.warn('app cannot be launched') + } + } + implicitWidth: 140 + implicitHeight: 40 + } + Button { + x: x - 6 + visible: model.name.toUpperCase() != "HOMESCREEN" && model.name.toUpperCase() != "LAUNCHER" + text: 'Uninstall' + onClicked: { + listView.model.uninstall(model.index) + } + implicitWidth: 140 + implicitHeight: 40 + } + } + } + Image { + source: 'qrc:/images/DividingLine.svg' + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + visible: model.index > 0 + } + + } + + states: [ + State { + name: "refreshState"; when: listView.contentY < -nPullHeight + StateChangeScript { + name: "refreshScript" + script: refreshModel() + } + } + ] + } + + function refreshModel() { + listView.y = nPullHeight; + + busyIndicator.running = true + refreshTimer.start(); + } + + Timer { + id: refreshTimer + interval: 1000 + repeat: false + running: false + onTriggered: { + listView.model.refresh() + refreshAnimation.start(); + } + } + + NumberAnimation { + id: refreshAnimation + target: listView + property: "y" + duration: 100 + from: nPullHeight + to: 0 + onStopped: { + busyIndicator.running = false + listView.y = 0; + } + } + +} + diff --git a/app/pages/SearchPage.qml b/app/pages/SearchPage.qml new file mode 100644 index 0000000..1d495b4 --- /dev/null +++ b/app/pages/SearchPage.qml @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2018 The Qt Company Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import QtQuick 2.6 +import QtQuick.Controls 2.0 + +Page { + id: root + + title: 'SearchPage' +} diff --git a/app/resources.qrc b/app/resources.qrc new file mode 100644 index 0000000..bcf707b --- /dev/null +++ b/app/resources.qrc @@ -0,0 +1,10 @@ + + + main.qml + pages/ListPage.qml + pages/SearchPage.qml + pages/ManagementPage.qml + pages/DownloadBar.qml + pages/DetailPage.qml + + \ No newline at end of file diff --git a/app/src/appinfo.cpp b/app/src/appinfo.cpp new file mode 100644 index 0000000..b06dd2f --- /dev/null +++ b/app/src/appinfo.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2016, 2017 Mentor Graphics Development (Deutschland) GmbH + * Copyright (C) 2016 The Qt Company Ltd. + * Copyright (c) 2018 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "appinfo.h" +#include "hmi-debug.h" + +class AppInfo::Private : public QSharedData { + public: + Private(); + Private(const Private& other); + + QString id; + QString version; + int width; + int height; + QString name; + QString description; + QString shortname; + QString author; + QString iconPath; + AppState state; + qreal progress; + + QString serverid; + QString wgtpath; + QString filename; + QString categoryid; + QString categoryname; + QString deviceid; + QString devicename; + double createdtime; +}; + +AppInfo::Private::Private() : width(-1), height(-1) {} + +AppInfo::Private::Private(const Private& other) + : QSharedData(other), + id(other.id), + version(other.version), + width(other.width), + height(other.height), + name(other.name), + description(other.description), + shortname(other.shortname), + author(other.author), + iconPath(other.iconPath), + state(other.state), + progress(other.progress), + serverid(other.serverid), + wgtpath(other.wgtpath), + filename(other.filename), + categoryid(other.categoryid), + categoryname(other.categoryname), + deviceid(other.deviceid), + devicename(other.devicename), + createdtime(other.createdtime) {} + +AppInfo::AppInfo() : d(new Private) {} + +AppInfo::AppInfo(const QString& icon, const QString& name, const QString& id) + : d(new Private) { + d->iconPath = icon; + d->name = name; + d->id = id; +} + +AppInfo::AppInfo(const AppInfo& other) : d(other.d) {} + +AppInfo::~AppInfo() {} + +AppInfo& AppInfo::operator=(const AppInfo& other) { + d = other.d; + return *this; +} + +QString AppInfo::id() const { + return d->id; +} + +QString AppInfo::version() const { + return d->version; +} + +int AppInfo::width() const { + return d->width; +} + +int AppInfo::height() const { + return d->height; +} + +QString AppInfo::name() const { + return d->name; +} + +QString AppInfo::description() const { + return d->description; +} + +QString AppInfo::shortname() const { + return d->shortname; +} + +QString AppInfo::author() const { + return d->author; +} + +QString AppInfo::iconPath() const { + return d->iconPath; +} + +AppInfo::AppState AppInfo::state() const { + return d->state; +} + +qreal AppInfo::progress() const { + return d->progress; +} + +QString AppInfo::serverId() const { + return d->serverid; +} +QString AppInfo::wgtPath() const { + return d->wgtpath; +} +QString AppInfo::fileName() const { + return d->filename; +} + +QString AppInfo::categoryId() const { + return d->categoryid; +} +QString AppInfo::categoryName() const { + return d->categoryname; +} +QString AppInfo::deviceId() const { + return d->deviceid; +} +QString AppInfo::deviceName() const { + return d->devicename; +} +double AppInfo::createdTime() const { + return d->createdtime; +} + +void AppInfo::setState(const AppState state) { + d->state = state; +} + +void AppInfo::setProgress(const qreal progress) { + d->progress = progress; +} + +void AppInfo::read(const QJsonObject& json) { + d->id = json["id"].toString(); + d->version = json["version"].toString(); + d->width = json["width"].toInt(); + d->height = json["height"].toInt(); + d->name = json["name"].toString(); + d->description = json["description"].toString(); + d->shortname = json["shortname"].toString(); + d->author = json["author"].toString(); + d->iconPath = json["icon"].toString(); + d->state = Launch; +} + +void AppInfo::readFromServer(const QJsonObject& json) { + d->name = json["appName"].toString(); + d->description = json["appAbstract"].toString(); + d->version = json["versionName"].toString(); + d->id = json["appIdCustom"].toString() + "@" + d->version; + d->serverid = json["appId"].toString(); + d->wgtpath = json["verFilePath"].toString(); + d->filename = json["verFilePath"].toString().section('/', -1); + d->author = json["developerName"].toString(); + d->categoryid = json["typeId"].toString(); + d->categoryname = json["typeName"].toString(); + d->deviceid = json["appDeviceTypeId"].toString(); + d->devicename = json["appDeviceTypeName"].toString(); + d->createdtime = json["updateDate"].toDouble(); + // d->iconPath = json["icon"].toString(); + d->iconPath = "file:///var/local/lib/afm/applications/launcher/0.1/icon.svg"; + d->state = Install; + d->progress = 0.0; +} diff --git a/app/src/appinfo.h b/app/src/appinfo.h new file mode 100644 index 0000000..052cbc9 --- /dev/null +++ b/app/src/appinfo.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2016, 2017 Mentor Graphics Development (Deutschland) GmbH + * Copyright (C) 2016 The Qt Company Ltd. + * Copyright (c) 2018 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef APPINFO_H +#define APPINFO_H + +#include +#include +#include + +class AppInfo { + Q_GADGET + Q_PROPERTY(QString id READ id) + Q_PROPERTY(QString version READ version) + Q_PROPERTY(int width READ width) + Q_PROPERTY(int height READ height) + Q_PROPERTY(QString name READ name) + Q_PROPERTY(QString description READ description) + Q_PROPERTY(QString shortname READ shortname) + Q_PROPERTY(QString author READ author) + Q_PROPERTY(QString iconPath READ iconPath) + Q_PROPERTY(AppState state READ state WRITE setState) + Q_PROPERTY(qreal progress READ progress WRITE setProgress) + Q_PROPERTY(QString serverid READ serverId) + Q_PROPERTY(QString wgtpath READ wgtPath) + Q_PROPERTY(QString filename READ fileName) + Q_PROPERTY(QString categoryid READ categoryId) + Q_PROPERTY(QString categoryname READ categoryName) + Q_PROPERTY(QString deviceid READ deviceId) + Q_PROPERTY(QString devicename READ deviceName) + Q_PROPERTY(double createdtime READ createdTime) + public: + enum AppState { Install = 0, Update, Launch, Downloading, Installing }; + Q_ENUM(AppState) + + AppInfo(); + AppInfo(const QString& icon, const QString& name, const QString& id); + AppInfo(const AppInfo& other); + virtual ~AppInfo(); + AppInfo& operator=(const AppInfo& other); + void swap(AppInfo& other) { qSwap(d, other.d); } + + QString id() const; + QString version() const; + int width() const; + int height() const; + QString name() const; + QString description() const; + QString shortname() const; + QString author() const; + QString iconPath() const; + AppState state() const; + qreal progress() const; + + QString serverId() const; + QString wgtPath() const; + QString fileName() const; + QString categoryId() const; + QString categoryName() const; + QString deviceId() const; + QString deviceName() const; + double createdTime() const; + + void setState(const AppState state); + void setProgress(const qreal progress); + + void read(const QJsonObject& json); + void readFromServer(const QJsonObject& json); + + private: + class Private; + QSharedDataPointer d; +}; + +Q_DECLARE_SHARED(AppInfo) +Q_DECLARE_METATYPE(AppInfo) +Q_DECLARE_METATYPE(QList) + +#endif // APPINFO_H diff --git a/app/src/config.h b/app/src/config.h new file mode 100644 index 0000000..0f2d5ed --- /dev/null +++ b/app/src/config.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016 The Qt Company Ltd. + * Copyright (c) 2018 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CONFIG_H +#define CONFIG_H + +// server url config +//#define SERVER_DOMAIN "192.168.20.161/webservice" +#define SERVER_DOMAIN "202.7.19.45/webservice" +//#define SERVER_DOMAIN "warehouse.tmc-tokai.jp/webservice" +#define SERVER_BASE_URL "/api/v1/app" +#define SERVER_API_LIST "/collection" + +#define getURL(api) \ + QString("http://%1%2%3").arg(SERVER_DOMAIN, SERVER_BASE_URL, api) +#define getUrlWithPage(api, offset, limit) \ + getURL(api).append( \ + QString("?sort=updateDate&order=desc&offset=%1&limit=%2") \ + .arg(QString::number(offset), QString::number(limit))) + +//#define getWgtUrl(path, typeId, appId) \ +// QString("http://%1%2/file/%3/%4/%5") \ +// .arg(SERVER_DOMAIN, SERVER_BASE_URL, path, typeId, appId) +#define getWgtUrl(path) \ + QString("http://%1%2/file?filePath=%3") \ + .arg(SERVER_DOMAIN, SERVER_BASE_URL, path) + +// server app page config +#define PAGE_SIZE 20 + +#define getDownloadFilePath(filename) QString("/tmp/%1").arg(filename) + +#endif // !CONFIG_H diff --git a/app/src/hmi-debug.h b/app/src/hmi-debug.h new file mode 100644 index 0000000..96bbd1f --- /dev/null +++ b/app/src/hmi-debug.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HMI_DEBUG_H__ +#define __HMI_DEBUG_H__ + +#include +#include +#include +#include +#include + +enum LOG_LEVEL { + LOG_LEVEL_NONE = 0, + LOG_LEVEL_ERROR, + LOG_LEVEL_WARNING, + LOG_LEVEL_NOTICE, + LOG_LEVEL_INFO, + LOG_LEVEL_DEBUG, + LOG_LEVEL_MAX = LOG_LEVEL_DEBUG +}; + +#define __FILENAME__ \ + (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) + +#define HMI_ERROR(prefix, args, ...) \ + _HMI_LOG(LOG_LEVEL_ERROR, __FILENAME__, __FUNCTION__, __LINE__, prefix, \ + args, ##__VA_ARGS__) +#define HMI_WARNING(prefix, args, ...) \ + _HMI_LOG(LOG_LEVEL_WARNING, __FILENAME__, __FUNCTION__, __LINE__, prefix, \ + args, ##__VA_ARGS__) +#define HMI_NOTICE(prefix, args, ...) \ + _HMI_LOG(LOG_LEVEL_NOTICE, __FILENAME__, __FUNCTION__, __LINE__, prefix, \ + args, ##__VA_ARGS__) +#define HMI_INFO(prefix, args, ...) \ + _HMI_LOG(LOG_LEVEL_INFO, __FILENAME__, __FUNCTION__, __LINE__, prefix, args, \ + ##__VA_ARGS__) +#define HMI_DEBUG(prefix, args, ...) \ + _HMI_LOG(LOG_LEVEL_DEBUG, __FILENAME__, __FUNCTION__, __LINE__, prefix, \ + args, ##__VA_ARGS__) + +static char ERROR_FLAG[6][20] = {"NONE", "ERROR", "WARNING", + "NOTICE", "INFO", "DEBUG"}; + +static void _HMI_LOG(enum LOG_LEVEL level, + const char* file, + const char* func, + const int line, + const char* prefix, + const char* log, + ...) { + const int log_level = (getenv("USE_HMI_DEBUG") == NULL) + ? LOG_LEVEL_ERROR + : atoi(getenv("USE_HMI_DEBUG")); + if (log_level < level) { + return; + } + + char* message; + struct timespec tp; + unsigned int time; + + clock_gettime(CLOCK_REALTIME, &tp); + time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000); + + va_list args; + va_start(args, log); + if (log == NULL || vasprintf(&message, log, args) < 0) + message = NULL; + fprintf(stderr, "[%10.3f] [%s %s] [%s, %s(), Line:%d] >>> %s \n", + time / 1000.0, prefix, ERROR_FLAG[level], file, func, line, message); + va_end(args); + free(message); +} + +#endif //__HMI_DEBUG_H__ diff --git a/app/src/httpclient.cpp b/app/src/httpclient.cpp new file mode 100644 index 0000000..490fd79 --- /dev/null +++ b/app/src/httpclient.cpp @@ -0,0 +1,267 @@ + +#include "httpclient.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class HttpClientPrivate { + public: + HttpClientPrivate(const QString& url); + + QString url; + QUrlQuery params; + QHash 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 successHandler, + std::function 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 successHandler, + std::function 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 successHandler, + std::function errorHandler, + const char* encoding) { + HttpClientPrivate::get(d, HttpClientPrivate::GET, successHandler, + errorHandler, encoding); +} + +void HttpClient::download( + const QString& savePath, + std::function successHandler, + std::function errorHandler, + std::function 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 readyRead, + std::function successHandler, + std::function errorHandler, + std::function 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 successHandler, + std::function 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 > 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 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 successHandler, + std::function errorHandler, + QNetworkReply* reply, + QNetworkAccessManager* manager) { + if (reply->error() == QNetworkReply::NoError) { + if (debug) { + qDebug().noquote() + << QString("request successed: %1").arg(successMessage); + } + + if (NULL != successHandler) { + successHandler(successMessage); + } + } else { + if (debug) { + qDebug().noquote() << QString("request failed: %1").arg(errorMessage); + } + + if (NULL != errorHandler) { + errorHandler(errorMessage); + } + } + + reply->deleteLater(); + if (NULL != manager) { + manager->deleteLater(); + } +} diff --git a/app/src/httpclient.h b/app/src/httpclient.h new file mode 100644 index 0000000..42f002c --- /dev/null +++ b/app/src/httpclient.h @@ -0,0 +1,48 @@ + +#ifndef HTTPCLIENT_H +#define HTTPCLIENT_H + +#include +#include + +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 successHandler, + std::function errorHandler = NULL, + const char* encoding = "UTF-8"); + + void download( + const QString& savePath, + std::function successHandler = NULL, + std::function errorHandler = NULL, + std::function progressHandler = NULL); + + void download( + std::function readyRead, + std::function successHandler = NULL, + std::function errorHandler = NULL, + std::function progressHandler = NULL); + + private: + HttpClientPrivate* d; +}; + +#endif // !HTTPCLIENT_H diff --git a/app/src/main.cpp b/app/src/main.cpp new file mode 100644 index 0000000..6dc6822 --- /dev/null +++ b/app/src/main.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2018 The Qt Company Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 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", 1, 0, "NativeAppModel"); + qmlRegisterType("ServerAppModel", 1, 0, "ServerAppModel"); + + QLibHomeScreen* homescreenHandler = new QLibHomeScreen(); + QLibWindowmanager* qwm = new QLibWindowmanager(); + + // WindowManager + if (qwm->init(port, secret) != 0) { + exit(EXIT_FAILURE); + } + + AGLScreenInfo screenInfo(qwm->get_scale_factor()); + + // Request a surface as described in layers.json windowmanager’s file + if (qwm->requestSurface(role) != 0) { + exit(EXIT_FAILURE); + } + + // Create an event callback against an event type. Here a lambda is + // called when SyncDraw event occurs + qwm->set_event_handler(QLibWindowmanager::Event_SyncDraw, + [qwm, role](json_object* object) { + fprintf(stderr, "Surface got syncDraw!\n"); + + qwm->endDraw(role); + }); + + // HomeScreen + homescreenHandler->init(port, token.c_str()); + // Set the event handler for Event_TapShortcut which will activate the + // surface for windowmanager + homescreenHandler->set_event_handler( + QLibHomeScreen::Event_TapShortcut, [qwm, role](json_object* object) { + qDebug("Surface warehouse got tapShortcut.\n"); + qwm->activateWindow(role); + }); + + context->setContextProperty(QStringLiteral("homescreenHandler"), + homescreenHandler); + context->setContextProperty(QStringLiteral("screenInfo"), &screenInfo); + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + QObject* root = engine.rootObjects().first(); + + QQuickWindow* window = qobject_cast(root); + QObject::connect(window, SIGNAL(frameSwapped()), qwm, + SLOT(slotActivateSurface())); + } + return app.exec(); +} diff --git a/app/src/nativeappmodel.cpp b/app/src/nativeappmodel.cpp new file mode 100644 index 0000000..a5fa42d --- /dev/null +++ b/app/src/nativeappmodel.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2016 The Qt Company Ltd. + * Copyright (C) 2016, 2017 Mentor Graphics Development (Deutschland) GmbH + * Copyright (c) 2018 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "nativeappmodel.h" +#include +#include +#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 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 NativeAppModel::roleNames() const { + QHash roles; + roles[IconRole] = "icon"; + roles[NameRole] = "name"; + roles[IdRole] = "id"; + roles[VersionRole] = "version"; + roles[DescriptionRole] = "description"; + roles[ShortNameRole] = "shortname"; + roles[AuthorRole] = "author"; + return roles; +} + +QString NativeAppModel::id(int i) const { + return data(index(i), IdRole).toString(); +} + +QString NativeAppModel::name(int i) const { + return data(index(i), NameRole).toString(); +} + +void NativeAppModel::appChanged(const QString& info) { + this->refresh(); +} + +int NativeAppModel::launch(const QString& application) { + int result = -1; + HMI_DEBUG("launch", "ApplicationLauncher launch %s.", + application.toStdString().c_str()); + + result = afm_user_daemon_proxy->start(application).value().toInt(); + HMI_DEBUG("launch", "ApplicationLauncher pid: %d.", result); + + return result; +} + +void NativeAppModel::uninstall(int index) { + const QString& id = this->d->data[index].id(); + QString result = afm_user_daemon_proxy->uninstall(id); + if (result == "null") { + beginRemoveRows(QModelIndex(), index, index); + this->d->data.removeAt(index); + endRemoveRows(); + } +} + +void NativeAppModel::refresh() { + beginResetModel(); + this->d->data.clear(); + this->d->getApps(); + endResetModel(); + emit applistChanged(this->d->data); +} diff --git a/app/src/nativeappmodel.h b/app/src/nativeappmodel.h new file mode 100644 index 0000000..f49121a --- /dev/null +++ b/app/src/nativeappmodel.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016 The Qt Company Ltd. + * Copyright (c) 2018 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NATIVEAPPMODE_H +#define NATIVEAPPMODE_H + +#include +#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 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& applist); + + private: + class Private; + Private* d; +}; + +#endif // NATIVEAPPMODE_H diff --git a/app/src/serverappmodel.cpp b/app/src/serverappmodel.cpp new file mode 100644 index 0000000..ad79159 --- /dev/null +++ b/app/src/serverappmodel.cpp @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2016 The Qt Company Ltd. + * Copyright (C) 2016, 2017 Mentor Graphics Development (Deutschland) GmbH + * Copyright (c) 2018 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "serverappmodel.h" +#include +#include +#include "afm_user_daemon_proxy.h" +#include "httpclient.h" + +#include "hmi-debug.h" + +extern org::AGL::afm::user* afm_user_daemon_proxy; + +ServerAppModel::ServerAppModel(QObject* parent) : QAbstractListModel(parent) { + // this->getAppPage(0, PAGE_SIZE); + connect(afm_user_daemon_proxy, &org::AGL::afm::user::changed, this, + &ServerAppModel::appChanged); +} + +ServerAppModel::~ServerAppModel() {} + +int ServerAppModel::rowCount(const QModelIndex& parent) const { + if (parent.isValid()) + return 0; + + return this->applist.count(); +} + +QVariant ServerAppModel::data(const QModelIndex& index, int role) const { + QVariant ret; + if (!index.isValid()) + return ret; + + switch (role) { + case IconRole: + ret = this->applist[index.row()].iconPath(); + break; + case NameRole: + ret = this->applist[index.row()].name(); + break; + case IdRole: + ret = this->applist[index.row()].id(); + break; + case VersionRole: + ret = this->applist[index.row()].version(); + break; + case DescriptionRole: + ret = this->applist[index.row()].description(); + break; + case AuthorRole: + ret = this->applist[index.row()].author(); + break; + case ServerIdRole: + ret = this->applist[index.row()].serverId(); + break; + case CategoryNameRole: + ret = this->applist[index.row()].categoryName(); + break; + case CreatedTimeRole: + ret = this->applist[index.row()].createdTime(); + break; + case StateRole: + ret = this->applist[index.row()].state(); + break; + case StateTextRole: + switch (this->applist[index.row()].state()) { + case AppInfo::Install: + ret = "Install"; + break; + case AppInfo::Update: + ret = "Update"; + break; + case AppInfo::Launch: + ret = "Launch"; + break; + case AppInfo::Downloading: + ret = "Downloading"; + break; + case AppInfo::Installing: + ret = "Installing"; + break; + default: + break; + } + break; + case ProgressRole: + ret = this->applist[index.row()].progress(); + break; + default: + break; + } + + return ret; +} + +QHash ServerAppModel::roleNames() const { + QHash roles; + roles[IconRole] = "icon"; + roles[NameRole] = "name"; + roles[IdRole] = "id"; + roles[VersionRole] = "version"; + roles[DescriptionRole] = "description"; + roles[AuthorRole] = "author"; + roles[ServerIdRole] = "appid"; + roles[CategoryNameRole] = "category"; + roles[CreatedTimeRole] = "createdtime"; + roles[StateRole] = "state"; + roles[StateTextRole] = "statetext"; + roles[ProgressRole] = "progress"; + return roles; +} + +QString ServerAppModel::id(int i) const { + return data(index(i), IdRole).toString(); +} + +QString ServerAppModel::name(int i) const { + return data(index(i), NameRole).toString(); +} + +QString ServerAppModel::stateText(int i) const { + return data(index(i), StateTextRole).toString(); +} + +int ServerAppModel::launch(const QString& application) { + int result = -1; + HMI_DEBUG("launch", "ApplicationLauncher launch %s.", + application.toStdString().c_str()); + + result = afm_user_daemon_proxy->start(application).value().toInt(); + HMI_DEBUG("launch", "ApplicationLauncher pid: %d.", result); + + return result; +} + +void ServerAppModel::appChanged(const QString& info) { + QJsonDocument japps = QJsonDocument::fromJson(info.toUtf8()); + QJsonObject const& jso = japps.object(); + QString operation = jso["operation"].toString(); + QString id = jso["data"].toString(); + if (operation == "uninstall") { + for (int i = 0; i < applist.size(); ++i) { + if (applist.at(i).id() == id) { + beginResetModel(); + applist[i].setState(AppInfo::Install); + endResetModel(); + break; + } + } + } +} + +void ServerAppModel::install(int index) { + AppInfo& appinfo = applist[index]; + + beginResetModel(); + appinfo.setState(AppInfo::Downloading); + endResetModel(); + + QString url = + getWgtUrl(appinfo.wgtPath()); + HttpClient client(url); + + client.download( + getDownloadFilePath(appinfo.fileName()), + [this, &appinfo](const QString& result) { + this->beginResetModel(); + appinfo.setState(AppInfo::Installing); + this->endResetModel(); + + QTimer::singleShot(3000, this, [this, &appinfo] { + QString installResult = afm_user_daemon_proxy->install( + getDownloadFilePath(appinfo.fileName())); + + this->beginResetModel(); + appinfo.setState(installResult.isEmpty() ? AppInfo::Install + : AppInfo::Launch); + appinfo.setProgress(0.0); + this->endResetModel(); + }); + }, + [this, &appinfo](const QString& error) { + HMI_ERROR("ERROR", "%s", error.toStdString().c_str()); + this->beginResetModel(); + appinfo.setState(AppInfo::Install); + appinfo.setProgress(0.0); + this->beginResetModel(); + }, + [this, &appinfo](const qint64 bytesReceived, const qint64 bytesTotal) { + qreal progressValue = ((qreal)bytesReceived / bytesTotal) * 100; + this->beginResetModel(); + appinfo.setProgress(progressValue); + this->endResetModel(); + }); +} + +void ServerAppModel::getPrevPage(int pageIndex) { + this->getAppPage(pageIndex, PAGE_SIZE, true); +} + +void ServerAppModel::getNextPage(int pageIndex) { + this->getAppPage(pageIndex, PAGE_SIZE); +} + +void ServerAppModel::setNativeApplist(const QList& applist) { + nativeApplist.clear(); + nativeApplist = applist; + checkAppState(); +} + +void ServerAppModel::checkAppState() { + if (applist.isEmpty() || nativeApplist.isEmpty()) { + return; + } + + beginResetModel(); + + QList::iterator it; + for (it = applist.begin(); it != applist.end(); ++it) { + QList::iterator nit; + for (nit = nativeApplist.begin(); nit != nativeApplist.end(); ++nit) { + if ((*it).id() == (*nit).id()) { + (*it).setState((*it).version() != (*nit).version() ? AppInfo::Update + : AppInfo::Launch); + break; + } + } + } + + endResetModel(); +} + +void ServerAppModel::getAppPage(int pageIndex, int pageSize, bool isPrev) { + // QString url = getUrlWithPage(SERVER_API_LIST, pageIndex, pageSize) + // .append(QString("&appDeviceTypeId=%1&appDeveloper=%2") + // .arg(QString::number(0), "zhang_xu")); + QString url = + getUrlWithPage(SERVER_API_LIST, pageIndex, pageSize) + .append(QString("&appDeviceTypeId=%1").arg(QString::number(0))); + HttpClient client(url); + + client.get( + [=](const QString& result) { + QJsonDocument jresult = QJsonDocument::fromJson(result.toUtf8()); + QJsonObject const& jro = jresult.object(); + QJsonArray const& jappList = jro["pagination_data"].toArray(); + + QList newList; + for (auto const& app : jappList) { + QJsonObject const& jso = app.toObject(); + + AppInfo appinfo; + appinfo.readFromServer(jso); + newList.append(appinfo); + } + + beginResetModel(); + if (isPrev || jappList.size() >= PAGE_SIZE) { + applist.clear(); + } + applist += newList; + endResetModel(); + + checkAppState(); + + emit requestCompleted(jappList.size(), PAGE_SIZE); + }, + [=](const QString& msg) { + HMI_ERROR("response", "response error: %s", msg.toStdString().c_str()); + }); +} diff --git a/app/src/serverappmodel.h b/app/src/serverappmodel.h new file mode 100644 index 0000000..ea3608f --- /dev/null +++ b/app/src/serverappmodel.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016 The Qt Company Ltd. + * Copyright (c) 2018 TOYOTA MOTOR CORPORATION + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SERVERAPPMODEL_H +#define SERVERAPPMODEL_H + +#include +#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 roleNames() const override; + Q_INVOKABLE QString id(int index) const; + Q_INVOKABLE QString name(int index) const; + Q_INVOKABLE QString stateText(int index) const; + Q_INVOKABLE void install(int index); + Q_INVOKABLE int launch(const QString& application); + + Q_INVOKABLE void getPrevPage(int pageIndex); + Q_INVOKABLE void getNextPage(int pageIndex); + + Q_INVOKABLE void setNativeApplist(const QList& applist); + + void getAppPage(int pageIndex, int pageSize, bool isPrev = false); + + void appChanged(const QString& info); + + signals: + void requestCompleted(int appsize, int pagesize); + + private: + void checkAppState(); + + QList applist; + QList 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 -- cgit