on 3ebd113 help sign in
unity8/tests/mocks/Unity/Application/ApplicationManager.cpp
476 lines 16 kB history raw
1/*
2 * Copyright (C) 2013-2015 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
17#include "ApplicationManager.h"
18#include "ApplicationInfo.h"
19#include "Session.h"
20#include "ApplicationTestInterface.h"
22#include <paths.h>
23#include <csignal>
25#include <QDir>
26#include <QGuiApplication>
27#include <QQuickItem>
28#include <QQuickView>
29#include <QQmlComponent>
30#include <QTimer>
31#include <QDateTime>
32#include <QtDBus/QtDBus>
34ApplicationManager *ApplicationManager::the_application_manager = nullptr;
36ApplicationManager *ApplicationManager::singleton()
38 if (!the_application_manager) {
39 the_application_manager = new ApplicationManager();
41 // Emit signal to notify Upstart that Mir is ready to receive client connections
42 // see http://upstart.ubuntu.com/cookbook/#expect-stop
43 if (qgetenv("UNITY_MIR_EMITS_SIGSTOP") == "1") {
44 raise(SIGSTOP);
45 }
46 }
47 return the_application_manager;
50ApplicationManager::ApplicationManager(QObject *parent)
51 : ApplicationManagerInterface(parent)
53 m_roleNames.insert(RoleSession, "session");
54 m_roleNames.insert(RoleFullscreen, "fullscreen");
56 buildListOfAvailableApplications();
58 // polling to find out when the toplevel window has been created as there's
59 // no signal telling us that
60 connect(&m_windowCreatedTimer, &QTimer::timeout,
61 this, &ApplicationManager::onWindowCreatedTimerTimeout);
62 m_windowCreatedTimer.setSingleShot(false);
63 m_windowCreatedTimer.start(200);
66ApplicationManager::~ApplicationManager()
70void ApplicationManager::onWindowCreatedTimerTimeout()
72 if (QGuiApplication::topLevelWindows().count() > 0) {
73 m_windowCreatedTimer.stop();
74 onWindowCreated();
75 }
78void ApplicationManager::onWindowCreated()
80 startApplication("unity8-dash");
81 focusApplication("unity8-dash");
84int ApplicationManager::rowCount(const QModelIndex& parent) const {
85 return !parent.isValid() ? m_runningApplications.size() : 0;
88QVariant ApplicationManager::data(const QModelIndex& index, int role) const {
89 if (index.row() < 0 || index.row() >= m_runningApplications.size())
90 return QVariant();
92 auto app = m_runningApplications.at(index.row());
93 switch(role) {
94 case RoleAppId:
95 return app->appId();
96 case RoleName:
97 return app->name();
98 case RoleComment:
99 return app->comment();
100 case RoleIcon:
101 return app->icon();
102 case RoleStage:
103 return app->stage();
104 case RoleState:
105 return app->state();
106 case RoleFocused:
107 return app->focused();
108 case RoleSession:
109 return QVariant::fromValue(app->session());
110 case RoleFullscreen:
111 return app->fullscreen();
112 default:
113 return QVariant();
114 }
117ApplicationInfo *ApplicationManager::get(int row) const {
118 if (row < 0 || row >= m_runningApplications.size())
119 return nullptr;
120 return m_runningApplications.at(row);
123ApplicationInfo *ApplicationManager::findApplication(const QString &appId) const {
124 for (ApplicationInfo *app : m_runningApplications) {
125 if (app->appId() == appId) {
126 return app;
127 }
128 }
129 return nullptr;
132QModelIndex ApplicationManager::findIndex(ApplicationInfo* application)
134 for (int i = 0; i < m_runningApplications.size(); ++i) {
135 if (m_runningApplications.at(i) == application) {
136 return index(i);
137 }
138 }
140 return QModelIndex();
143void ApplicationManager::add(ApplicationInfo *application) {
144 if (!application || m_runningApplications.contains(application)) {
145 return;
146 }
148 beginInsertRows(QModelIndex(), m_runningApplications.size(), m_runningApplications.size());
149 m_runningApplications.append(application);
150 endInsertRows();
151 Q_EMIT applicationAdded(application->appId());
152 Q_EMIT countChanged();
153 if (count() == 1) Q_EMIT emptyChanged(isEmpty()); // was empty but not anymore
155 connect(application, &ApplicationInfo::sessionChanged, this, [application, this]() {
156 QModelIndex appIndex = findIndex(application);
157 if (!appIndex.isValid()) return;
158 Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << ApplicationManager::RoleSession);
159 });
160 connect(application, &ApplicationInfo::focusedChanged, this, [application, this]() {
161 QModelIndex appIndex = findIndex(application);
162 if (!appIndex.isValid()) return;
163 Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << ApplicationManager::RoleFocused);
164 });
165 connect(application, &ApplicationInfo::stateChanged, this, [application, this]() {
166 QModelIndex appIndex = findIndex(application);
167 if (!appIndex.isValid()) return;
168 Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << ApplicationManager::RoleState);
169 });
172void ApplicationManager::remove(ApplicationInfo *application) {
173 int i = m_runningApplications.indexOf(application);
174 if (i != -1) {
175 beginRemoveRows(QModelIndex(), i, i);
176 m_runningApplications.removeAt(i);
177 endRemoveRows();
178 Q_EMIT applicationRemoved(application->appId());
179 Q_EMIT countChanged();
180 if (isEmpty()) Q_EMIT emptyChanged(isEmpty());
181 }
182 application->disconnect(this);
185void ApplicationManager::move(int from, int to) {
186 if (from == to) return;
188 if (from >= 0 && from < m_runningApplications.size() && to >= 0 && to < m_runningApplications.size()) {
189 QModelIndex parent;
190 /* When moving an item down, the destination index needs to be incremented
191 * by one, as explained in the documentation:
192 * http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */
193 beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0));
194 m_runningApplications.move(from, to);
195 endMoveRows();
196 }
199ApplicationInfo* ApplicationManager::startApplication(const QString &appId,
200 const QStringList &arguments)
202 return startApplication(appId, NoFlag, arguments);
205ApplicationInfo* ApplicationManager::startApplication(const QString &appId,
206 ExecFlags flags,
207 const QStringList &arguments)
209 Q_UNUSED(arguments)
210 ApplicationInfo *application = add(appId);
211 if (!application)
212 return 0;
214 if (flags.testFlag(ApplicationManager::ForceMainStage)
215 && application->stage() == ApplicationInfo::SideStage) {
216 application->setStage(ApplicationInfo::MainStage);
217 }
218 application->setState(ApplicationInfo::Starting);
220 return application;
223ApplicationInfo* ApplicationManager::add(QString appId)
225 ApplicationInfo *application = 0;
227 for (ApplicationInfo *availableApp : m_availableApplications) {
228 if (availableApp->appId() == appId) {
229 application = availableApp;
230 break;
231 }
232 }
234 if (application)
235 add(application);
237 return application;
240bool ApplicationManager::stopApplication(const QString &appId)
242 ApplicationInfo *application = findApplication(appId);
243 if (application == nullptr)
244 return false;
246 if (application->appId() == focusedApplicationId()) {
247 unfocusCurrentApplication();
248 }
249 application->setState(ApplicationInfo::Stopped);
250 remove(application);
251 return true;
254QString ApplicationManager::focusedApplicationId() const {
255 for (ApplicationInfo *app : m_runningApplications) {
256 if (app->focused()) {
257 return app->appId();
258 }
259 }
260 return QString();
263bool ApplicationManager::focusApplication(const QString &appId)
265 ApplicationInfo *application = findApplication(appId);
266 if (application == nullptr)
267 return false;
269 // unfocus currently focused app
270 for (ApplicationInfo *app : m_runningApplications) {
271 if (app->focused()) {
272 app->setFocused(false);
273 }
274 }
276 // focus this app
277 application->setFocused(true);
279 // move app to top of stack
280 move(m_runningApplications.indexOf(application), 0);
281 Q_EMIT focusedApplicationIdChanged();
282 return true;
285bool ApplicationManager::requestFocusApplication(const QString &appId)
287 QMetaObject::invokeMethod(this, "focusRequested", Qt::QueuedConnection, Q_ARG(QString, appId));
288 return true;
291void ApplicationManager::unfocusCurrentApplication()
293 for (ApplicationInfo *app : m_runningApplications) {
294 if (app->focused()) {
295 app->setFocused(false);
296 }
297 }
298 Q_EMIT focusedApplicationIdChanged();
301void ApplicationManager::buildListOfAvailableApplications()
303 /*
304 ATTENTION!
305 Be careful when changing application properties here as some qmltests
306 rely on them being the way it's specified here (e.g. that camera-app
307 is fullscreen, that twitter-webapp can rotate in all directions, etc)
308 */
310 ApplicationInfo *application;
312 application = new ApplicationInfo(this);
313 application->setAppId("unity8-dash");
314 application->setName("Unity 8 Mock Dash");
315 application->setScreenshotId("unity8-dash");
316 application->setStage(ApplicationInfo::MainStage);
317 application->setSupportedOrientations(Qt::PrimaryOrientation);
318 m_availableApplications.append(application);
320 application = new ApplicationInfo(this);
321 application->setAppId("dialer-app");
322 application->setName("Dialer");
323 application->setScreenshotId("dialer");
324 application->setIconId("dialer-app");
325 application->setStage(ApplicationInfo::SideStage);
326 application->setSupportedOrientations(Qt::PortraitOrientation
327 | Qt::InvertedPortraitOrientation);
328 m_availableApplications.append(application);
330 application = new ApplicationInfo(this);
331 application->setAppId("camera-app");
332 application->setName("Camera");
333 application->setScreenshotId("camera");
334 application->setIconId("camera");
335 application->setFullscreen(true);
336 application->setSupportedOrientations(Qt::PortraitOrientation
337 | Qt::LandscapeOrientation
338 | Qt::InvertedPortraitOrientation
339 | Qt::InvertedLandscapeOrientation);
340 application->setRotatesWindowContents(true);
341 m_availableApplications.append(application);
343 application = new ApplicationInfo(this);
344 application->setAppId("gallery-app");
345 application->setName("Gallery");
346 application->setScreenshotId("gallery");
347 application->setIconId("gallery");
348 application->setFullscreen(true);
349 application->setStage(ApplicationInfo::MainStage);
350 m_availableApplications.append(application);
352 application = new ApplicationInfo(this);
353 application->setAppId("facebook-webapp");
354 application->setName("Facebook");
355 application->setScreenshotId("facebook");
356 application->setIconId("facebook");
357 application->setStage(ApplicationInfo::SideStage);
358 m_availableApplications.append(application);
360 application = new ApplicationInfo(this);
361 application->setAppId("webbrowser-app");
362 application->setFullscreen(true);
363 application->setName("Browser");
364 application->setScreenshotId("browser");
365 application->setIconId("browser");
366 m_availableApplications.append(application);
368 application = new ApplicationInfo(this);
369 application->setAppId("twitter-webapp");
370 application->setName("Twitter");
371 application->setScreenshotId("twitter");
372 application->setIconId("twitter");
373 application->setStage(ApplicationInfo::SideStage);
374 m_availableApplications.append(application);
376 application = new ApplicationInfo(this);
377 application->setAppId("map");
378 application->setName("Map");
379 application->setIconId("map");
380 application->setScreenshotId("map");
381 m_availableApplications.append(application);
383 application = new ApplicationInfo(this);
384 application->setAppId("gmail-webapp");
385 application->setName("GMail");
386 application->setIconId("gmail");
387 application->setScreenshotId("gmail-webapp.svg");
388 application->setFullscreen(false);
389 application->setStage(ApplicationInfo::MainStage);
390 application->setSupportedOrientations(Qt::PortraitOrientation
391 | Qt::LandscapeOrientation
392 | Qt::InvertedPortraitOrientation
393 | Qt::InvertedLandscapeOrientation);
394 m_availableApplications.append(application);
396 application = new ApplicationInfo(this);
397 application->setAppId("music-app");
398 application->setName("Music");
399 application->setIconId("soundcloud");
400 application->setScreenshotId("music");
401 application->setFullscreen(false);
402 application->setStage(ApplicationInfo::MainStage);
403 application->setSupportedOrientations(Qt::PortraitOrientation
404 | Qt::LandscapeOrientation
405 | Qt::InvertedPortraitOrientation
406 | Qt::InvertedLandscapeOrientation);
407 m_availableApplications.append(application);
409 application = new ApplicationInfo(this);
410 application->setAppId("ubuntu-weather-app");
411 application->setName("Weather");
412 application->setIconId("weather");
413 application->setScreenshotId("ubuntu-weather-app.svg");
414 application->setSupportedOrientations(Qt::LandscapeOrientation
415 | Qt::InvertedLandscapeOrientation);
416 m_availableApplications.append(application);
418 application = new ApplicationInfo(this);
419 application->setAppId("notes-app");
420 application->setName("Notepad");
421 application->setIconId("notepad");
422 application->setStage(ApplicationInfo::SideStage);
423 m_availableApplications.append(application);
425 application = new ApplicationInfo(this);
426 application->setAppId("calendar-app");
427 application->setName("Calendar");
428 application->setIconId("calendar");
429 application->setStage(ApplicationInfo::SideStage);
430 m_availableApplications.append(application);
432 application = new ApplicationInfo(this);
433 application->setAppId("evernote");
434 application->setName("Evernote");
435 application->setIconId("evernote");
436 m_availableApplications.append(application);
438 application = new ApplicationInfo(this);
439 application->setAppId("pinterest");
440 application->setName("Pinterest");
441 application->setIconId("pinterest");
442 m_availableApplications.append(application);
444 application = new ApplicationInfo(this);
445 application->setAppId("soundcloud");
446 application->setName("SoundCloud");
447 application->setIconId("soundcloud");
448 m_availableApplications.append(application);
450 application = new ApplicationInfo(this);
451 application->setAppId("wikipedia");
452 application->setName("Wikipedia");
453 application->setIconId("wikipedia");
454 m_availableApplications.append(application);
456 application = new ApplicationInfo(this);
457 application->setAppId("youtube");
458 application->setName("YouTube");
459 application->setIconId("youtube");
460 m_availableApplications.append(application);
464QStringList ApplicationManager::availableApplications()
466 QStringList appIds;
467 Q_FOREACH(ApplicationInfo *app, m_availableApplications) {
468 appIds << app->appId();
469 }
470 return appIds;
473bool ApplicationManager::isEmpty() const
475 return m_runningApplications.isEmpty();