Merge lp:~albaguirre/unity8/add-screenshotter into lp:unity8

Proposed by Alberto Aguirre
Status: Merged
Approved by: Albert Astals Cid
Approved revision: 1308
Merged at revision: 1398
Proposed branch: lp:~albaguirre/unity8/add-screenshotter
Merge into: lp:unity8
Diff against target: 569 lines (+413/-4)
15 files modified
debian/unity8-private.install (+1/-0)
plugins/CMakeLists.txt (+1/-0)
plugins/ScreenGrabber/CMakeLists.txt (+13/-0)
plugins/ScreenGrabber/ScreenGrabber.qmltypes (+17/-0)
plugins/ScreenGrabber/plugin.cpp (+28/-0)
plugins/ScreenGrabber/plugin.h (+33/-0)
plugins/ScreenGrabber/qmldir (+3/-0)
plugins/ScreenGrabber/screengrabber.cpp (+94/-0)
plugins/ScreenGrabber/screengrabber.h (+42/-0)
qml/Components/ScreenGrabber.qml (+72/-0)
qml/Components/VolumeKeyFilter.qml (+63/-0)
qml/Shell.qml (+14/-4)
tests/mocks/QtMultimedia/QtMultimedia.qmltypes (+10/-0)
tests/mocks/QtMultimedia/audio.cpp (+10/-0)
tests/mocks/QtMultimedia/audio.h (+12/-0)
To merge this branch: bzr merge lp:~albaguirre/unity8/add-screenshotter
Reviewer Review Type Date Requested Status
Albert Astals Cid (community) Approve
PS Jenkins bot (community) continuous-integration Needs Fixing
Daniel d'Andrada (community) Abstain
Michael Zanetti (community) Needs Information
Gerry Boland (community) Needs Fixing
Review via email: mp+235834@code.launchpad.net

Commit message

Add a plugin to take screenshots on vol up + vol down

The screenshots are saved into <Pictures dir>/Screenshots/

Description of the change

Add a plugin to take screenshots on vol up + vol down

The screenshots are saved into <Pictures dir>/Screenshots/

Are there any related MPs required for this MP to build/function as expected?
No

Did you perform an exploratory manual test run of your code change and any related functionality?
Yes

Did you make sure that your branch does not contain spurious tags?
Yes

If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
N/A

If you changed the UI, has there been a design review?
Yes design has reviewed (see https://bugs.launchpad.net/ubuntu/+source/mir/+bug/1369644)

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote :

Does this actually take screenshot of running applications? ScreenShotter::takeScreenshot seems like it will only take snapshots of the shell itself, no?

review: Needs Information
Revision history for this message
Gerry Boland (gerboland) wrote :

+QString ScreenShotter::generateName()
+QString ScreenShotter::generateUniqueNum()
+ // First lets look for existing files using our numbering pattern
instead of this complex numbering pattern, why not just use current date & time?

review: Needs Fixing
Revision history for this message
Alberto Aguirre (albaguirre) wrote :

> Does this actually take screenshot of running applications?
> ScreenShotter::takeScreenshot seems like it will only take snapshots of the
> shell itself, no?

Yes it takes screenshots of whatever is being rendered to the screen by qtmir. This is because the shell window will be backed by qtmir's DisplayWindow, which is where all the shell and apps surfaces will be rendered into.

Revision history for this message
Alberto Aguirre (albaguirre) wrote :

> +QString ScreenShotter::generateName()
> +QString ScreenShotter::generateUniqueNum()
> + // First lets look for existing files using our numbering pattern
> instead of this complex numbering pattern, why not just use current date &
> time?

Ok I guess I can do that. I suppose I just wanted it to produce sequentially numbered files as most other products do when saving say camera snapshots.

Revision history for this message
Alberto Aguirre (albaguirre) wrote :

@Albert:

This is an example screenshot taken with this branch:
https://launchpadlibrarian.net/185774052/screenshot20141825_191841150.jpg

Revision history for this message
Alberto Aguirre (albaguirre) wrote :

> +QString ScreenShotter::generateName()
> +QString ScreenShotter::generateUniqueNum()
> + // First lets look for existing files using our numbering pattern
> instead of this complex numbering pattern, why not just use current date &
> time?

Done.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote :

> > Does this actually take screenshot of running applications?
> > ScreenShotter::takeScreenshot seems like it will only take snapshots of the
> > shell itself, no?
>
> Yes it takes screenshots of whatever is being rendered to the screen by qtmir.
> This is because the shell window will be backed by qtmir's DisplayWindow,
> which is where all the shell and apps surfaces will be rendered into.

Isn't this [ab]using an implementation detail of qtmir/qtubuntu? Reading the documentation of QGuiApplication::topLevelWindows it seems pretty clear it is windows about the application itself, not the rest.

I'm fine if we do decide this, but maybe it needs some comment in the code.

Revision history for this message
Michael Zanetti (mzanetti) wrote :

Albert, the reason why this works is really because application surfaces are just normal QQuickItems inside the shell. It does only screenshot the shell window with whatever the shell paints, in this case also the application surface items. I think this is ok as it really only works inside the shell application but not from other apps etc.

But the other question I have is if we really want to have such a thing in the product. I imagine people hitting up + down accidentally all the time. Do we really want them to make screenshots without even knowing? This doesn't sound like being something I would want to have enabled in a final product. Instead I think this should be done properly through some ui element, maybe in the developer mode options.

review: Needs Information
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> Albert, the reason why this works is really because application surfaces are
> just normal QQuickItems inside the shell. It does only screenshot the shell
> window with whatever the shell paints, in this case also the application
> surface items. I think this is ok as it really only works inside the shell
> application but not from other apps etc.
>
>
> But the other question I have is if we really want to have such a thing in the
> product. I imagine people hitting up + down accidentally all the time. Do we
> really want them to make screenshots without even knowing? This doesn't sound
> like being something I would want to have enabled in a final product. Instead
> I think this should be done properly through some ui element, maybe in the
> developer mode options.

It reminds me that I have a screenshot of my lockscreen in my android phone and I have absolutely no idea how I did it. :-D
Will google it now.

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

Would it be possible to move the screenshot animation onto a separate QML file? Shell.qml is already quite big as it is.

review: Needs Fixing
Revision history for this message
Alberto Aguirre (albaguirre) wrote :

> Albert, the reason why this works is really because application surfaces are
> just normal QQuickItems inside the shell. It does only screenshot the shell
> window with whatever the shell paints, in this case also the application
> surface items. I think this is ok as it really only works inside the shell
> application but not from other apps etc.
>
Right!

>
> But the other question I have is if we really want to have such a thing in the
> product. I imagine people hitting up + down accidentally all the time. Do we
> really want them to make screenshots without even knowing? This doesn't sound
> like being something I would want to have enabled in a final product. Instead
> I think this should be done properly through some ui element, maybe in the
> developer mode options.

From the bug/feature request I don't think they want a developer option...it's for users to share screenshots of their phone to social media...

As for accidental bumps of the keys...maybe I can just disable screenshotting when screen is off?

Revision history for this message
Alberto Aguirre (albaguirre) wrote :

> Would it be possible to move the screenshot animation onto a separate QML
> file? Shell.qml is already quite big as it is.

Sure.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) :
review: Abstain
Revision history for this message
Albert Astals Cid (aacid) wrote :

How hard would be adding the shutter sound that we use in Camera application be like Vesa suggests in the bug?

review: Needs Information
Revision history for this message
Albert Astals Cid (aacid) wrote :

Text conflict in qml/Shell.qml
1 conflicts encountered.

Revision history for this message
Alberto Aguirre (albaguirre) wrote :

@Albert,

Yes I will add that. I just need to find some time.

Revision history for this message
Alberto Aguirre (albaguirre) wrote :

@Albert

Ok, fixed conflict and added shutter sounds as requested by design.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1301. By Alberto Aguirre

Clasify shutter sound as alert to avoid pausing other players.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote :

 * saveScreenshot
make QImage and QStrings there const &

 * ScreenGrabber::captureAndSave
Make QWindowList windows const so we get the "fast" operator[] when doing windows[0]

 * onKeyReleased:
bring the else up to the } line

 * screenGrabber.enable(Powerd.status === Powerd.On):
Looks like this could be a binding instead of a function call?

 * There's a few ; missing in the two onStopped functions

 * //TODO: This should be configurable (perhaps through gsettings?):
What's the use case/benefit for the user of making this configurable?

 * screenshotsDir.mkdir("Screenshots");
Should we localize this?

review: Needs Fixing
Revision history for this message
Michał Sawicz (saviq) wrote :

> * //TODO: This should be configurable (perhaps through gsettings?):
> What's the use case/benefit for the user of making this configurable?

Yeah, don't think we need to configure this. Why not PNG by default though?

> * screenshotsDir.mkdir("Screenshots");
> Should we localize this?

No, we decided against localizing folder names because of having to "unlocalize" them everywhere that tries to read them.

Is that folder be picked up by the gallery app already?

Revision history for this message
Albert Astals Cid (aacid) wrote :

> > * //TODO: This should be configurable (perhaps through gsettings?):
> > What's the use case/benefit for the user of making this configurable?
>
> Yeah, don't think we need to configure this. Why not PNG by default though?

Yeah maybe PNG makes more sense

>
> > * screenshotsDir.mkdir("Screenshots");
> > Should we localize this?
>
> No, we decided against localizing folder names because of having to
> "unlocalize" them everywhere that tries to read them.
>
> Is that folder be picked up by the gallery app already?

Yes (screenshots look very fuzzy in the gallery app, but when downloaded with adb they look fine so i guess it's a gallery bug)

Revision history for this message
Alberto Aguirre (albaguirre) wrote :

> > * //TODO: This should be configurable (perhaps through gsettings?):
> > What's the use case/benefit for the user of making this configurable?
>
> Yeah, don't think we need to configure this. Why not PNG by default though?
>

I had it as PNG but other products do jpg too...presumably size?

> > * screenshotsDir.mkdir("Screenshots");
> > Should we localize this?
>
> No, we decided against localizing folder names because of having to
> "unlocalize" them everywhere that tries to read them.
>
> Is that folder be picked up by the gallery app already?

Yes it is.

Revision history for this message
Alberto Aguirre (albaguirre) wrote :

> * saveScreenshot
> make QImage and QStrings there const &
>
They need to be copies, because they will be run in a separate thread plus Qt has copy on writing semantics on those.

>
> * ScreenGrabber::captureAndSave
> Make QWindowList windows const so we get the "fast" operator[] when doing
> windows[0]
>
Agree.

>
> * onKeyReleased:
> bring the else up to the } line
>
Sure.

>
> * screenGrabber.enable(Powerd.status === Powerd.On):
> Looks like this could be a binding instead of a function call?
>
Ok will do.

> * There's a few ; missing in the two onStopped functions
>
Ok I'll fix it.

>
> * //TODO: This should be configurable (perhaps through gsettings?):
> What's the use case/benefit for the user of making this configurable?
>
I guess simply size/quality tradeoff.

1302. By Alberto Aguirre

Address review feedback

1303. By Alberto Aguirre

merge lp:unity8

1304. By Alberto Aguirre

Use binding instead of function call to enable/disable screenshotting according to screen power status.

Revision history for this message
Alberto Aguirre (albaguirre) wrote :

I've addressed the review comments.

I have left JPG as the format as the size tradeoff is:

For 960x540 screenshot we are talking about 41KB (JPG) vs 1.6MB (PNG).

Let me know if you still want PNG as default.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Alberto Aguirre (albaguirre) wrote :

Actually it looks like QIMage does compress png if we set the quality number to 0 or something low.

So the size is about for a 960x540 screenshot is around 41KB (JPG) vs 330KB (PNG).

1305. By Alberto Aguirre

Make PNG the default screenshot format

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote :

file:///tmp/buildd/unity8-8.00+14.10.20141013.2bzr1306pkg0utopic1686/qml/Components/ScreenGrabber.qml:44:9: Cannot assign to non-existent property "audioRole"
             audioRole: MediaPlayer.alert

review: Approve
Revision history for this message
Albert Astals Cid (aacid) wrote :

i meant needs fixing

review: Needs Fixing
Revision history for this message
Alberto Aguirre (albaguirre) wrote :
Revision history for this message
Albert Astals Cid (aacid) wrote :

It is, but we have our own QtMultimedia mock to make testing easier and the role is not available there. Can you define it in the mock? tests/mocks/QtMultimedia/audio.* ?

1306. By Alberto Aguirre

Add audioRole to QtMultimedia mock

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote :

We should try to eat the key presses, otherwise if i let the button pressed after taking the screenshot it changes my volume that is clearly not what we want, no?

review: Needs Fixing
1307. By Alberto Aguirre

Avoid volume control changes when both volume keys are pressed.

Revision history for this message
Alberto Aguirre (albaguirre) wrote :

> We should try to eat the key presses, otherwise if i let the button pressed
> after taking the screenshot it changes my volume that is clearly not what we
> want, no?

Fixed.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1308. By Alberto Aguirre

Remove trailing whitespace in comments

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote :

 * Did you perform an exploratory manual test run of the code change and any related functionality?
Yes

 * Did CI run pass? If not, please explain why.
Autopilot is previously borked

 * Did you make sure that the branch does not contain spurious tags?
Yes

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/unity8-private.install'
2--- debian/unity8-private.install 2014-10-06 15:45:25 +0000
3+++ debian/unity8-private.install 2014-10-30 02:40:10 +0000
4@@ -6,6 +6,7 @@
5 usr/lib/*/unity8/qml/Lights
6 usr/lib/*/unity8/qml/Powerd
7 usr/lib/*/unity8/qml/SessionBroadcast
8+usr/lib/*/unity8/qml/ScreenGrabber
9 usr/lib/*/unity8/qml/Ubuntu
10 usr/lib/*/unity8/qml/Unity
11 usr/lib/*/unity8/qml/Utils
12
13=== modified file 'plugins/CMakeLists.txt'
14--- plugins/CMakeLists.txt 2014-09-29 09:43:18 +0000
15+++ plugins/CMakeLists.txt 2014-10-30 02:40:10 +0000
16@@ -18,6 +18,7 @@
17 add_subdirectory(Dash)
18 add_subdirectory(Powerd)
19 add_subdirectory(SessionBroadcast)
20+add_subdirectory(ScreenGrabber)
21 add_subdirectory(Ubuntu)
22 add_subdirectory(Unity)
23 add_subdirectory(Utils)
24
25=== added directory 'plugins/ScreenGrabber'
26=== added file 'plugins/ScreenGrabber/CMakeLists.txt'
27--- plugins/ScreenGrabber/CMakeLists.txt 1970-01-01 00:00:00 +0000
28+++ plugins/ScreenGrabber/CMakeLists.txt 2014-10-30 02:40:10 +0000
29@@ -0,0 +1,13 @@
30+include_directories(
31+ ${CMAKE_CURRENT_BINARY_DIR}
32+)
33+
34+set(SCREENGRABBERSOURCES
35+ screengrabber.cpp
36+ plugin.cpp
37+)
38+
39+add_library(ScreenGrabber-qml MODULE ${SCREENGRABBERSOURCES})
40+qt5_use_modules(ScreenGrabber-qml Qml Gui Quick Concurrent)
41+
42+add_unity8_plugin(ScreenGrabber 0.1 ScreenGrabber TARGETS ScreenGrabber-qml)
43
44=== added file 'plugins/ScreenGrabber/ScreenGrabber.qmltypes'
45--- plugins/ScreenGrabber/ScreenGrabber.qmltypes 1970-01-01 00:00:00 +0000
46+++ plugins/ScreenGrabber/ScreenGrabber.qmltypes 2014-10-30 02:40:10 +0000
47@@ -0,0 +1,17 @@
48+import QtQuick.tooling 1.1
49+
50+// This file describes the plugin-supplied types contained in the library.
51+// It is used for QML tooling purposes only.
52+//
53+// This file was auto-generated by:
54+// 'qmlplugindump -nonrelocatable ScreenGrabber 0.1 plugins'
55+
56+Module {
57+ Component {
58+ name: "ScreenGrabber"
59+ prototype: "QObject"
60+ exports: ["ScreenGrabber/ScreenGrabber 0.1"]
61+ exportMetaObjectRevisions: [0]
62+ Method { name: "captureAndSave" }
63+ }
64+}
65
66=== added file 'plugins/ScreenGrabber/plugin.cpp'
67--- plugins/ScreenGrabber/plugin.cpp 1970-01-01 00:00:00 +0000
68+++ plugins/ScreenGrabber/plugin.cpp 2014-10-30 02:40:10 +0000
69@@ -0,0 +1,28 @@
70+/*
71+ * Copyright (C) 2014 Canonical, Ltd.
72+ *
73+ * This program is free software; you can redistribute it and/or modify
74+ * it under the terms of the GNU General Public License as published by
75+ * the Free Software Foundation; version 3.
76+ *
77+ * This program is distributed in the hope that it will be useful,
78+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
79+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
80+ * GNU General Public License for more details.
81+ *
82+ * You should have received a copy of the GNU General Public License
83+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
84+ *
85+ * Authors: Alberto Aguirre <alberto.aguirre@canonical.com>
86+ */
87+
88+#include "plugin.h"
89+#include "screengrabber.h"
90+
91+#include <QtQml/qqml.h>
92+
93+void ScreenGrabberPlugin::registerTypes(const char *uri)
94+{
95+ Q_ASSERT(uri == QLatin1String("ScreenGrabber"));
96+ qmlRegisterType<ScreenGrabber>(uri, 0, 1, "ScreenGrabber");
97+}
98
99=== added file 'plugins/ScreenGrabber/plugin.h'
100--- plugins/ScreenGrabber/plugin.h 1970-01-01 00:00:00 +0000
101+++ plugins/ScreenGrabber/plugin.h 2014-10-30 02:40:10 +0000
102@@ -0,0 +1,33 @@
103+/*
104+ * Copyright (C) 2014 Canonical, Ltd.
105+ *
106+ * This program is free software; you can redistribute it and/or modify
107+ * it under the terms of the GNU General Public License as published by
108+ * the Free Software Foundation; version 3.
109+ *
110+ * This program is distributed in the hope that it will be useful,
111+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
112+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
113+ * GNU General Public License for more details.
114+ *
115+ * You should have received a copy of the GNU General Public License
116+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
117+ *
118+ * Authors: Alberto Aguirre <alberto.aguirre@canonical.com>
119+ */
120+
121+#ifndef SCREENGRABBER_PLUGIN_H
122+#define SCREENGRABBER_PLUGIN_H
123+
124+#include <QtQml/QQmlExtensionPlugin>
125+
126+class ScreenGrabberPlugin : public QQmlExtensionPlugin
127+{
128+ Q_OBJECT
129+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
130+
131+public:
132+ void registerTypes(const char *uri);
133+};
134+
135+#endif // SCREENGRABBER_PLUGIN_H
136
137=== added file 'plugins/ScreenGrabber/qmldir'
138--- plugins/ScreenGrabber/qmldir 1970-01-01 00:00:00 +0000
139+++ plugins/ScreenGrabber/qmldir 2014-10-30 02:40:10 +0000
140@@ -0,0 +1,3 @@
141+module ScreenGrabber
142+plugin ScreenGrabber-qml
143+typeinfo ScreenGrabber.qmltypes
144
145=== added file 'plugins/ScreenGrabber/screengrabber.cpp'
146--- plugins/ScreenGrabber/screengrabber.cpp 1970-01-01 00:00:00 +0000
147+++ plugins/ScreenGrabber/screengrabber.cpp 2014-10-30 02:40:10 +0000
148@@ -0,0 +1,94 @@
149+/*
150+ * Copyright (C) 2014 Canonical, Ltd.
151+ *
152+ * This program is free software; you can redistribute it and/or modify
153+ * it under the terms of the GNU General Public License as published by
154+ * the Free Software Foundation; version 3.
155+ *
156+ * This program is distributed in the hope that it will be useful,
157+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
158+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
159+ * GNU General Public License for more details.
160+ *
161+ * You should have received a copy of the GNU General Public License
162+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
163+ *
164+ * Authors: Alberto Aguirre <alberto.aguirre@canonical.com>
165+ */
166+
167+#include "screengrabber.h"
168+
169+#include <QDir>
170+#include <QDateTime>
171+#include <QStandardPaths>
172+#include <QtGui/QImage>
173+#include <QtGui/QGuiApplication>
174+#include <QtQuick/QQuickWindow>
175+#include <QtConcurrent/QtConcurrentRun>
176+
177+#include <QDebug>
178+
179+void saveScreenshot(QImage screenshot, QString filename, QString format, int quality)
180+{
181+ if (!screenshot.save(filename, format.toLatin1().data(), quality))
182+ qWarning() << "ScreenShotter: failed to save snapshot!";
183+}
184+
185+ScreenGrabber::ScreenGrabber(QObject *parent)
186+ : QObject(parent),
187+ screenshotQuality(0)
188+{
189+ QDir screenshotsDir(QStandardPaths::displayName(QStandardPaths::PicturesLocation));
190+ screenshotsDir.mkdir("Screenshots");
191+ screenshotsDir.cd("Screenshots");
192+ if (screenshotsDir.exists())
193+ {
194+ fileNamePrefix = screenshotsDir.absolutePath();
195+ fileNamePrefix.append("/screenshot");
196+ }
197+ else
198+ {
199+ qWarning() << "ScreenShotter: failed to create directory at: " << screenshotsDir.absolutePath();
200+ }
201+}
202+
203+void ScreenGrabber::captureAndSave()
204+{
205+ if (fileNamePrefix.isEmpty())
206+ {
207+ qWarning() << "ScreenShotter: no directory to save screenshot";
208+ return;
209+ }
210+
211+ const QWindowList windows = QGuiApplication::topLevelWindows();
212+ if (windows.empty())
213+ {
214+ qWarning() << "ScreenShotter: no top level windows found!";
215+ return;
216+ }
217+
218+ QQuickWindow *main_window = qobject_cast<QQuickWindow *>(windows[0]);
219+ if (!main_window)
220+ {
221+ qWarning() << "ScreenShotter: can only take screenshots of QQuickWindows";
222+ return;
223+ }
224+
225+ QImage screenshot = main_window->grabWindow();
226+ QtConcurrent::run(saveScreenshot, screenshot, makeFileName(), getFormat(), screenshotQuality);
227+}
228+
229+QString ScreenGrabber::makeFileName()
230+{
231+ QString fileName(fileNamePrefix);
232+ fileName.append(QDateTime::currentDateTime().toString("yyyymmdd_hhmmsszzz"));
233+ fileName.append(".");
234+ fileName.append(getFormat());
235+ return fileName;
236+}
237+
238+QString ScreenGrabber::getFormat()
239+{
240+ //TODO: This should be configurable (perhaps through gsettings?)
241+ return "png";
242+}
243
244=== added file 'plugins/ScreenGrabber/screengrabber.h'
245--- plugins/ScreenGrabber/screengrabber.h 1970-01-01 00:00:00 +0000
246+++ plugins/ScreenGrabber/screengrabber.h 2014-10-30 02:40:10 +0000
247@@ -0,0 +1,42 @@
248+/*
249+ * Copyright (C) 2014 Canonical, Ltd.
250+ *
251+ * This program is free software; you can redistribute it and/or modify
252+ * it under the terms of the GNU General Public License as published by
253+ * the Free Software Foundation; version 3.
254+ *
255+ * This program is distributed in the hope that it will be useful,
256+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
257+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
258+ * GNU General Public License for more details.
259+ *
260+ * You should have received a copy of the GNU General Public License
261+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
262+ *
263+ * Authors: Alberto Aguirre <alberto.aguirre@canonical.com>
264+ */
265+
266+#ifndef SNAPSHOTTER_H
267+#define SNAPSHOTTER_H
268+
269+#include <QObject>
270+#include <QString>
271+
272+class ScreenGrabber: public QObject
273+{
274+ Q_OBJECT
275+
276+public:
277+ explicit ScreenGrabber(QObject *parent = 0);
278+
279+public Q_SLOTS:
280+ void captureAndSave();
281+
282+private:
283+ QString makeFileName();
284+ QString getFormat();
285+ QString fileNamePrefix;
286+ int screenshotQuality;
287+};
288+
289+#endif
290
291=== added file 'qml/Components/ScreenGrabber.qml'
292--- qml/Components/ScreenGrabber.qml 1970-01-01 00:00:00 +0000
293+++ qml/Components/ScreenGrabber.qml 2014-10-30 02:40:10 +0000
294@@ -0,0 +1,72 @@
295+/*
296+ * Copyright (C) 2014 Canonical, Ltd.
297+ *
298+ * This program is free software; you can redistribute it and/or modify
299+ * it under the terms of the GNU General Public License as published by
300+ * the Free Software Foundation; version 3.
301+ *
302+ * This program is distributed in the hope that it will be useful,
303+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
304+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
305+ * GNU General Public License for more details.
306+ *
307+ * You should have received a copy of the GNU General Public License
308+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
309+ */
310+
311+import QtQuick 2.0
312+import QtMultimedia 5.0
313+import ScreenGrabber 0.1
314+
315+Rectangle {
316+ id: root
317+ enabled: true
318+ visible: false
319+ color: "white"
320+ anchors.fill: parent
321+ opacity: 0.0
322+
323+ ScreenGrabber {
324+ id: screenGrabber
325+ objectName: "screenGrabber"
326+ }
327+
328+ Audio {
329+ id: shutterSound
330+ audioRole: MediaPlayer.alert
331+ source: "/system/media/audio/ui/camera_click.ogg"
332+ }
333+
334+ function capture() {
335+ if (!enabled)
336+ return;
337+
338+ visible = true;
339+ shutterSound.stop();
340+ shutterSound.play();
341+ fadeIn.start();
342+ }
343+
344+ NumberAnimation on opacity {
345+ id: fadeIn
346+ from: 0.0
347+ to: 1.0
348+ onStopped: {
349+ if (visible) {
350+ fadeOut.start();
351+ }
352+ }
353+ }
354+
355+ NumberAnimation on opacity {
356+ id: fadeOut
357+ from: 1.0
358+ to: 0.0
359+ onStopped: {
360+ if (visible) {
361+ screenGrabber.captureAndSave();
362+ visible = false;
363+ }
364+ }
365+ }
366+}
367
368=== added file 'qml/Components/VolumeKeyFilter.qml'
369--- qml/Components/VolumeKeyFilter.qml 1970-01-01 00:00:00 +0000
370+++ qml/Components/VolumeKeyFilter.qml 2014-10-30 02:40:10 +0000
371@@ -0,0 +1,63 @@
372+/*
373+ * Copyright (C) 2014 Canonical, Ltd.
374+ *
375+ * This program is free software; you can redistribute it and/or modify
376+ * it under the terms of the GNU General Public License as published by
377+ * the Free Software Foundation; version 3.
378+ *
379+ * This program is distributed in the hope that it will be useful,
380+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
381+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
382+ * GNU General Public License for more details.
383+ *
384+ * You should have received a copy of the GNU General Public License
385+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
386+ */
387+
388+import QtQuick 2.0
389+/*!
390+ \brief A filter for volume keys
391+
392+A filter which treats volume keys as single tri-state key with the states:
393+VolumeUp Pressed, VolumeDown Pressed or Volume Up+Down pressed
394+*/
395+QtObject {
396+ id: root
397+
398+ signal volumeUpPressed()
399+ signal volumeDownPressed()
400+ signal bothVolumeKeysPressed()
401+
402+ property bool volumeUpKeyPressed: false
403+ property bool volumeDownKeyPressed: false
404+ property bool aVolumeKeyWasReleased: true
405+
406+ function onKeyPressed(key) {
407+ if (key == Qt.Key_VolumeUp)
408+ volumeUpKeyPressed = true;
409+ else if (key == Qt.Key_VolumeDown)
410+ volumeDownKeyPressed = true;
411+
412+ if (volumeDownKeyPressed && volumeUpKeyPressed) {
413+ //avoids sending a signal repeatedly if both keys are held
414+ //instead one of the keys must have been previously released
415+ if (aVolumeKeyWasReleased)
416+ bothVolumeKeysPressed();
417+ aVolumeKeyWasReleased = false;
418+ } else if (volumeDownKeyPressed) {
419+ volumeDownPressed();
420+ } else if (volumeUpKeyPressed) {
421+ volumeUpPressed();
422+ }
423+ }
424+
425+ function onKeyReleased(key) {
426+ if (key == Qt.Key_VolumeUp) {
427+ volumeUpKeyPressed = false;
428+ aVolumeKeyWasReleased = true;
429+ } else if (key == Qt.Key_VolumeDown) {
430+ volumeDownKeyPressed = false;
431+ aVolumeKeyWasReleased = true;
432+ }
433+ }
434+}
435
436=== modified file 'qml/Shell.qml'
437--- qml/Shell.qml 2014-10-13 15:41:29 +0000
438+++ qml/Shell.qml 2014-10-30 02:40:10 +0000
439@@ -132,23 +132,32 @@
440 objectName: "dashCommunicator"
441 }
442
443+ ScreenGrabber {
444+ id: screenGrabber
445+ z: edgeDemo.z + 10
446+ enabled: Powerd.status === Powerd.On
447+ }
448+
449 Binding {
450 target: ApplicationManager
451 property: "forceDashActive"
452 value: launcher.shown || launcher.dashSwipe
453 }
454
455+ VolumeKeyFilter {
456+ id: volumeKeyFilter
457+ onVolumeDownPressed: volumeControl.volumeDown()
458+ onVolumeUpPressed: volumeControl.volumeUp()
459+ onBothVolumeKeysPressed: screenGrabber.capture()
460+ }
461
462 WindowKeysFilter {
463- // Handle but do not filter out volume keys
464- Keys.onVolumeUpPressed: { volumeControl.volumeUp(); event.accepted = false; }
465- Keys.onVolumeDownPressed: { volumeControl.volumeDown(); event.accepted = false; }
466-
467 Keys.onPressed: {
468 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
469 dialogs.onPowerKeyPressed();
470 event.accepted = true;
471 } else {
472+ volumeKeyFilter.onKeyPressed(event.key);
473 event.accepted = false;
474 }
475 }
476@@ -158,6 +167,7 @@
477 dialogs.onPowerKeyReleased();
478 event.accepted = true;
479 } else {
480+ volumeKeyFilter.onKeyReleased(event.key);
481 event.accepted = false;
482 }
483 }
484
485=== modified file 'tests/mocks/QtMultimedia/QtMultimedia.qmltypes'
486--- tests/mocks/QtMultimedia/QtMultimedia.qmltypes 2014-06-26 07:47:57 +0000
487+++ tests/mocks/QtMultimedia/QtMultimedia.qmltypes 2014-10-30 02:40:10 +0000
488@@ -20,11 +20,21 @@
489 "StoppedState": 2
490 }
491 }
492+ Enum {
493+ name: "AudioRole"
494+ values: {
495+ "AlarmRole": 0,
496+ "AlertRole": 1,
497+ "MultimediaRole": 2,
498+ "PhoneRole": 3
499+ }
500+ }
501 Property { name: "source"; type: "QUrl" }
502 Property { name: "playbackState"; type: "PlaybackState"; isReadonly: true }
503 Property { name: "position"; type: "int"; isReadonly: true }
504 Property { name: "duration"; type: "int"; isReadonly: true }
505 Property { name: "errorString"; type: "string"; isReadonly: true }
506+ Property { name: "audioRole"; type: "AudioRole" }
507 Signal {
508 name: "sourceChanged"
509 Parameter { name: "source"; type: "QUrl" }
510
511=== modified file 'tests/mocks/QtMultimedia/audio.cpp'
512--- tests/mocks/QtMultimedia/audio.cpp 2014-09-12 14:51:55 +0000
513+++ tests/mocks/QtMultimedia/audio.cpp 2014-10-30 02:40:10 +0000
514@@ -105,3 +105,13 @@
515 stop();
516 }
517 }
518+
519+Audio::AudioRole Audio::audioRole() const
520+{
521+ return Audio::MultimediaRole;
522+}
523+
524+void Audio::setAudioRole(Audio::AudioRole audioRole)
525+{
526+ Q_UNUSED(audioRole);
527+}
528
529=== modified file 'tests/mocks/QtMultimedia/audio.h'
530--- tests/mocks/QtMultimedia/audio.h 2013-11-14 12:12:05 +0000
531+++ tests/mocks/QtMultimedia/audio.h 2014-10-30 02:40:10 +0000
532@@ -27,11 +27,13 @@
533 {
534 Q_OBJECT
535 Q_ENUMS(PlaybackState)
536+ Q_ENUMS(AudioRole)
537 Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
538 Q_PROPERTY(PlaybackState playbackState READ playbackState NOTIFY playbackStateChanged)
539 Q_PROPERTY(int position READ position NOTIFY positionChanged)
540 Q_PROPERTY(int duration READ duration NOTIFY durationChanged)
541 Q_PROPERTY(QString errorString READ errorString NOTIFY errorStringChanged)
542+ Q_PROPERTY(AudioRole audioRole READ audioRole WRITE setAudioRole)
543 public:
544 enum PlaybackState {
545 PlayingState,
546@@ -39,6 +41,13 @@
547 StoppedState
548 };
549
550+ enum AudioRole {
551+ AlarmRole,
552+ AlertRole,
553+ MultimediaRole,
554+ PhoneRole
555+ };
556+
557 explicit Audio(QObject *parent = 0);
558
559 QUrl source() const;
560@@ -52,6 +61,9 @@
561
562 QString errorString() const;
563
564+ AudioRole audioRole() const;
565+ void setAudioRole(AudioRole audioRole);
566+
567 public Q_SLOTS:
568 void pause();
569 void play();

Subscribers

People subscribed via source and target branches