AglShellGrpcClient: Add activation with gRPC proxy
[apps/homescreen.git] / homescreen / src / homescreenhandler.cpp
1 // SPDX-License-Identifier: Apache-2.0
2 /*
3  * Copyright (c) 2017, 2018, 2019 TOYOTA MOTOR CORPORATION
4  * Copyright (c) 2022 Konsulko Group
5  */
6
7 #include <QGuiApplication>
8 #include <QScreen>
9 #include <QFileInfo>
10 #include <functional>
11
12 #include "homescreenhandler.h"
13 #include "hmi-debug.h"
14
15 QScreen *find_screen(const char *output);
16
17 // defined by meson build file
18 #include QT_QPA_HEADER
19
20 // LAUNCHER_APP_ID shouldn't be started by applaunchd as it is started as
21 // a user session by systemd
22 #define LAUNCHER_APP_ID          "launcher"
23
24
25 HomescreenHandler::HomescreenHandler(ApplicationLauncher *launcher, GrpcClient *_client, QObject *parent) :
26         QObject(parent)
27         , m_grpc_client(_client)
28 {
29         mp_launcher = launcher;
30         mp_applauncher_client = new AppLauncherClient();
31
32         //
33         // The "started" event is received any time a start request is made to applaunchd,
34         // and the application either starts successfully or is already running. This
35         // effectively acts as a "switch to app X" action.
36         //
37         connect(mp_applauncher_client,
38                 &AppLauncherClient::appStatusEvent,
39                 this,
40                 &HomescreenHandler::processAppStatusEvent);
41 }
42
43 HomescreenHandler::~HomescreenHandler()
44 {
45         delete m_grpc_client;
46         delete mp_applauncher_client;
47 }
48
49 void HomescreenHandler::tapShortcut(QString app_id)
50 {
51         HMI_DEBUG("HomeScreen","tapShortcut %s", app_id.toStdString().c_str());
52
53         if (app_id == LAUNCHER_APP_ID) {
54                 activateApp(app_id);
55                 return;
56         }
57
58         if (!mp_applauncher_client->startApplication(app_id)) {
59                 HMI_ERROR("HomeScreen","Unable to start application '%s'",
60                           app_id.toStdString().c_str());
61                 return;
62         }
63 }
64
65 /*
66  * Keep track of currently running apps and the order in which
67  * they were activated. That way, when an app is closed, we can
68  * switch back to the previously active one.
69  */
70 void HomescreenHandler::addAppToStack(const QString& app_id)
71 {
72         if (app_id == "homescreen")
73                 return;
74
75         if (!apps_stack.contains(app_id)) {
76                 apps_stack << app_id;
77         } else {
78                 int current_pos = apps_stack.indexOf(app_id);
79                 int last_pos = apps_stack.size() - 1;
80
81                 if (current_pos != last_pos)
82                         apps_stack.move(current_pos, last_pos);
83         }
84 }
85
86 void HomescreenHandler::activateApp(const QString& app_id)
87 {
88         QScreen *default_screen = qApp->screens().first();
89         std::string default_output_name;
90
91         if (!default_screen) {
92                 HMI_DEBUG("HomeScreen", "No default output found to activate on!\n");
93         } else {
94                 default_output_name = default_screen->name().toStdString();
95                 HMI_DEBUG("HomeScreen", "Activating app_id %s by default on output %s\n",
96                                 app_id.toStdString().c_str(), default_output_name.c_str());
97         }
98
99         if (mp_launcher) {
100                 mp_launcher->setCurrent(app_id);
101         }
102
103         // search for a pending application which might have a different output
104         auto iter = pending_app_list.begin();
105         bool found_pending_app = false;
106         while (iter != pending_app_list.end()) {
107                 const QString &app_to_search = iter->first;
108
109                 if (app_to_search == app_id) {
110                         found_pending_app = true;
111                         HMI_DEBUG("HomeScreen", "Found app_id %s in pending list  of applications",
112                                         app_id.toStdString().c_str());
113                         break;
114                 }
115
116                 iter++;
117         }
118
119         if (found_pending_app) {
120                 const QString &output_name = iter->second;
121                 QScreen *screen =
122                         ::find_screen(output_name.toStdString().c_str());
123
124                 if (!screen) {
125                         HMI_DEBUG("HomeScreen", "Can't activate application %s on another "
126                                   "output, because output %s could not be found. "
127                                   "Trying with remoting ones.",
128                                   app_id.toStdString().c_str(),
129                                   output_name.toStdString().c_str());
130
131                         // try with remoting-remote-X which is the streaming
132                         // one
133                         std::string new_remote_output = 
134                                 "remoting-" + output_name.toStdString();
135
136                         screen = ::find_screen(new_remote_output.c_str());
137                         if (!screen) {
138                                 HMI_DEBUG("HomeScreen", "Can't activate application %s on another "
139                                           "output, because output remoting-%s could not be found",
140                                           app_id.toStdString().c_str(),
141                                           output_name.toStdString().c_str());
142                                 return;
143                         }
144
145                         HMI_DEBUG("HomeScreen", "Found a stream remoting output %s to activate application %s on",
146                                   new_remote_output.c_str(),
147                                   app_id.toStdString().c_str());
148                         default_output_name = new_remote_output;
149                 }
150
151                 pending_app_list.erase(iter);
152                 HMI_DEBUG("HomeScreen", "For application %s found another "
153                                 "output to activate %s\n",
154                                 app_id.toStdString().c_str(),
155                                 default_output_name.c_str());
156         }
157
158         if (default_output_name.empty()) {
159                 HMI_DEBUG("HomeScreen", "No suitable output found for activating %s",
160                                 app_id.toStdString().c_str());
161                 return;
162         }
163
164         HMI_DEBUG("HomeScreen", "Activating application %s on output %s",
165                         app_id.toStdString().c_str(), default_output_name.c_str());
166         m_grpc_client->ActivateApp(app_id.toStdString(), default_output_name);
167 }
168
169 void HomescreenHandler::deactivateApp(const QString& app_id)
170 {
171         if (apps_stack.contains(app_id)) {
172                 apps_stack.removeOne(app_id);
173                 if (!apps_stack.isEmpty())
174                         activateApp(apps_stack.last());
175         }
176 }
177
178 void HomescreenHandler::processAppStatusEvent(const QString &app_id, const QString &status)
179 {
180         HMI_DEBUG("HomeScreen", "Processing application %s, status %s",
181                         app_id.toStdString().c_str(), status.toStdString().c_str());
182
183         if (status == "started") {
184                 activateApp(app_id);
185         } else if (status == "terminated") {
186                 HMI_DEBUG("HomeScreen", "Application %s terminated, activating last app", app_id.toStdString().c_str());
187                 deactivateApp(app_id);
188         } else if (status == "deactivated") {
189                 HMI_DEBUG("HomeScreen", "Application %s deactivated, activating last app", app_id.toStdString().c_str());
190         }
191 }