on 1663e5c help sign in
unity8/tests/mocks/Unity/Application/ApplicationManager.cpp
486 lines 15 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>
28ApplicationManager::ApplicationManager(QObject *parent)
29 : ApplicationManagerInterface(parent)
30 , m_mainStageComponent(0)
31 , m_mainStage(0)
32 , m_sideStageComponent(0)
33 , m_sideStage(0)
35 buildListOfAvailableApplications();
38ApplicationManager::~ApplicationManager()
42int ApplicationManager::rowCount(const QModelIndex& parent) const {
43 return !parent.isValid() ? m_runningApplications.size() : 0;
46QVariant ApplicationManager::data(const QModelIndex& index, int role) const {
47 if (index.row() < 0 || index.row() >= m_runningApplications.size())
48 return QVariant();
50 auto app = m_runningApplications.at(index.row());
51 switch(role) {
52 case RoleAppId:
53 return app->appId();
54 case RoleName:
55 return app->name();
56 case RoleComment:
57 return app->comment();
58 case RoleIcon:
59 return app->icon();
60 case RoleStage:
61 return app->stage();
62 case RoleState:
63 return app->state();
64 case RoleFocused:
65 return app->focused();
66 default:
67 return QVariant();
68 }
71ApplicationInfo *ApplicationManager::get(int row) const {
72 if (row < 0 || row >= m_runningApplications.size())
73 return nullptr;
74 return m_runningApplications.at(row);
77ApplicationInfo *ApplicationManager::findApplication(const QString &appId) const {
78 for (ApplicationInfo *app : m_runningApplications) {
79 if (app->appId() == appId) {
80 return app;
81 }
82 }
83 return nullptr;
86void ApplicationManager::add(ApplicationInfo *application) {
87 if (!application)
88 return;
90 beginInsertRows(QModelIndex(), m_runningApplications.size(), m_runningApplications.size());
91 m_runningApplications.append(application);
92 endInsertRows();
93 Q_EMIT countChanged();
96void ApplicationManager::remove(ApplicationInfo *application) {
97 int i = m_runningApplications.indexOf(application);
98 if (i != -1) {
99 beginRemoveRows(QModelIndex(), i, i);
100 m_runningApplications.removeAt(i);
101 endRemoveRows();
102 Q_EMIT countChanged();
103 }
106void ApplicationManager::move(int from, int to) {
107 if (from == to) return;
109 if (from >= 0 && from < m_runningApplications.size() && to >= 0 && to < m_runningApplications.size()) {
110 QModelIndex parent;
111 /* When moving an item down, the destination index needs to be incremented
112 * by one, as explained in the documentation:
113 * http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */
114 beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0));
115 m_runningApplications.move(from, to);
116 endMoveRows();
117 }
120int ApplicationManager::keyboardHeight() const
122 return 0;
125bool ApplicationManager::keyboardVisible() const
127 return false;
130int ApplicationManager::sideStageWidth() const
132 return 0;
135ApplicationManager::StageHint ApplicationManager::stageHint() const
137 return MainStage;
140ApplicationManager::FormFactorHint ApplicationManager::formFactorHint() const
142 return PhoneFormFactor;
145ApplicationInfo* ApplicationManager::startApplication(const QString &appId,
146 const QStringList &arguments)
148 return startApplication(appId, NoFlag, arguments);
151ApplicationInfo* ApplicationManager::startApplication(const QString &appId,
152 ExecFlags flags,
153 const QStringList &arguments)
155 Q_UNUSED(arguments)
156 ApplicationInfo *application = 0;
158 for (ApplicationInfo *availableApp : m_availableApplications) {
159 if (availableApp->appId() == appId) {
160 application = availableApp;
161 break;
162 }
163 }
165 if (!application)
166 return 0;
168 if (flags.testFlag(ApplicationManager::ForceMainStage)
169 && application->stage() == ApplicationInfo::SideStage) {
170 application->setStage(ApplicationInfo::MainStage);
171 }
172 add(application);
174 return application;
177bool ApplicationManager::stopApplication(const QString &appId)
179 ApplicationInfo *application = findApplication(appId);
180 if (application == nullptr)
181 return false;
183 remove(application);
184 Q_EMIT focusedApplicationIdChanged();
185 return true;
188QString ApplicationManager::focusedApplicationId() const {
189 for (ApplicationInfo *app : m_runningApplications) {
190 if (app->focused()) {
191 return app->appId();
192 }
193 }
194 return QString();
197bool ApplicationManager::focusApplication(const QString &appId)
199 ApplicationInfo *application = findApplication(appId);
200 if (application == nullptr)
201 return false;
203 if (application->stage() == ApplicationInfo::MainStage) {
204 // unfocus currently focused mainstage app
205 for (ApplicationInfo *app : m_runningApplications) {
206 if (app->focused() && app->stage() == ApplicationInfo::MainStage) {
207 app->setFocused(false);
208 app->hideWindow();
209 }
210 }
212 // focus this app
213 application->setFocused(true);
214 if (!m_mainStage)
215 createMainStage();
216 application->showWindow(m_mainStage);
217 m_mainStage->setZ(-1000);
218 if (m_sideStage)
219 m_sideStage->setZ(-2000);
220 } else if (application->stage() == ApplicationInfo::SideStage) {
221 // unfocus currently focused sidestage app
222 for (ApplicationInfo *app : m_runningApplications) {
223 if (app->focused() && app->stage() == ApplicationInfo::SideStage) {
224 app->setFocused(false);
225 app->hideWindow();
226 }
227 }
229 // focus this app
230 application->setFocused(true);
231 if (!m_sideStage)
232 createSideStage();
233 application->showWindow(m_sideStage);
234 m_sideStage->setZ(-1000);
235 if (m_mainStage)
236 m_mainStage->setZ(-2000);
237 }
239 // move app to top of stack
240 move(m_runningApplications.indexOf(application), 0);
241 Q_EMIT focusedApplicationIdChanged();
242 return true;
245void ApplicationManager::unfocusCurrentApplication()
247 for (ApplicationInfo *app : m_runningApplications) {
248 if (app->focused()) {
249 app->hideWindow();
250 app->setFocused(false);
251 }
252 }
253 Q_EMIT focusedApplicationIdChanged();
256void ApplicationManager::generateQmlStrings(ApplicationInfo *application)
258 // TODO: Is there a better way of solving this fullscreen vs. regular
259 // application height?
260 QString topMargin;
261 if (application->fullscreen()) {
262 topMargin.append("0");
263 } else {
264 // Taken from Panel.panelHeight
265 topMargin.append("units.gu(3) + units.dp(2)");
266 }
268 QString windowQml = QString(
269 "import QtQuick 2.0\n"
270 "Image {\n"
271 " anchors.fill: parent\n"
272 " anchors.topMargin: %1\n"
273 " source: \"file://%2/Dash/graphics/phone/screenshots/%3.png\"\n"
274 " smooth: true\n"
275 " fillMode: Image.PreserveAspectCrop\n"
276 "}").arg(topMargin)
277 .arg(qmlDirectory())
278 .arg(application->icon().toString());
279 application->setWindowQml(windowQml);
281 QString imageQml = QString(
282 "import QtQuick 2.0\n"
283 "Image {\n"
284 " anchors.fill: parent\n"
285 " source: \"file://%1/Dash/graphics/phone/screenshots/%2.png\"\n"
286 " smooth: true\n"
287 " fillMode: Image.PreserveAspectCrop\n"
288 "}").arg(qmlDirectory())
289 .arg(application->icon().toString());
290 application->setImageQml(imageQml);
293void ApplicationManager::buildListOfAvailableApplications()
295 ApplicationInfo *application;
297 application = new ApplicationInfo(this);
298 application->setAppId("phone-app");
299 application->setName("Phone");
300 application->setIcon(QUrl("phone"));
301 application->setStage(ApplicationInfo::SideStage);
302 generateQmlStrings(application);
303 m_availableApplications.append(application);
305 application = new ApplicationInfo(this);
306 application->setAppId("camera-app");
307 application->setName("Camera");
308 application->setIcon(QUrl("camera"));
309 application->setFullscreen(true);
310 generateQmlStrings(application);
311 m_availableApplications.append(application);
313 application = new ApplicationInfo(this);
314 application->setAppId("gallery-app");
315 application->setName("Gallery");
316 application->setIcon(QUrl("gallery"));
317 generateQmlStrings(application);
318 m_availableApplications.append(application);
320 application = new ApplicationInfo(this);
321 application->setAppId("facebook-webapp");
322 application->setName("Facebook");
323 application->setIcon(QUrl("facebook"));
324 application->setStage(ApplicationInfo::SideStage);
325 generateQmlStrings(application);
326 m_availableApplications.append(application);
328 application = new ApplicationInfo(this);
329 application->setAppId("webbrowser-app");
330 application->setName("Browser");
331 application->setIcon(QUrl("browser"));
332 generateQmlStrings(application);
333 m_availableApplications.append(application);
335 application = new ApplicationInfo(this);
336 application->setAppId("twitter-webapp");
337 application->setName("Twitter");
338 application->setIcon(QUrl("twitter"));
339 application->setStage(ApplicationInfo::SideStage);
340 generateQmlStrings(application);
341 m_availableApplications.append(application);
343 application = new ApplicationInfo(this);
344 application->setAppId("gmail-webapp");
345 application->setName("GMail");
346 application->setIcon(QUrl("gmail"));
347 m_availableApplications.append(application);
349 application = new ApplicationInfo(this);
350 application->setAppId("ubuntu-weather-app");
351 application->setName("Weather");
352 application->setIcon(QUrl("weather"));
353 application->setStage(ApplicationInfo::SideStage);
354 generateQmlStrings(application);
355 m_availableApplications.append(application);
357 application = new ApplicationInfo(this);
358 application->setAppId("notes-app");
359 application->setName("Notepad");
360 application->setIcon(QUrl("notepad"));
361 application->setStage(ApplicationInfo::SideStage);
362 m_availableApplications.append(application);
364 application = new ApplicationInfo(this);
365 application->setAppId("calendar-app");
366 application->setName("Calendar");
367 application->setIcon(QUrl("calendar"));
368 application->setStage(ApplicationInfo::SideStage);
369 m_availableApplications.append(application);
371 application = new ApplicationInfo(this);
372 application->setAppId("mediaplayer-app");
373 application->setName("Media Player");
374 application->setIcon(QUrl("mediaplayer-app"));
375 application->setFullscreen(true);
376 m_availableApplications.append(application);
378 application = new ApplicationInfo(this);
379 application->setAppId("evernote");
380 application->setName("Evernote");
381 application->setIcon(QUrl("evernote"));
382 m_availableApplications.append(application);
384 application = new ApplicationInfo(this);
385 application->setAppId("map");
386 application->setName("Map");
387 application->setIcon(QUrl("map"));
388 generateQmlStrings(application);
389 m_availableApplications.append(application);
391 application = new ApplicationInfo(this);
392 application->setAppId("pinterest");
393 application->setName("Pinterest");
394 application->setIcon(QUrl("pinterest"));
395 m_availableApplications.append(application);
397 application = new ApplicationInfo(this);
398 application->setAppId("soundcloud");
399 application->setName("SoundCloud");
400 application->setIcon(QUrl("soundcloud"));
401 m_availableApplications.append(application);
403 application = new ApplicationInfo(this);
404 application->setAppId("wikipedia");
405 application->setName("Wikipedia");
406 application->setIcon(QUrl("wikipedia"));
407 m_availableApplications.append(application);
409 application = new ApplicationInfo(this);
410 application->setAppId("youtube");
411 application->setName("YouTube");
412 application->setIcon(QUrl("youtube"));
413 m_availableApplications.append(application);
416void ApplicationManager::createMainStageComponent()
418 // The assumptions I make here really should hold.
419 QQuickView *quickView =
420 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
422 QQmlEngine *engine = quickView->engine();
424 m_mainStageComponent = new QQmlComponent(engine, this);
425 QString mainStageQml =
426 "import QtQuick 2.0\n"
427 "Rectangle {\n"
428 " anchors.fill: parent\n"
429 " color: 'black'\n"
430 " z: -2000\n"
431 "}\n";
432 m_mainStageComponent->setData(mainStageQml.toLatin1(), QUrl());
435void ApplicationManager::createMainStage()
437 if (!m_mainStageComponent)
438 createMainStageComponent();
440 // The assumptions I make here really should hold.
441 QQuickView *quickView =
442 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
444 QQuickItem *shell = quickView->rootObject();
446 m_mainStage = qobject_cast<QQuickItem *>(m_mainStageComponent->create());
447 m_mainStage->setParentItem(shell);
450void ApplicationManager::createSideStageComponent()
452 // The assumptions I make here really should hold.
453 QQuickView *quickView =
454 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
456 QQmlEngine *engine = quickView->engine();
458 m_sideStageComponent = new QQmlComponent(engine, this);
459 QString sideStageQml =
460 "import QtQuick 2.0\n"
461 "import Ubuntu.Components 0.1\n"
462 "Item {\n"
463 " width: units.gu(40)\n" // from SideStage in Shell.qml
464 " anchors.top: parent.top\n"
465 " anchors.bottom: parent.bottom\n"
466 " anchors.right: parent.right\n"
467 " z: -1000\n"
468 "}\n";
469 m_sideStageComponent->setData(sideStageQml.toLatin1(), QUrl());
472void ApplicationManager::createSideStage()
474 if (!m_sideStageComponent)
475 createSideStageComponent();
477 // The assumptions I make here really should hold.
478 QQuickView *quickView =
479 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
481 QQuickItem *shell = quickView->rootObject();
483 m_sideStage = qobject_cast<QQuickItem *>(m_sideStageComponent->create());
484 m_sideStage->setParentItem(shell);
485 m_sideStage->setFlag(QQuickItem::ItemHasContents, false);