on 97b41f2 help sign in
unity8/tests/mocks/Unity/Application/ApplicationManager.cpp
590 lines 20 kB history raw
1/*
2 * Copyright (C) 2013 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"
20#include <paths.h>
22#include <QDir>
23#include <QGuiApplication>
24#include <QQuickItem>
25#include <QQuickView>
26#include <QQmlComponent>
27#include <QTimer>
28#include <QDateTime>
30ApplicationManager::ApplicationManager(QObject *parent)
31 : ApplicationManagerInterface(parent)
32 , m_suspended(false)
33 , m_mainStageComponent(0)
34 , m_mainStage(0)
35 , m_sideStageComponent(0)
36 , m_sideStage(0)
37 , m_rightMargin(0)
39 buildListOfAvailableApplications();
42ApplicationManager::~ApplicationManager()
46int ApplicationManager::rowCount(const QModelIndex& parent) const {
47 return !parent.isValid() ? m_runningApplications.size() : 0;
50QVariant ApplicationManager::data(const QModelIndex& index, int role) const {
51 if (index.row() < 0 || index.row() >= m_runningApplications.size())
52 return QVariant();
54 auto app = m_runningApplications.at(index.row());
55 switch(role) {
56 case RoleAppId:
57 return app->appId();
58 case RoleName:
59 return app->name();
60 case RoleComment:
61 return app->comment();
62 case RoleIcon:
63 return app->icon();
64 case RoleStage:
65 return app->stage();
66 case RoleState:
67 return app->state();
68 case RoleFocused:
69 return app->focused();
70 case RoleScreenshot:
71 return app->screenshot();
72 default:
73 return QVariant();
74 }
77ApplicationInfo *ApplicationManager::get(int row) const {
78 if (row < 0 || row >= m_runningApplications.size())
79 return nullptr;
80 return m_runningApplications.at(row);
83ApplicationInfo *ApplicationManager::findApplication(const QString &appId) const {
84 for (ApplicationInfo *app : m_runningApplications) {
85 if (app->appId() == appId) {
86 return app;
87 }
88 }
89 return nullptr;
92void ApplicationManager::add(ApplicationInfo *application) {
93 if (!application) {
94 return;
95 }
97 beginInsertRows(QModelIndex(), m_runningApplications.size(), m_runningApplications.size());
98 m_runningApplications.append(application);
99 endInsertRows();
100 Q_EMIT applicationAdded(application->appId());
101 Q_EMIT countChanged();
102 Q_EMIT focusRequested(application->appId());
105void ApplicationManager::remove(ApplicationInfo *application) {
106 int i = m_runningApplications.indexOf(application);
107 if (i != -1) {
108 beginRemoveRows(QModelIndex(), i, i);
109 m_runningApplications.removeAt(i);
110 endRemoveRows();
111 Q_EMIT applicationRemoved(application->appId());
112 Q_EMIT countChanged();
113 }
116void ApplicationManager::move(int from, int to) {
117 if (from == to) return;
119 if (from >= 0 && from < m_runningApplications.size() && to >= 0 && to < m_runningApplications.size()) {
120 QModelIndex parent;
121 /* When moving an item down, the destination index needs to be incremented
122 * by one, as explained in the documentation:
123 * http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */
124 beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0));
125 m_runningApplications.move(from, to);
126 endMoveRows();
127 }
130int ApplicationManager::keyboardHeight() const
132 return 0;
135bool ApplicationManager::keyboardVisible() const
137 return false;
140int ApplicationManager::sideStageWidth() const
142 return 0;
145ApplicationManager::StageHint ApplicationManager::stageHint() const
147 return MainStage;
150ApplicationManager::FormFactorHint ApplicationManager::formFactorHint() const
152 return PhoneFormFactor;
155ApplicationInfo* ApplicationManager::startApplication(const QString &appId,
156 const QStringList &arguments)
158 return startApplication(appId, NoFlag, arguments);
161ApplicationInfo* ApplicationManager::startApplication(const QString &appId,
162 ExecFlags flags,
163 const QStringList &arguments)
165 Q_UNUSED(arguments)
166 ApplicationInfo *application = 0;
168 for (ApplicationInfo *availableApp : m_availableApplications) {
169 if (availableApp->appId() == appId) {
170 application = availableApp;
171 break;
172 }
173 }
175 if (!application)
176 return 0;
178 if (flags.testFlag(ApplicationManager::ForceMainStage)
179 && application->stage() == ApplicationInfo::SideStage) {
180 application->setStage(ApplicationInfo::MainStage);
181 }
182 add(application);
184 return application;
187bool ApplicationManager::stopApplication(const QString &appId)
189 ApplicationInfo *application = findApplication(appId);
190 if (application == nullptr)
191 return false;
193 if (application->appId() == focusedApplicationId()) {
194 unfocusCurrentApplication();
195 }
196 remove(application);
197 return true;
200bool ApplicationManager::updateScreenshot(const QString &appId)
202 int idx = -1;
203 ApplicationInfo *application = nullptr;
204 for (int i = 0; i < m_availableApplications.count(); ++i) {
205 application = m_availableApplications.at(i);
206 if (application->appId() == appId) {
207 idx = i;
208 break;
209 }
210 }
212 if (idx == -1) {
213 return false;
214 }
216 application->setScreenshot(QString("image://application/%1/%2").arg(appId).arg(QDateTime::currentMSecsSinceEpoch()));
217 QModelIndex appIndex = index(idx);
218 Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << RoleScreenshot);
219 return true;
222QString ApplicationManager::focusedApplicationId() const {
223 for (ApplicationInfo *app : m_runningApplications) {
224 if (app->focused()) {
225 return app->appId();
226 }
227 }
228 return QString();
231bool ApplicationManager::suspended() const
233 return m_suspended;
236void ApplicationManager::setSuspended(bool suspended)
238 ApplicationInfo *focusedApp = findApplication(focusedApplicationId());
239 if (focusedApp) {
240 if (suspended) {
241 focusedApp->setState(ApplicationInfo::Suspended);
242 } else {
243 focusedApp->setState(ApplicationInfo::Running);
244 }
245 }
246 m_suspended = suspended;
247 Q_EMIT suspendedChanged();
250bool ApplicationManager::focusApplication(const QString &appId)
252 ApplicationInfo *application = findApplication(appId);
253 if (application == nullptr)
254 return false;
256 if (application->stage() == ApplicationInfo::MainStage) {
257 // unfocus currently focused mainstage app
258 for (ApplicationInfo *app : m_runningApplications) {
259 if (app->focused() && app->stage() == ApplicationInfo::MainStage) {
260 app->setFocused(false);
261 app->hideWindow();
262 }
263 }
265 // focus this app
266 application->setFocused(true);
267 if (!m_mainStage)
268 createMainStage();
269 application->showWindow(m_mainStage);
270 m_mainStage->setZ(-1000);
271 if (m_sideStage)
272 m_sideStage->setZ(-2000);
273 } else if (application->stage() == ApplicationInfo::SideStage) {
274 // unfocus currently focused sidestage app
275 for (ApplicationInfo *app : m_runningApplications) {
276 if (app->focused() && app->stage() == ApplicationInfo::SideStage) {
277 app->setFocused(false);
278 app->hideWindow();
279 }
280 }
282 // focus this app
283 application->setFocused(true);
284 if (!m_sideStage)
285 createSideStage();
286 application->showWindow(m_sideStage);
287 m_sideStage->setZ(-1000);
288 if (m_mainStage)
289 m_mainStage->setZ(-2000);
290 }
292 // move app to top of stack
293 move(m_runningApplications.indexOf(application), 0);
294 Q_EMIT focusedApplicationIdChanged();
295 return true;
298bool ApplicationManager::requestFocusApplication(const QString &appId)
300 if (appId != focusedApplicationId()) {
301 QMetaObject::invokeMethod(this, "focusRequested", Qt::QueuedConnection, Q_ARG(QString, appId));
302 return true;
303 }
304 return false;
307void ApplicationManager::unfocusCurrentApplication()
309 for (ApplicationInfo *app : m_runningApplications) {
310 if (app->focused()) {
311 app->hideWindow();
312 app->setFocused(false);
313 }
314 }
315 Q_EMIT focusedApplicationIdChanged();
318void ApplicationManager::generateQmlStrings(ApplicationInfo *application)
320 // TODO: Is there a better way of solving this fullscreen vs. regular
321 // application height?
322 QString topMargin;
323 if (application->fullscreen()) {
324 topMargin.append("0");
325 } else {
326 // Taken from Panel.panelHeight
327 topMargin.append("units.gu(3) + units.dp(2)");
328 }
330 QString windowQml = QString(
331 "import QtQuick 2.0\n"
332 "Image {\n"
333 " anchors.fill: parent\n"
334 " anchors.topMargin: %1\n"
335 " anchors.rightMargin: %2\n"
336 " source: \"file://%3/Dash/graphics/phone/screenshots/%4.png\"\n"
337 " smooth: true\n"
338 " fillMode: Image.PreserveAspectCrop\n"
339 "}").arg(topMargin)
340 .arg(m_rightMargin)
341 .arg(qmlDirectory())
342 .arg(application->icon().toString());
343 application->setWindowQml(windowQml);
345 QString imageQml = QString(
346 "import QtQuick 2.0\n"
347 "Image {\n"
348 " anchors.fill: parent\n"
349 " source: \"file://%1/Dash/graphics/phone/screenshots/%2.png\"\n"
350 " smooth: true\n"
351 " fillMode: Image.PreserveAspectCrop\n"
352 "}").arg(qmlDirectory())
353 .arg(application->icon().toString());
354 application->setImageQml(imageQml);
357void ApplicationManager::buildListOfAvailableApplications()
359 ApplicationInfo *application;
361 application = new ApplicationInfo(this);
362 application->setAppId("phone-app");
363 application->setName("Phone");
364 application->setIcon(QUrl("phone"));
365 application->setStage(ApplicationInfo::SideStage);
366 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
367 generateQmlStrings(application);
368 m_availableApplications.append(application);
370 application = new ApplicationInfo(this);
371 application->setAppId("camera-app");
372 application->setName("Camera");
373 application->setIcon(QUrl("camera"));
374 application->setFullscreen(true);
375 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
376 generateQmlStrings(application);
377 m_availableApplications.append(application);
379 application = new ApplicationInfo(this);
380 application->setAppId("gallery-app");
381 application->setName("Gallery");
382 application->setIcon(QUrl("gallery"));
383 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
384 generateQmlStrings(application);
385 m_availableApplications.append(application);
387 application = new ApplicationInfo(this);
388 application->setAppId("facebook-webapp");
389 application->setName("Facebook");
390 application->setIcon(QUrl("facebook"));
391 application->setStage(ApplicationInfo::SideStage);
392 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
393 generateQmlStrings(application);
394 m_availableApplications.append(application);
396 application = new ApplicationInfo(this);
397 application->setAppId("webbrowser-app");
398 application->setFullscreen(true);
399 application->setName("Browser");
400 application->setIcon(QUrl("browser"));
401 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
402 generateQmlStrings(application);
403 m_availableApplications.append(application);
405 application = new ApplicationInfo(this);
406 application->setAppId("twitter-webapp");
407 application->setName("Twitter");
408 application->setIcon(QUrl("twitter"));
409 application->setStage(ApplicationInfo::SideStage);
410 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
411 generateQmlStrings(application);
412 m_availableApplications.append(application);
414 application = new ApplicationInfo(this);
415 application->setAppId("gmail-webapp");
416 application->setName("GMail");
417 application->setIcon(QUrl("gmail"));
418 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
419 m_availableApplications.append(application);
421 application = new ApplicationInfo(this);
422 application->setAppId("ubuntu-weather-app");
423 application->setName("Weather");
424 application->setIcon(QUrl("weather"));
425 application->setStage(ApplicationInfo::SideStage);
426 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
427 generateQmlStrings(application);
428 m_availableApplications.append(application);
430 application = new ApplicationInfo(this);
431 application->setAppId("notes-app");
432 application->setName("Notepad");
433 application->setIcon(QUrl("notepad"));
434 application->setStage(ApplicationInfo::SideStage);
435 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
436 m_availableApplications.append(application);
438 application = new ApplicationInfo(this);
439 application->setAppId("calendar-app");
440 application->setName("Calendar");
441 application->setIcon(QUrl("calendar"));
442 application->setStage(ApplicationInfo::SideStage);
443 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
444 m_availableApplications.append(application);
446 application = new ApplicationInfo(this);
447 application->setAppId("mediaplayer-app");
448 application->setName("Media Player");
449 application->setIcon(QUrl("mediaplayer-app"));
450 application->setFullscreen(true);
451 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
452 m_availableApplications.append(application);
454 application = new ApplicationInfo(this);
455 application->setAppId("evernote");
456 application->setName("Evernote");
457 application->setIcon(QUrl("evernote"));
458 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
459 m_availableApplications.append(application);
461 application = new ApplicationInfo(this);
462 application->setAppId("map");
463 application->setName("Map");
464 application->setIcon(QUrl("map"));
465 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
466 generateQmlStrings(application);
467 m_availableApplications.append(application);
469 application = new ApplicationInfo(this);
470 application->setAppId("pinterest");
471 application->setName("Pinterest");
472 application->setIcon(QUrl("pinterest"));
473 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
474 m_availableApplications.append(application);
476 application = new ApplicationInfo(this);
477 application->setAppId("soundcloud");
478 application->setName("SoundCloud");
479 application->setIcon(QUrl("soundcloud"));
480 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
481 m_availableApplications.append(application);
483 application = new ApplicationInfo(this);
484 application->setAppId("wikipedia");
485 application->setName("Wikipedia");
486 application->setIcon(QUrl("wikipedia"));
487 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
488 m_availableApplications.append(application);
490 application = new ApplicationInfo(this);
491 application->setAppId("youtube");
492 application->setName("YouTube");
493 application->setIcon(QUrl("youtube"));
494 application->setScreenshot(QString("image://application/%1/%2").arg(application->appId()).arg(QDateTime::currentMSecsSinceEpoch()));
495 m_availableApplications.append(application);
498void ApplicationManager::createMainStageComponent()
500 // The assumptions I make here really should hold.
501 QQuickView *quickView =
502 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
504 QQmlEngine *engine = quickView->engine();
506 m_mainStageComponent = new QQmlComponent(engine, this);
507 QString mainStageQml =
508 "import QtQuick 2.0\n"
509 "Rectangle {\n"
510 " anchors.fill: parent\n"
511 " color: 'black'\n"
512 " z: -2000\n"
513 "}\n";
514 m_mainStageComponent->setData(mainStageQml.toLatin1(), QUrl());
517void ApplicationManager::createMainStage()
519 if (!m_mainStageComponent)
520 createMainStageComponent();
522 // The assumptions I make here really should hold.
523 QQuickView *quickView =
524 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
526 QQuickItem *shell = quickView->rootObject();
528 m_mainStage = qobject_cast<QQuickItem *>(m_mainStageComponent->create());
529 m_mainStage->setParentItem(shell);
532void ApplicationManager::createSideStageComponent()
534 // The assumptions I make here really should hold.
535 QQuickView *quickView =
536 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
538 QQmlEngine *engine = quickView->engine();
540 m_sideStageComponent = new QQmlComponent(engine, this);
541 QString sideStageQml =
542 "import QtQuick 2.0\n"
543 "import Ubuntu.Components 0.1\n"
544 "Item {\n"
545 " width: units.gu(40)\n" // from SideStage in Shell.qml
546 " anchors.top: parent.top\n"
547 " anchors.bottom: parent.bottom\n"
548 " anchors.right: parent.right\n"
549 " z: -1000\n"
550 "}\n";
551 m_sideStageComponent->setData(sideStageQml.toLatin1(), QUrl());
554void ApplicationManager::createSideStage()
556 if (!m_sideStageComponent)
557 createSideStageComponent();
559 // The assumptions I make here really should hold.
560 QQuickView *quickView =
561 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
563 QQuickItem *shell = quickView->rootObject();
565 m_sideStage = qobject_cast<QQuickItem *>(m_sideStageComponent->create());
566 m_sideStage->setParentItem(shell);
567 m_sideStage->setFlag(QQuickItem::ItemHasContents, false);
570QStringList ApplicationManager::availableApplications()
572 QStringList appIds;
573 Q_FOREACH(ApplicationInfo *app, m_availableApplications) {
574 appIds << app->appId();
575 }
576 return appIds;
579int ApplicationManager::rightMargin() const
581 return m_rightMargin;
584void ApplicationManager::setRightMargin(int rightMargin)
586 m_rightMargin = rightMargin;
587 Q_FOREACH(ApplicationInfo *app, m_availableApplications) {
588 generateQmlStrings(app);
589 }