on aefad3f help sign in
unity8/tests/mocks/Unity/Application/ApplicationManager.cpp
519 lines 16 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"
19#include "paths.h"
21#include <QDir>
22#include <QGuiApplication>
23#include <QQuickItem>
24#include <QQuickView>
25#include <QQmlComponent>
26#include <QTimer>
27#include <QDateTime>
28#include <QDebug>
30ApplicationManager::ApplicationManager(QObject *parent)
31 : ApplicationManagerInterface(parent)
32 , m_mainStageComponent(0)
33 , m_mainStage(0)
34 , m_sideStageComponent(0)
35 , m_sideStage(0)
37 buildListOfAvailableApplications();
40ApplicationManager::~ApplicationManager()
44int ApplicationManager::rowCount(const QModelIndex& parent) const {
45 return !parent.isValid() ? m_runningApplications.size() : 0;
48QVariant ApplicationManager::data(const QModelIndex& index, int role) const {
49 if (index.row() < 0 || index.row() >= m_runningApplications.size())
50 return QVariant();
52 auto app = m_runningApplications.at(index.row());
53 switch(role) {
54 case RoleAppId:
55 return app->appId();
56 case RoleName:
57 return app->name();
58 case RoleComment:
59 return app->comment();
60 case RoleIcon:
61 return app->icon();
62 case RoleStage:
63 return app->stage();
64 case RoleState:
65 return app->state();
66 case RoleFocused:
67 return app->focused();
68 default:
69 return QVariant();
70 }
73ApplicationInfo *ApplicationManager::get(int row) const {
74 if (row < 0 || row >= m_runningApplications.size())
75 return nullptr;
76 return m_runningApplications.at(row);
79ApplicationInfo *ApplicationManager::findApplication(const QString &appId) const {
80 for (ApplicationInfo *app : m_runningApplications) {
81 if (app->appId() == appId) {
82 return app;
83 }
84 }
85 return nullptr;
88void ApplicationManager::add(ApplicationInfo *application) {
89 if (!application)
90 return;
92 beginInsertRows(QModelIndex(), m_runningApplications.size(), m_runningApplications.size());
93 m_runningApplications.append(application);
94 endInsertRows();
95 Q_EMIT countChanged();
98void ApplicationManager::remove(ApplicationInfo *application) {
99 int i = m_runningApplications.indexOf(application);
100 if (i != -1) {
101 beginRemoveRows(QModelIndex(), i, i);
102 m_runningApplications.removeAt(i);
103 endRemoveRows();
104 Q_EMIT countChanged();
105 }
108void ApplicationManager::move(int from, int to) {
109 if (from == to) return;
111 if (from >= 0 && from < m_runningApplications.size() && to >= 0 && to < m_runningApplications.size()) {
112 QModelIndex parent;
113 /* When moving an item down, the destination index needs to be incremented
114 * by one, as explained in the documentation:
115 * http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */
116 beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0));
117 m_runningApplications.move(from, to);
118 endMoveRows();
119 }
122int ApplicationManager::keyboardHeight() const
124 return 0;
127bool ApplicationManager::keyboardVisible() const
129 return false;
132int ApplicationManager::sideStageWidth() const
134 return 0;
137ApplicationManager::StageHint ApplicationManager::stageHint() const
139 return MainStage;
142ApplicationManager::FormFactorHint ApplicationManager::formFactorHint() const
144 return PhoneFormFactor;
147ApplicationInfo* ApplicationManager::startApplication(const QString &appId,
148 const QStringList &arguments)
150 return startApplication(appId, NoFlag, arguments);
153ApplicationInfo* ApplicationManager::startApplication(const QString &appId,
154 ExecFlags flags,
155 const QStringList &arguments)
157 Q_UNUSED(arguments)
158 ApplicationInfo *application = 0;
160 for (ApplicationInfo *availableApp : m_availableApplications) {
161 if (availableApp->appId() == appId) {
162 application = availableApp;
163 break;
164 }
165 }
167 if (!application)
168 return 0;
170 if (flags.testFlag(ApplicationManager::ForceMainStage)
171 && application->stage() == ApplicationInfo::SideStage) {
172 application->setStage(ApplicationInfo::MainStage);
173 }
174 add(application);
176 QMetaObject::invokeMethod(this, "focusApplication", Qt::QueuedConnection, Q_ARG(QString, appId));
178 return application;
181bool ApplicationManager::stopApplication(const QString &appId)
183 ApplicationInfo *application = findApplication(appId);
184 if (application == nullptr)
185 return false;
187 remove(application);
188 Q_EMIT focusedApplicationIdChanged();
189 qDebug() << Q_FUNC_INFO << "emitting focusedAppChanged" << focusedApplicationId();
190 return true;
193void ApplicationManager::updateScreenshot(const QString &appId)
195 int idx = -1;
196 ApplicationInfo *application = nullptr;
197 for (int i = 0; i < m_availableApplications.count(); ++i) {
198 application = m_availableApplications.at(i);
199 if (application->appId() == appId) {
200 idx = i;
201 break;
202 }
203 }
205 if (idx == -1) {
206 return;
207 }
209 application->setScreenshot(QString("image://application/%1/%2").arg(appId).arg(QDateTime::currentMSecsSinceEpoch()));
210 QModelIndex appIndex = index(idx);
211 Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << RoleScreenshot);
214QString ApplicationManager::focusedApplicationId() const {
215 for (ApplicationInfo *app : m_runningApplications) {
216 if (app->focused()) {
217 return app->appId();
218 }
219 }
220 return QString();
223bool ApplicationManager::focusApplication(const QString &appId)
225 ApplicationInfo *application = findApplication(appId);
226 if (application == nullptr)
227 return false;
229 if (application->stage() == ApplicationInfo::MainStage) {
230 // unfocus currently focused mainstage app
231 for (ApplicationInfo *app : m_runningApplications) {
232 if (app->focused() && app->stage() == ApplicationInfo::MainStage) {
233 app->setFocused(false);
234 app->hideWindow();
235 }
236 }
238 // focus this app
239 application->setFocused(true);
240 if (!m_mainStage)
241 createMainStage();
242 application->showWindow(m_mainStage);
243 m_mainStage->setZ(-1000);
244 if (m_sideStage)
245 m_sideStage->setZ(-2000);
246 } else if (application->stage() == ApplicationInfo::SideStage) {
247 // unfocus currently focused sidestage app
248 for (ApplicationInfo *app : m_runningApplications) {
249 if (app->focused() && app->stage() == ApplicationInfo::SideStage) {
250 app->setFocused(false);
251 app->hideWindow();
252 }
253 }
255 // focus this app
256 application->setFocused(true);
257 if (!m_sideStage)
258 createSideStage();
259 application->showWindow(m_sideStage);
260 m_sideStage->setZ(-1000);
261 if (m_mainStage)
262 m_mainStage->setZ(-2000);
263 }
265 // move app to top of stack
266 move(m_runningApplications.indexOf(application), 0);
267 qDebug() << Q_FUNC_INFO << "emitting focusedAppChanged" << focusedApplicationId();
268 Q_EMIT focusedApplicationIdChanged();
269 return true;
272void ApplicationManager::activateApplication(const QString &appId)
274 QMetaObject::invokeMethod(this, "requestFocus", Qt::QueuedConnection, Q_ARG(QString, appId));
277void ApplicationManager::unfocusCurrentApplication()
279 for (ApplicationInfo *app : m_runningApplications) {
280 if (app->focused()) {
281 app->hideWindow();
282 app->setFocused(false);
283 }
284 }
285 qDebug() << Q_FUNC_INFO << "emitting focusedAppChanged" << focusedApplicationId();
286 Q_EMIT focusedApplicationIdChanged();
289void ApplicationManager::generateQmlStrings(ApplicationInfo *application)
291 // TODO: Is there a better way of solving this fullscreen vs. regular
292 // application height?
293 QString topMargin;
294 if (application->fullscreen()) {
295 topMargin.append("0");
296 } else {
297 // Taken from Panel.panelHeight
298 topMargin.append("units.gu(3) + units.dp(2)");
299 }
301 QString windowQml = QString(
302 "import QtQuick 2.0\n"
303 "Image {\n"
304 " anchors.fill: parent\n"
305 " anchors.topMargin: %1\n"
306 " source: \"file://%2/Dash/graphics/phone/screenshots/%3.png\"\n"
307 " smooth: true\n"
308 " fillMode: Image.PreserveAspectCrop\n"
309 "}").arg(topMargin)
310 .arg(shellAppDirectory())
311 .arg(application->icon().toString());
312 application->setWindowQml(windowQml);
314 QString imageQml = QString(
315 "import QtQuick 2.0\n"
316 "Image {\n"
317 " anchors.fill: parent\n"
318 " source: \"file://%1/Dash/graphics/phone/screenshots/%2.png\"\n"
319 " smooth: true\n"
320 " fillMode: Image.PreserveAspectCrop\n"
321 "}").arg(shellAppDirectory())
322 .arg(application->icon().toString());
323 application->setImageQml(imageQml);
326void ApplicationManager::buildListOfAvailableApplications()
328 ApplicationInfo *application;
330 application = new ApplicationInfo(this);
331 application->setAppId("phone-app");
332 application->setName("Phone");
333 application->setIcon(QUrl("phone"));
334 application->setStage(ApplicationInfo::SideStage);
335 generateQmlStrings(application);
336 m_availableApplications.append(application);
338 application = new ApplicationInfo(this);
339 application->setAppId("camera-app");
340 application->setName("Camera");
341 application->setIcon(QUrl("camera"));
342 application->setFullscreen(true);
343 generateQmlStrings(application);
344 m_availableApplications.append(application);
346 application = new ApplicationInfo(this);
347 application->setAppId("gallery-app");
348 application->setName("Gallery");
349 application->setIcon(QUrl("gallery"));
350 generateQmlStrings(application);
351 m_availableApplications.append(application);
353 application = new ApplicationInfo(this);
354 application->setAppId("facebook-webapp");
355 application->setName("Facebook");
356 application->setIcon(QUrl("facebook"));
357 application->setStage(ApplicationInfo::SideStage);
358 generateQmlStrings(application);
359 m_availableApplications.append(application);
361 application = new ApplicationInfo(this);
362 application->setAppId("webbrowser-app");
363 application->setName("Browser");
364 application->setIcon(QUrl("browser"));
365 generateQmlStrings(application);
366 m_availableApplications.append(application);
368 application = new ApplicationInfo(this);
369 application->setAppId("twitter-webapp");
370 application->setName("Twitter");
371 application->setIcon(QUrl("twitter"));
372 application->setStage(ApplicationInfo::SideStage);
373 generateQmlStrings(application);
374 m_availableApplications.append(application);
376 application = new ApplicationInfo(this);
377 application->setAppId("gmail-webapp");
378 application->setName("GMail");
379 application->setIcon(QUrl("gmail"));
380 m_availableApplications.append(application);
382 application = new ApplicationInfo(this);
383 application->setAppId("ubuntu-weather-app");
384 application->setName("Weather");
385 application->setIcon(QUrl("weather"));
386 application->setStage(ApplicationInfo::SideStage);
387 generateQmlStrings(application);
388 m_availableApplications.append(application);
390 application = new ApplicationInfo(this);
391 application->setAppId("notes-app");
392 application->setName("Notepad");
393 application->setIcon(QUrl("notepad"));
394 application->setStage(ApplicationInfo::SideStage);
395 m_availableApplications.append(application);
397 application = new ApplicationInfo(this);
398 application->setAppId("calendar-app");
399 application->setName("Calendar");
400 application->setIcon(QUrl("calendar"));
401 application->setStage(ApplicationInfo::SideStage);
402 m_availableApplications.append(application);
404 application = new ApplicationInfo(this);
405 application->setAppId("mediaplayer-app");
406 application->setName("Media Player");
407 application->setIcon(QUrl("mediaplayer-app"));
408 application->setFullscreen(true);
409 m_availableApplications.append(application);
411 application = new ApplicationInfo(this);
412 application->setAppId("evernote");
413 application->setName("Evernote");
414 application->setIcon(QUrl("evernote"));
415 m_availableApplications.append(application);
417 application = new ApplicationInfo(this);
418 application->setAppId("map");
419 application->setName("Map");
420 application->setIcon(QUrl("map"));
421 generateQmlStrings(application);
422 m_availableApplications.append(application);
424 application = new ApplicationInfo(this);
425 application->setAppId("pinterest");
426 application->setName("Pinterest");
427 application->setIcon(QUrl("pinterest"));
428 m_availableApplications.append(application);
430 application = new ApplicationInfo(this);
431 application->setAppId("soundcloud");
432 application->setName("SoundCloud");
433 application->setIcon(QUrl("soundcloud"));
434 m_availableApplications.append(application);
436 application = new ApplicationInfo(this);
437 application->setAppId("wikipedia");
438 application->setName("Wikipedia");
439 application->setIcon(QUrl("wikipedia"));
440 m_availableApplications.append(application);
442 application = new ApplicationInfo(this);
443 application->setAppId("youtube");
444 application->setName("YouTube");
445 application->setIcon(QUrl("youtube"));
446 m_availableApplications.append(application);
449void ApplicationManager::createMainStageComponent()
451 // The assumptions I make here really should hold.
452 QQuickView *quickView =
453 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
455 QQmlEngine *engine = quickView->engine();
457 m_mainStageComponent = new QQmlComponent(engine, this);
458 QString mainStageQml =
459 "import QtQuick 2.0\n"
460 "Rectangle {\n"
461 " anchors.fill: parent\n"
462 " color: 'black'\n"
463 " z: -2000\n"
464 "}\n";
465 m_mainStageComponent->setData(mainStageQml.toLatin1(), QUrl());
468void ApplicationManager::createMainStage()
470 if (!m_mainStageComponent)
471 createMainStageComponent();
473 // The assumptions I make here really should hold.
474 QQuickView *quickView =
475 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
477 QQuickItem *shell = quickView->rootObject();
479 m_mainStage = qobject_cast<QQuickItem *>(m_mainStageComponent->create());
480 m_mainStage->setParentItem(shell);
483void ApplicationManager::createSideStageComponent()
485 // The assumptions I make here really should hold.
486 QQuickView *quickView =
487 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
489 QQmlEngine *engine = quickView->engine();
491 m_sideStageComponent = new QQmlComponent(engine, this);
492 QString sideStageQml =
493 "import QtQuick 2.0\n"
494 "import Ubuntu.Components 0.1\n"
495 "Item {\n"
496 " width: units.gu(40)\n" // from SideStage in Shell.qml
497 " anchors.top: parent.top\n"
498 " anchors.bottom: parent.bottom\n"
499 " anchors.right: parent.right\n"
500 " z: -1000\n"
501 "}\n";
502 m_sideStageComponent->setData(sideStageQml.toLatin1(), QUrl());
505void ApplicationManager::createSideStage()
507 if (!m_sideStageComponent)
508 createSideStageComponent();
510 // The assumptions I make here really should hold.
511 QQuickView *quickView =
512 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
514 QQuickItem *shell = quickView->rootObject();
516 m_sideStage = qobject_cast<QQuickItem *>(m_sideStageComponent->create());
517 m_sideStage->setParentItem(shell);
518 m_sideStage->setFlag(QQuickItem::ItemHasContents, false);