From fd1fbfdbdddaa659e8148e4ecce7ec33fb8b8ae0 Mon Sep 17 00:00:00 2001 From: zheng_wenlong Date: Thu, 22 Nov 2018 14:30:38 +0900 Subject: dynamic update applist --- .gitignore | 4 +- launcher/launcher.pro | 8 +- launcher/qml/Launcher.qml | 18 +- launcher/qml/images/images.qrc | 4 + launcher/qml/images/sdl_active.svg | 140 +++++++++++ launcher/qml/images/sdl_inactive.svg | 83 +++++++ launcher/qml/images/webbrowser_active.svg | 368 ++++++++++++++++++++++++++++ launcher/qml/images/webbrowser_inactive.svg | 311 +++++++++++++++++++++++ launcher/src/appfwhandler.cpp | 212 ++++++++++++++++ launcher/src/appfwhandler.h | 76 ++++++ launcher/src/applicationmodel.cpp | 68 ++++- launcher/src/applicationmodel.h | 1 + launcher/src/main.cpp | 5 + package/config.xml | 4 + 14 files changed, 1287 insertions(+), 15 deletions(-) create mode 100644 launcher/qml/images/sdl_active.svg create mode 100644 launcher/qml/images/sdl_inactive.svg create mode 100644 launcher/qml/images/webbrowser_active.svg create mode 100644 launcher/qml/images/webbrowser_inactive.svg create mode 100644 launcher/src/appfwhandler.cpp create mode 100644 launcher/src/appfwhandler.h diff --git a/.gitignore b/.gitignore index 567609b..684dcbc 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -build/ +build +*.pro.user +app/config.tests diff --git a/launcher/launcher.pro b/launcher/launcher.pro index 7fd1319..1130052 100644 --- a/launcher/launcher.pro +++ b/launcher/launcher.pro @@ -18,7 +18,7 @@ TARGET = launcher QT = qml quick dbus websockets CONFIG += c++11 link_pkgconfig DESTDIR = $${OUT_PWD}/../package/root/bin -PKGCONFIG += qlibwindowmanager qlibhomescreen +PKGCONFIG += qlibwindowmanager qlibhomescreen libafbwsc include(../interfaces/interfaces.pri) @@ -26,12 +26,14 @@ SOURCES += \ src/main.cpp \ src/applicationmodel.cpp \ src/appinfo.cpp \ - src/applicationlauncher.cpp + src/applicationlauncher.cpp \ + src/appfwhandler.cpp HEADERS += \ src/applicationlauncher.h \ src/applicationmodel.h \ - src/appinfo.h + src/appinfo.h \ + src/appfwhandler.h OTHER_FILES += \ README.md diff --git a/launcher/qml/Launcher.qml b/launcher/qml/Launcher.qml index 6756589..7ad3f38 100644 --- a/launcher/qml/Launcher.qml +++ b/launcher/qml/Launcher.qml @@ -59,6 +59,14 @@ ApplicationWindow { height: grid.cellHeight } + Connections { + target: appfwhandler + onApplistupdate: { + console.warn("applist update in Launcher.qml") + applicationModel.updateApplist(info); + } + } + MouseArea { id: loc anchors.fill: parent @@ -67,15 +75,15 @@ ApplicationWindow { property int index: grid.indexAt(loc.mouseX, loc.mouseY) x: 62 y: 264 - anchors.rightMargin: 0 - anchors.bottomMargin: 0 - anchors.leftMargin: 0 - anchors.topMargin: 0 onPressAndHold: currentId = applicationModel.id(newIndex = index) onReleased: { + if(loc.index < 0) { + return + } if (currentId === '') { pid = launcher.launch(applicationModel.id(loc.index)) if (1 < pid) { + homescreenHandler.tapShortcut(applicationModel.name(loc.index)) } else { console.warn("app cannot be launched!") @@ -83,7 +91,7 @@ ApplicationWindow { } else { currentId = '' } - homescreenHandler.tapShortcut(applicationModel.name(loc.index)) + //homescreenHandler.tapShortcut(applicationModel.name(loc.index)) } onPositionChanged: { if (loc.currentId === '') return diff --git a/launcher/qml/images/images.qrc b/launcher/qml/images/images.qrc index b3a1cf6..64b5f9c 100644 --- a/launcher/qml/images/images.qrc +++ b/launcher/qml/images/images.qrc @@ -27,9 +27,13 @@ video_inactive.svg browser_active.svg browser_inactive.svg + sdl_active.svg + sdl_inactive.svg blank_active.svg blank_inactive.svg plus_active.svg plus_inactive.svg + webbrowser_active.svg + webbrowser_inactive.svg diff --git a/launcher/qml/images/sdl_active.svg b/launcher/qml/images/sdl_active.svg new file mode 100644 index 0000000..efe3fe7 --- /dev/null +++ b/launcher/qml/images/sdl_active.svg @@ -0,0 +1,140 @@ + + + +image/svg+xml diff --git a/launcher/qml/images/sdl_inactive.svg b/launcher/qml/images/sdl_inactive.svg new file mode 100644 index 0000000..dcc077a --- /dev/null +++ b/launcher/qml/images/sdl_inactive.svg @@ -0,0 +1,83 @@ + + + +image/svg+xml diff --git a/launcher/qml/images/webbrowser_active.svg b/launcher/qml/images/webbrowser_active.svg new file mode 100644 index 0000000..6bb1ac5 --- /dev/null +++ b/launcher/qml/images/webbrowser_active.svg @@ -0,0 +1,368 @@ + + + +image/svg+xml diff --git a/launcher/qml/images/webbrowser_inactive.svg b/launcher/qml/images/webbrowser_inactive.svg new file mode 100644 index 0000000..e4a87aa --- /dev/null +++ b/launcher/qml/images/webbrowser_inactive.svg @@ -0,0 +1,311 @@ + + + +image/svg+xml diff --git a/launcher/src/appfwhandler.cpp b/launcher/src/appfwhandler.cpp new file mode 100644 index 0000000..9df6d7d --- /dev/null +++ b/launcher/src/appfwhandler.cpp @@ -0,0 +1,212 @@ +/* + * 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 +#include +#include "appfwhandler.h" +#include "hmi-debug.h" + +AppFwHandler* AppFwHandler::myself = nullptr; + +// called when pws hangsup +static void _on_pws_hangup(void *closure) +{ + if(AppFwHandler::myself) + AppFwHandler::myself->on_pws_hangup(); +} + +static void _on_pws_reply(void *closure, void *request, struct json_object *obj, const char *error, const char *info) +{ + HMI_DEBUG("AppFwHandler", "%s called,error=[%s], info=[%s], obj=[%s]", __FUNCTION__, error, info, json_object_to_json_string(obj)); + if(json_object_is_type(obj, json_type_object)) { + struct json_object *obj1, *obj2, *obj3; + json_object_object_get_ex(obj, "icon", &obj1); + json_object_object_get_ex(obj, "name", &obj2); + json_object_object_get_ex(obj, "id", &obj3); + if(json_object_is_type(obj3, json_type_null)) + return; + QString icon = json_object_get_string(obj1); + QString name = json_object_get_string(obj2); + QString id = json_object_get_string(obj3); + QStringList info; + info << icon << name << id; + emit AppFwHandler::myself->applistupdate(info); + } +} + +static void _on_pws_event_broadcast(void *closure, const char *event_name, struct json_object *data) +{ + HMI_DEBUG("AppFwHandler", "%s called,event=%s, [%s]", __FUNCTION__, event_name, json_object_to_json_string(data)); + QStringList list = QString(event_name).split('/'); + if(list[0] == "afm-main" && list[1] == "application-list-changed") { + struct json_object *obj1, *obj2; + json_object_object_get_ex(data, "operation", &obj1); + json_object_object_get_ex(data, "data", &obj2); + QString oper = json_object_get_string(obj1); + QString id = json_object_get_string(obj2); + + if(oper == "uninstall") { + QStringList info; + // icon, name, id + info << "" << "" << id; + emit AppFwHandler::myself->applistupdate(info); + } + else if (oper == "install") { + // call state + AppFwHandler::myself->detail(id); + } + else { + HMI_DEBUG("AppFwHandler","data error"); + } + } +} + +// the callback interface for pws +static struct afb_proto_ws_client_itf pws_itf = { + .on_reply = _on_pws_reply, + .on_event_create = nullptr, + .on_event_remove = nullptr, + .on_event_subscribe = nullptr, + .on_event_unsubscribe = nullptr, + .on_event_push = nullptr, + .on_event_broadcast = _on_pws_event_broadcast, +}; + +AppFwHandler::AppFwHandler(const char* appname, QObject *parent) : QObject(parent) +{ + myself = this; + int uid = getuid(); + QString _uid; + if(uid == 0) + _uid = QString('0'); + else + _uid = QString(uid); + + m_sessionid = _uid + QString(appname); + m_uri = "unix:/run/user/" + _uid + "/apis/ws/afm-main"; + HMI_NOTICE("AppFwHandler","m_uri=%s, m_sessionid=%s", m_uri.toStdString().c_str(), m_sessionid.toStdString().c_str()); +} + +int AppFwHandler::init(void) +{ + // get default loop + int rc = sd_event_default(&m_evloop); + if(rc < 0) + { + HMI_ERROR("AppFwHandler", "can't create event loop"); + return 1; + } + + // connect to framework + if (!try_connect_pws()) { + HMI_ERROR("connection to %s failed: %m\n", m_uri.toStdString().c_str()); + return 1; + } +// runnables(); + return 0; +} + +int AppFwHandler::runnables(void) +{ + int ret = 1; + if(call(__FUNCTION__, "{\"info\":\"test my guess\"}") < 0) + ret = 0; + return ret; +} + +int AppFwHandler::detail(QString id) +{ + int ret = 1; + HMI_DEBUG("AppFwHandler", "detail id is %s\n", id.toStdString().c_str()); + if(call(__FUNCTION__, id.toStdString().c_str()) < 0) + ret = 0; + return ret; +} + +int AppFwHandler::try_connect_pws(void) +{ + m_pws = afb_ws_client_connect_api(m_evloop, m_uri.toStdString().c_str(), &pws_itf, NULL); + if (m_pws == nullptr) { + HMI_ERROR("AppFwHandler", "connection to %s failed!\n", m_uri.toStdString().c_str()); + return 0; + } + afb_proto_ws_on_hangup(m_pws, _on_pws_hangup); + return 1; +} + +void AppFwHandler::on_pws_hangup(void) +{ + struct afb_proto_ws *apw = m_pws; + m_pws = nullptr; + afb_proto_ws_unref(apw); + attempt_connect_pws(10); +} + +void AppFwHandler::attempt_connect_pws(int count) +{ + if(m_time != nullptr) { + HMI_NOTICE("AppFwHandler", "attempt_connect_pws retrying!\n"); + return; + } + if(count > 0) + m_retry = count; + else + return; + + m_time = new QTimer(this); + connect(m_time, SIGNAL(timeout()), this, SLOT(connect_pws_timer_slot())); + m_time->start(5000); +} + +void AppFwHandler::connect_pws_timer_slot(void) +{ + --m_retry; + int ret = try_connect_pws(); + if(ret) { + m_retry = 0; + disconnect(m_time, 0, 0, 0); + delete m_time; + m_time = nullptr; + } + else { + if(m_retry > 0) + m_time->start(5000); + } +} + +int AppFwHandler::call(const char *verb, const char *object) +{ + static int num = 0; + if(verb == nullptr) { + HMI_NOTICE("AppFwHandler", "parameter is null!\n"); + return 0; + } + num++; + + QString key = QString(num) + ':' + QString(verb); + enum json_tokener_error jerr; + struct json_object *obj = json_tokener_parse_verbose(object, &jerr); + if (jerr != json_tokener_success) + obj = json_object_new_string(object); + + int rc = afb_proto_ws_client_call(m_pws, verb, obj, m_sessionid.toStdString().c_str(), key.toLatin1().data(), NULL); + json_object_put(obj); + if (rc < 0) { + HMI_ERROR("AppFwHandler", "calling %s(%s) failed!\n", verb, object); + } + + return rc; +} diff --git a/launcher/src/appfwhandler.h b/launcher/src/appfwhandler.h new file mode 100644 index 0000000..25a401b --- /dev/null +++ b/launcher/src/appfwhandler.h @@ -0,0 +1,76 @@ +/* + * 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 APPFWHANDLER_H +#define APPFWHANDLER_H + +#include +#include +#include +#include +#include +extern "C" { +#include +#include +} + +class AppFwHandler : public QObject +{ + Q_OBJECT +public: + AppFwHandler(const char* appname, QObject *parent = nullptr); + AppFwHandler(AppFwHandler&) = delete; + AppFwHandler &operator=(AppFwHandler&) = delete; + ~AppFwHandler() = default; + + int init(void); + int runnables(void); + int detail(QString id); + void on_pws_hangup(void); + void registerCallback( + /* can't be NULL */ + void (*on_reply_cb)(void *closure, void *request, struct json_object *obj, const char *error, const char *info), + + /* can be NULL */ + void (*on_event_create_cb)(void *closure, const char *event_name, int event_id) = nullptr, + void (*on_event_remove_cb)(void *closure, const char *event_name, int event_id) = nullptr, + void (*on_event_subscribe_cb)(void *closure, void *request, const char *event_name, int event_id) = nullptr, + void (*on_event_unsubscribe_cb)(void *closure, void *request, const char *event_name, int event_id) = nullptr, + void (*on_event_push_cb)(void *closure, const char *event_name, int event_id, struct json_object *data) = nullptr, + void (*on_event_broadcast_cb)(void *closure, const char *event_name, struct json_object *data) = nullptr); + + static AppFwHandler* myself; +signals: + void applistupdate(QStringList info); + +private slots: + void connect_pws_timer_slot(void); + +private: + int try_connect_pws(void); + void attempt_connect_pws(int count); + int call(const char *verb, const char *object); + + QString m_api = "afm-main"; + QString m_uri; + QString m_sessionid; + sd_event *m_evloop = nullptr; + afb_proto_ws *m_pws = nullptr; + QTimer* m_time; + int m_retry = 0; +}; + +#endif // APPFWHANDLER_H diff --git a/launcher/src/applicationmodel.cpp b/launcher/src/applicationmodel.cpp index 090a300..003fe61 100644 --- a/launcher/src/applicationmodel.cpp +++ b/launcher/src/applicationmodel.cpp @@ -33,6 +33,9 @@ class ApplicationModel::Private public: Private(); + void addApp(QString icon, QString name, QString id); + void removeApp(QString id); + QList data; }; @@ -61,17 +64,46 @@ ApplicationModel::Private::Private() auto const icon = get_icon_name(jso); // Hide HomeScreen icon itself - if (name != "launcher" && - name != "homescreen-2017" && - name != "homescreen" && - !name.contains("OnScreen", Qt::CaseInsensitive)) { - this->data.append(AppInfo(icon, name, id)); - } + if (name != "launcher" && + name != "homescreen-2017" && + name != "homescreen" && + name != "restriction" && + !name.contains("OnScreen", Qt::CaseInsensitive)) { + this->data.append(AppInfo(icon, name, id)); + } HMI_DEBUG("launcher","name: %s icon: %s id: %s.", name.toStdString().c_str(), icon.toStdString().c_str(), id.toStdString().c_str()); } } +void ApplicationModel::Private::addApp(QString icon, QString name, QString id) +{ + HMI_DEBUG("addApp","name: %s icon: %s id: %s.", name.toStdString().c_str(), icon.toStdString().c_str(), id.toStdString().c_str()); + for(int i = 0; i < this->data.size(); ++i) { + if(this->data[i].id() == id) + return; + } + + QString _icon = name.toLower(); + if ( !QFile::exists(QString(":/images/%1_active.svg").arg(_icon)) || + !QFile::exists(QString(":/images/%1_inactive.svg").arg(_icon)) ) + { + _icon = "blank"; + } + this->data.append(AppInfo(_icon, name, id)); +} + +void ApplicationModel::Private::removeApp(QString id) +{ + HMI_DEBUG("removeApp","id: %s.",id.toStdString().c_str()); + for (int i = 0; i < this->data.size(); ++i) { + if (this->data.at(i).id() == id) { + this->data.removeAt(i); + break; + } + } +} + ApplicationModel::ApplicationModel(QObject *parent) : QAbstractListModel(parent) , d(new Private()) @@ -155,3 +187,27 @@ void ApplicationModel::move(int from, int to) HMI_NOTICE("launcher","from : %d, to : %d. false.", from, to); } } + +void ApplicationModel::updateApplist(QStringList info) +{ + QString icon = info.at(0); + QString name = info.at(1); + QString id = info.at(2); + QString appid = id.split('@')[0]; + + if ( appid == "launcher" || + appid == "homescreen-2017" || + appid == "homescreen" || + appid.contains("onscreen", Qt::CaseInsensitive)) { + return; + } + beginResetModel(); + if(icon == "") { // uninstall + d->removeApp(id); + } + else { + // new app + d->addApp(icon, name, id); + } + endResetModel(); +} diff --git a/launcher/src/applicationmodel.h b/launcher/src/applicationmodel.h index 8398b62..750bf75 100644 --- a/launcher/src/applicationmodel.h +++ b/launcher/src/applicationmodel.h @@ -34,6 +34,7 @@ public: Q_INVOKABLE QString id(int index) const; Q_INVOKABLE QString name(int index) const; Q_INVOKABLE void move(int from, int to); + Q_INVOKABLE void updateApplist(QStringList info); private: class Private; diff --git a/launcher/src/main.cpp b/launcher/src/main.cpp index d79e880..8cdb76a 100644 --- a/launcher/src/main.cpp +++ b/launcher/src/main.cpp @@ -31,6 +31,7 @@ #include "afm_user_daemon_proxy.h" #include "qlibhomescreen.h" #include "hmi-debug.h" +#include "appfwhandler.h" // XXX: We want this DBus connection to be shared across the different // QML objects, is there another way to do this, a nice way, perhaps? @@ -128,6 +129,9 @@ int main(int argc, char *argv[]) layoutHandler->activateSurface(myname); }); + AppFwHandler* appfwhandler = new AppFwHandler(myname.toStdString().c_str()); + appfwhandler->init(); + QUrl bindingAddress; bindingAddress.setScheme(QStringLiteral("ws")); bindingAddress.setHost(QStringLiteral("localhost")); @@ -154,6 +158,7 @@ int main(int argc, char *argv[]) engine.rootContext()->setContextProperty(QStringLiteral("homescreenHandler"), homescreenHandler); engine.rootContext()->setContextProperty(QStringLiteral("launcher"), launcher); engine.rootContext()->setContextProperty(QStringLiteral("screenInfo"), &screenInfo); + engine.rootContext()->setContextProperty(QStringLiteral("appfwhandler"), appfwhandler); engine.load(QUrl(QStringLiteral("qrc:/Launcher.qml"))); QObject *root = engine.rootObjects().first(); diff --git a/package/config.xml b/package/config.xml index 62f99ed..0c7c021 100644 --- a/package/config.xml +++ b/package/config.xml @@ -9,10 +9,14 @@ + + + + -- cgit 1.2.3-korg