diff options
Diffstat (limited to 'applauncher')
-rw-r--r-- | applauncher/AppLauncherClient.cpp | 41 | ||||
-rw-r--r-- | applauncher/AppLauncherClient.h | 36 | ||||
-rw-r--r-- | applauncher/AppLauncherGrpcClient.cpp | 113 | ||||
-rw-r--r-- | applauncher/AppLauncherGrpcClient.h | 55 | ||||
-rw-r--r-- | applauncher/meson.build | 56 | ||||
-rw-r--r-- | applauncher/protos/applauncher.proto | 50 |
6 files changed, 351 insertions, 0 deletions
diff --git a/applauncher/AppLauncherClient.cpp b/applauncher/AppLauncherClient.cpp new file mode 100644 index 0000000..bbbcef3 --- /dev/null +++ b/applauncher/AppLauncherClient.cpp @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + * Copyright (C) 2022 Konsulko Group + */ + +#include <QDebug> + +#include "AppLauncherClient.h" +#include "AppLauncherGrpcClient.h" + +AppLauncherClient::AppLauncherClient(QObject *parent) : QObject(parent) +{ + m_launcher = new AppLauncherGrpcClient(this); +} + +AppLauncherClient::~AppLauncherClient() +{ + delete m_launcher; +} + +bool AppLauncherClient::startApplication(const QString &id) +{ + if (m_launcher) + return m_launcher->StartApplication(id); + + return false; +} + +bool AppLauncherClient::listApplications(QList<QMap<QString, QString>> &list) +{ + if (!m_launcher) { + return false; + } + + return m_launcher->ListApplications(list); +} + +void AppLauncherClient::sendStatusEvent(const QString &id, const QString &status) +{ + emit appStatusEvent(id, status); +} diff --git a/applauncher/AppLauncherClient.h b/applauncher/AppLauncherClient.h new file mode 100644 index 0000000..91723ab --- /dev/null +++ b/applauncher/AppLauncherClient.h @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + * Copyright (C) 2022 Konsulko Group + */ + +#ifndef APPLAUNCHER_CLIENT_H +#define APPLAUNCHER_CLIENT_H + +#include <QObject> +#include <QList> +#include <QMap> + +class AppLauncherGrpcClient; + +class AppLauncherClient : public QObject +{ + Q_OBJECT + +public: + explicit AppLauncherClient(QObject *parent = Q_NULLPTR); + virtual ~AppLauncherClient(); + + Q_INVOKABLE bool startApplication(const QString &id); + Q_INVOKABLE bool listApplications(QList<QMap<QString, QString>> &list); + +public slots: + void sendStatusEvent(const QString &id, const QString &status); + +signals: + void appStatusEvent(const QString &id, const QString &status); + +private: + AppLauncherGrpcClient *m_launcher; +}; + +#endif // APPLAUNCHER_CLIENT_H diff --git a/applauncher/AppLauncherGrpcClient.cpp b/applauncher/AppLauncherGrpcClient.cpp new file mode 100644 index 0000000..a25b148 --- /dev/null +++ b/applauncher/AppLauncherGrpcClient.cpp @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + * Copyright (C) 2022 Konsulko Group + */ + +#include <QDebug> +#include "AppLauncherGrpcClient.h" +#include "AppLauncherClient.h" + +using grpc::Channel; +using grpc::ClientContext; +using grpc::ClientReader; +using grpc::Status; + +using automotivegradelinux::AppLauncher; +using automotivegradelinux::StartRequest; +using automotivegradelinux::StartResponse; +using automotivegradelinux::ListRequest; +using automotivegradelinux::ListResponse; +using automotivegradelinux::AppInfo; +using automotivegradelinux::StatusRequest; +using automotivegradelinux::StatusResponse; +using automotivegradelinux::AppStatus; + + +void AppStatusEventReader::GetStatusEvents() +{ + ClientContext context; + StatusRequest request; + StatusResponse response; + + std::unique_ptr<ClientReader<StatusResponse> > reader(stub_->GetStatusEvents(&context, request)); + while (reader->Read(&response)) { + if (response.has_app()) { + AppStatus app_status = response.app(); + emit statusUpdate(QString::fromStdString(app_status.id()), + QString::fromStdString(app_status.status())); + } + } + Status status = reader->Finish(); + if (!status.ok()) { + qWarning() << "GetStatusEvents RPC failed"; + } + + emit finished(); +} + +AppLauncherGrpcClient::AppLauncherGrpcClient(QObject *parent) : QObject(parent) +{ + stub_ = AppLauncher::NewStub(grpc::CreateChannel("localhost:50052", grpc::InsecureChannelCredentials())); + + // Create thread to read status events + AppStatusEventReader *reader = new AppStatusEventReader(stub_); + reader->moveToThread(&m_event_thread); + connect(&m_event_thread, &QThread::started, reader, &AppStatusEventReader::GetStatusEvents); + connect(reader, &AppStatusEventReader::finished, &m_event_thread, &QThread::quit); + // FIXME: Normally the thread finishing would be connected per the below + // to trigger cleaning up the object. That seems to trigger a crash + // for not entirely obvious reasons. It seems unrelated to the signal + // connection to AppLauncherClient, as not connecting does not prevent + // the crash; further investigation is required. + //connect(reader, &AppStatusEventReader::finished, reader, &AppStatusEventReader::deleteLater); + //connect(&m_event_thread, &QThread::finished, &m_event_thread, &QThread::deleteLater); + + // To avoid having an intermediary slot+signal in this class, try + // casting parent to AppLauncherClient and connect directly to its + // slot. Callers should set parent to ensure this works as required. + if (parent) { + AppLauncherClient *launcher = qobject_cast<AppLauncherClient*>(parent); + if (launcher) + connect(reader, + &AppStatusEventReader::statusUpdate, + launcher, + &AppLauncherClient::sendStatusEvent); + } + + // Start status event handling + m_event_thread.start(); +} + +bool AppLauncherGrpcClient::StartApplication(const QString &id) +{ + StartRequest request; + request.set_id(id.toStdString()); + + ClientContext context; + StartResponse response; + Status status = stub_->StartApplication(&context, request, &response); + + return status.ok(); +} + +bool AppLauncherGrpcClient::ListApplications(QList<QMap<QString, QString>> &list) +{ + ListRequest request; // empty + ClientContext context; + ListResponse response; + + Status status = stub_->ListApplications(&context, request, &response); + if (!status.ok()) + return false; + + for (int i = 0; i < response.apps_size(); i++) { + QMap<QString, QString> appinfo_map; + AppInfo appinfo = response.apps(i); + appinfo_map["id"] = QString::fromStdString(appinfo.id()); + appinfo_map["name"] = QString::fromStdString(appinfo.name()); + appinfo_map["icon_path"] = QString::fromStdString(appinfo.icon_path()); + list.append(appinfo_map); + } + + return true; +} diff --git a/applauncher/AppLauncherGrpcClient.h b/applauncher/AppLauncherGrpcClient.h new file mode 100644 index 0000000..f9e1d0f --- /dev/null +++ b/applauncher/AppLauncherGrpcClient.h @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + * Copyright (C) 2022 Konsulko Group + */ + +#ifndef APPLAUNCHER_GRPC_CLIENT_H +#define APPLAUNCHER_GRPC_CLIENT_H + +#include <QObject> +#include <QList> +#include <QMap> +#include <QThread> +#include <grpcpp/grpcpp.h> + +#include "applauncher.grpc.pb.h" + +using grpc::Channel; + +class AppStatusEventReader : public QObject +{ + Q_OBJECT +public: + AppStatusEventReader(std::shared_ptr<automotivegradelinux::AppLauncher::Stub> &stub, + QObject *parent = Q_NULLPTR) : QObject(parent), stub_(stub) {} + +public slots: + void GetStatusEvents(); + +signals: + void statusUpdate(const QString &id, const QString &status); + void finished(); + +private: + std::shared_ptr<automotivegradelinux::AppLauncher::Stub> stub_; +}; + +class AppLauncherGrpcClient : public QObject +{ + Q_OBJECT + +public: + AppLauncherGrpcClient(QObject *parent = Q_NULLPTR); + + bool StartApplication(const QString &id); + + bool ListApplications(QList<QMap<QString, QString>> &list); + +private: + std::shared_ptr<automotivegradelinux::AppLauncher::Stub> stub_; + + QThread m_event_thread; + +}; + +#endif // APPLAUNCHER_GRPC_CLIENT_H diff --git a/applauncher/meson.build b/applauncher/meson.build new file mode 100644 index 0000000..b36184a --- /dev/null +++ b/applauncher/meson.build @@ -0,0 +1,56 @@ +cpp = meson.get_compiler('cpp') +grpcpp_reflection_dep = cpp.find_library('grpc++_reflection') + +qt5_dep = dependency('qt5', modules: ['Qml']) +applauncher_dep = [ + qt5_dep, + dependency('protobuf'), + dependency('grpc'), + dependency('grpc++'), + grpcpp_reflection_dep, +] + +protoc = find_program('protoc') +grpc_cpp = find_program('grpc_cpp_plugin') + +protoc_gen = generator(protoc, \ + output : ['@BASENAME@.pb.cc', '@BASENAME@.pb.h'], + arguments : ['--proto_path=@CURRENT_SOURCE_DIR@/protos', + '--cpp_out=@BUILD_DIR@', + '@INPUT@']) +generated_protoc_sources = protoc_gen.process('protos/applauncher.proto') + +grpc_gen = generator(protoc, \ + output : ['@BASENAME@.grpc.pb.cc', '@BASENAME@.grpc.pb.h'], + arguments : ['--proto_path=@CURRENT_SOURCE_DIR@/protos', + '--grpc_out=@BUILD_DIR@', + '--plugin=protoc-gen-grpc=' + grpc_cpp.path(), + '@INPUT@']) +generated_grpc_sources = grpc_gen.process('protos/applauncher.proto') + +moc_files = qt5.compile_moc(headers : ['AppLauncherClient.h', 'AppLauncherGrpcClient.h'], + dependencies: qt5_dep) + +src = [ + 'AppLauncherClient.cpp', + 'AppLauncherGrpcClient.cpp', + generated_protoc_sources, + generated_grpc_sources, + moc_files +] +lib = shared_library('qtappfw-applauncher', + sources: src, + version: '1.0.0', + soversion: '0', + dependencies: applauncher_dep, + install: true) + +install_headers('AppLauncherClient.h') + +pkg_mod = import('pkgconfig') +pkg_mod.generate(libraries : lib, + version : '1.0', + name : 'libqtappfw-applauncher', + filebase : 'qtappfw-applauncher', + requires: 'Qt5Qml', + description : 'Library wrapping AGL AppLauncher API in Qt objects') diff --git a/applauncher/protos/applauncher.proto b/applauncher/protos/applauncher.proto new file mode 100644 index 0000000..0b8e0fc --- /dev/null +++ b/applauncher/protos/applauncher.proto @@ -0,0 +1,50 @@ +syntax = "proto3"; + +package automotivegradelinux; + +service AppLauncher { + rpc StartApplication(StartRequest) returns (StartResponse) {} + rpc ListApplications(ListRequest) returns (ListResponse) {} + rpc GetStatusEvents(StatusRequest) returns (stream StatusResponse) {} +} + +message StartRequest { + string id = 1; +} + +message StartResponse { + bool status = 1; + string message = 2; +} + +message ListRequest { +} + +message ListResponse { + repeated AppInfo apps = 1; +} + +message AppInfo { + string id = 1; + string name = 2; + string icon_path = 3; +} + +message StatusRequest { +} + +message AppStatus { + string id = 1; + string status = 2; +} + +// Future-proofing for e.g. potentially signaling a list refresh +message LauncherStatus { +} + +message StatusResponse { + oneof status { + AppStatus app = 1; + LauncherStatus launcher = 2; + } +} |