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
=== modified file 'debian/unity8-private.install'
--- debian/unity8-private.install 2014-10-06 15:45:25 +0000
+++ debian/unity8-private.install 2014-10-30 02:40:10 +0000
@@ -6,6 +6,7 @@
6usr/lib/*/unity8/qml/Lights6usr/lib/*/unity8/qml/Lights
7usr/lib/*/unity8/qml/Powerd7usr/lib/*/unity8/qml/Powerd
8usr/lib/*/unity8/qml/SessionBroadcast8usr/lib/*/unity8/qml/SessionBroadcast
9usr/lib/*/unity8/qml/ScreenGrabber
9usr/lib/*/unity8/qml/Ubuntu10usr/lib/*/unity8/qml/Ubuntu
10usr/lib/*/unity8/qml/Unity11usr/lib/*/unity8/qml/Unity
11usr/lib/*/unity8/qml/Utils12usr/lib/*/unity8/qml/Utils
1213
=== modified file 'plugins/CMakeLists.txt'
--- plugins/CMakeLists.txt 2014-09-29 09:43:18 +0000
+++ plugins/CMakeLists.txt 2014-10-30 02:40:10 +0000
@@ -18,6 +18,7 @@
18add_subdirectory(Dash)18add_subdirectory(Dash)
19add_subdirectory(Powerd)19add_subdirectory(Powerd)
20add_subdirectory(SessionBroadcast)20add_subdirectory(SessionBroadcast)
21add_subdirectory(ScreenGrabber)
21add_subdirectory(Ubuntu)22add_subdirectory(Ubuntu)
22add_subdirectory(Unity)23add_subdirectory(Unity)
23add_subdirectory(Utils)24add_subdirectory(Utils)
2425
=== added directory 'plugins/ScreenGrabber'
=== added file 'plugins/ScreenGrabber/CMakeLists.txt'
--- plugins/ScreenGrabber/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ plugins/ScreenGrabber/CMakeLists.txt 2014-10-30 02:40:10 +0000
@@ -0,0 +1,13 @@
1include_directories(
2 ${CMAKE_CURRENT_BINARY_DIR}
3)
4
5set(SCREENGRABBERSOURCES
6 screengrabber.cpp
7 plugin.cpp
8)
9
10add_library(ScreenGrabber-qml MODULE ${SCREENGRABBERSOURCES})
11qt5_use_modules(ScreenGrabber-qml Qml Gui Quick Concurrent)
12
13add_unity8_plugin(ScreenGrabber 0.1 ScreenGrabber TARGETS ScreenGrabber-qml)
014
=== added file 'plugins/ScreenGrabber/ScreenGrabber.qmltypes'
--- plugins/ScreenGrabber/ScreenGrabber.qmltypes 1970-01-01 00:00:00 +0000
+++ plugins/ScreenGrabber/ScreenGrabber.qmltypes 2014-10-30 02:40:10 +0000
@@ -0,0 +1,17 @@
1import QtQuick.tooling 1.1
2
3// This file describes the plugin-supplied types contained in the library.
4// It is used for QML tooling purposes only.
5//
6// This file was auto-generated by:
7// 'qmlplugindump -nonrelocatable ScreenGrabber 0.1 plugins'
8
9Module {
10 Component {
11 name: "ScreenGrabber"
12 prototype: "QObject"
13 exports: ["ScreenGrabber/ScreenGrabber 0.1"]
14 exportMetaObjectRevisions: [0]
15 Method { name: "captureAndSave" }
16 }
17}
018
=== added file 'plugins/ScreenGrabber/plugin.cpp'
--- plugins/ScreenGrabber/plugin.cpp 1970-01-01 00:00:00 +0000
+++ plugins/ScreenGrabber/plugin.cpp 2014-10-30 02:40:10 +0000
@@ -0,0 +1,28 @@
1/*
2 * Copyright (C) 2014 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 *
16 * Authors: Alberto Aguirre <alberto.aguirre@canonical.com>
17 */
18
19#include "plugin.h"
20#include "screengrabber.h"
21
22#include <QtQml/qqml.h>
23
24void ScreenGrabberPlugin::registerTypes(const char *uri)
25{
26 Q_ASSERT(uri == QLatin1String("ScreenGrabber"));
27 qmlRegisterType<ScreenGrabber>(uri, 0, 1, "ScreenGrabber");
28}
029
=== added file 'plugins/ScreenGrabber/plugin.h'
--- plugins/ScreenGrabber/plugin.h 1970-01-01 00:00:00 +0000
+++ plugins/ScreenGrabber/plugin.h 2014-10-30 02:40:10 +0000
@@ -0,0 +1,33 @@
1/*
2 * Copyright (C) 2014 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 *
16 * Authors: Alberto Aguirre <alberto.aguirre@canonical.com>
17 */
18
19#ifndef SCREENGRABBER_PLUGIN_H
20#define SCREENGRABBER_PLUGIN_H
21
22#include <QtQml/QQmlExtensionPlugin>
23
24class ScreenGrabberPlugin : public QQmlExtensionPlugin
25{
26 Q_OBJECT
27 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28
29public:
30 void registerTypes(const char *uri);
31};
32
33#endif // SCREENGRABBER_PLUGIN_H
034
=== added file 'plugins/ScreenGrabber/qmldir'
--- plugins/ScreenGrabber/qmldir 1970-01-01 00:00:00 +0000
+++ plugins/ScreenGrabber/qmldir 2014-10-30 02:40:10 +0000
@@ -0,0 +1,3 @@
1module ScreenGrabber
2plugin ScreenGrabber-qml
3typeinfo ScreenGrabber.qmltypes
04
=== added file 'plugins/ScreenGrabber/screengrabber.cpp'
--- plugins/ScreenGrabber/screengrabber.cpp 1970-01-01 00:00:00 +0000
+++ plugins/ScreenGrabber/screengrabber.cpp 2014-10-30 02:40:10 +0000
@@ -0,0 +1,94 @@
1/*
2 * Copyright (C) 2014 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 *
16 * Authors: Alberto Aguirre <alberto.aguirre@canonical.com>
17 */
18
19#include "screengrabber.h"
20
21#include <QDir>
22#include <QDateTime>
23#include <QStandardPaths>
24#include <QtGui/QImage>
25#include <QtGui/QGuiApplication>
26#include <QtQuick/QQuickWindow>
27#include <QtConcurrent/QtConcurrentRun>
28
29#include <QDebug>
30
31void saveScreenshot(QImage screenshot, QString filename, QString format, int quality)
32{
33 if (!screenshot.save(filename, format.toLatin1().data(), quality))
34 qWarning() << "ScreenShotter: failed to save snapshot!";
35}
36
37ScreenGrabber::ScreenGrabber(QObject *parent)
38 : QObject(parent),
39 screenshotQuality(0)
40{
41 QDir screenshotsDir(QStandardPaths::displayName(QStandardPaths::PicturesLocation));
42 screenshotsDir.mkdir("Screenshots");
43 screenshotsDir.cd("Screenshots");
44 if (screenshotsDir.exists())
45 {
46 fileNamePrefix = screenshotsDir.absolutePath();
47 fileNamePrefix.append("/screenshot");
48 }
49 else
50 {
51 qWarning() << "ScreenShotter: failed to create directory at: " << screenshotsDir.absolutePath();
52 }
53}
54
55void ScreenGrabber::captureAndSave()
56{
57 if (fileNamePrefix.isEmpty())
58 {
59 qWarning() << "ScreenShotter: no directory to save screenshot";
60 return;
61 }
62
63 const QWindowList windows = QGuiApplication::topLevelWindows();
64 if (windows.empty())
65 {
66 qWarning() << "ScreenShotter: no top level windows found!";
67 return;
68 }
69
70 QQuickWindow *main_window = qobject_cast<QQuickWindow *>(windows[0]);
71 if (!main_window)
72 {
73 qWarning() << "ScreenShotter: can only take screenshots of QQuickWindows";
74 return;
75 }
76
77 QImage screenshot = main_window->grabWindow();
78 QtConcurrent::run(saveScreenshot, screenshot, makeFileName(), getFormat(), screenshotQuality);
79}
80
81QString ScreenGrabber::makeFileName()
82{
83 QString fileName(fileNamePrefix);
84 fileName.append(QDateTime::currentDateTime().toString("yyyymmdd_hhmmsszzz"));
85 fileName.append(".");
86 fileName.append(getFormat());
87 return fileName;
88}
89
90QString ScreenGrabber::getFormat()
91{
92 //TODO: This should be configurable (perhaps through gsettings?)
93 return "png";
94}
095
=== added file 'plugins/ScreenGrabber/screengrabber.h'
--- plugins/ScreenGrabber/screengrabber.h 1970-01-01 00:00:00 +0000
+++ plugins/ScreenGrabber/screengrabber.h 2014-10-30 02:40:10 +0000
@@ -0,0 +1,42 @@
1/*
2 * Copyright (C) 2014 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 *
16 * Authors: Alberto Aguirre <alberto.aguirre@canonical.com>
17 */
18
19#ifndef SNAPSHOTTER_H
20#define SNAPSHOTTER_H
21
22#include <QObject>
23#include <QString>
24
25class ScreenGrabber: public QObject
26{
27 Q_OBJECT
28
29public:
30 explicit ScreenGrabber(QObject *parent = 0);
31
32public Q_SLOTS:
33 void captureAndSave();
34
35private:
36 QString makeFileName();
37 QString getFormat();
38 QString fileNamePrefix;
39 int screenshotQuality;
40};
41
42#endif
043
=== added file 'qml/Components/ScreenGrabber.qml'
--- qml/Components/ScreenGrabber.qml 1970-01-01 00:00:00 +0000
+++ qml/Components/ScreenGrabber.qml 2014-10-30 02:40:10 +0000
@@ -0,0 +1,72 @@
1/*
2 * Copyright (C) 2014 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 */
16
17import QtQuick 2.0
18import QtMultimedia 5.0
19import ScreenGrabber 0.1
20
21Rectangle {
22 id: root
23 enabled: true
24 visible: false
25 color: "white"
26 anchors.fill: parent
27 opacity: 0.0
28
29 ScreenGrabber {
30 id: screenGrabber
31 objectName: "screenGrabber"
32 }
33
34 Audio {
35 id: shutterSound
36 audioRole: MediaPlayer.alert
37 source: "/system/media/audio/ui/camera_click.ogg"
38 }
39
40 function capture() {
41 if (!enabled)
42 return;
43
44 visible = true;
45 shutterSound.stop();
46 shutterSound.play();
47 fadeIn.start();
48 }
49
50 NumberAnimation on opacity {
51 id: fadeIn
52 from: 0.0
53 to: 1.0
54 onStopped: {
55 if (visible) {
56 fadeOut.start();
57 }
58 }
59 }
60
61 NumberAnimation on opacity {
62 id: fadeOut
63 from: 1.0
64 to: 0.0
65 onStopped: {
66 if (visible) {
67 screenGrabber.captureAndSave();
68 visible = false;
69 }
70 }
71 }
72}
073
=== added file 'qml/Components/VolumeKeyFilter.qml'
--- qml/Components/VolumeKeyFilter.qml 1970-01-01 00:00:00 +0000
+++ qml/Components/VolumeKeyFilter.qml 2014-10-30 02:40:10 +0000
@@ -0,0 +1,63 @@
1/*
2 * Copyright (C) 2014 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 */
16
17import QtQuick 2.0
18/*!
19 \brief A filter for volume keys
20
21A filter which treats volume keys as single tri-state key with the states:
22VolumeUp Pressed, VolumeDown Pressed or Volume Up+Down pressed
23*/
24QtObject {
25 id: root
26
27 signal volumeUpPressed()
28 signal volumeDownPressed()
29 signal bothVolumeKeysPressed()
30
31 property bool volumeUpKeyPressed: false
32 property bool volumeDownKeyPressed: false
33 property bool aVolumeKeyWasReleased: true
34
35 function onKeyPressed(key) {
36 if (key == Qt.Key_VolumeUp)
37 volumeUpKeyPressed = true;
38 else if (key == Qt.Key_VolumeDown)
39 volumeDownKeyPressed = true;
40
41 if (volumeDownKeyPressed && volumeUpKeyPressed) {
42 //avoids sending a signal repeatedly if both keys are held
43 //instead one of the keys must have been previously released
44 if (aVolumeKeyWasReleased)
45 bothVolumeKeysPressed();
46 aVolumeKeyWasReleased = false;
47 } else if (volumeDownKeyPressed) {
48 volumeDownPressed();
49 } else if (volumeUpKeyPressed) {
50 volumeUpPressed();
51 }
52 }
53
54 function onKeyReleased(key) {
55 if (key == Qt.Key_VolumeUp) {
56 volumeUpKeyPressed = false;
57 aVolumeKeyWasReleased = true;
58 } else if (key == Qt.Key_VolumeDown) {
59 volumeDownKeyPressed = false;
60 aVolumeKeyWasReleased = true;
61 }
62 }
63}
064
=== modified file 'qml/Shell.qml'
--- qml/Shell.qml 2014-10-13 15:41:29 +0000
+++ qml/Shell.qml 2014-10-30 02:40:10 +0000
@@ -132,23 +132,32 @@
132 objectName: "dashCommunicator"132 objectName: "dashCommunicator"
133 }133 }
134134
135 ScreenGrabber {
136 id: screenGrabber
137 z: edgeDemo.z + 10
138 enabled: Powerd.status === Powerd.On
139 }
140
135 Binding {141 Binding {
136 target: ApplicationManager142 target: ApplicationManager
137 property: "forceDashActive"143 property: "forceDashActive"
138 value: launcher.shown || launcher.dashSwipe144 value: launcher.shown || launcher.dashSwipe
139 }145 }
140146
147 VolumeKeyFilter {
148 id: volumeKeyFilter
149 onVolumeDownPressed: volumeControl.volumeDown()
150 onVolumeUpPressed: volumeControl.volumeUp()
151 onBothVolumeKeysPressed: screenGrabber.capture()
152 }
141153
142 WindowKeysFilter {154 WindowKeysFilter {
143 // Handle but do not filter out volume keys
144 Keys.onVolumeUpPressed: { volumeControl.volumeUp(); event.accepted = false; }
145 Keys.onVolumeDownPressed: { volumeControl.volumeDown(); event.accepted = false; }
146
147 Keys.onPressed: {155 Keys.onPressed: {
148 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {156 if (event.key == Qt.Key_PowerOff || event.key == Qt.Key_PowerDown) {
149 dialogs.onPowerKeyPressed();157 dialogs.onPowerKeyPressed();
150 event.accepted = true;158 event.accepted = true;
151 } else {159 } else {
160 volumeKeyFilter.onKeyPressed(event.key);
152 event.accepted = false;161 event.accepted = false;
153 }162 }
154 }163 }
@@ -158,6 +167,7 @@
158 dialogs.onPowerKeyReleased();167 dialogs.onPowerKeyReleased();
159 event.accepted = true;168 event.accepted = true;
160 } else {169 } else {
170 volumeKeyFilter.onKeyReleased(event.key);
161 event.accepted = false;171 event.accepted = false;
162 }172 }
163 }173 }
164174
=== modified file 'tests/mocks/QtMultimedia/QtMultimedia.qmltypes'
--- tests/mocks/QtMultimedia/QtMultimedia.qmltypes 2014-06-26 07:47:57 +0000
+++ tests/mocks/QtMultimedia/QtMultimedia.qmltypes 2014-10-30 02:40:10 +0000
@@ -20,11 +20,21 @@
20 "StoppedState": 220 "StoppedState": 2
21 }21 }
22 }22 }
23 Enum {
24 name: "AudioRole"
25 values: {
26 "AlarmRole": 0,
27 "AlertRole": 1,
28 "MultimediaRole": 2,
29 "PhoneRole": 3
30 }
31 }
23 Property { name: "source"; type: "QUrl" }32 Property { name: "source"; type: "QUrl" }
24 Property { name: "playbackState"; type: "PlaybackState"; isReadonly: true }33 Property { name: "playbackState"; type: "PlaybackState"; isReadonly: true }
25 Property { name: "position"; type: "int"; isReadonly: true }34 Property { name: "position"; type: "int"; isReadonly: true }
26 Property { name: "duration"; type: "int"; isReadonly: true }35 Property { name: "duration"; type: "int"; isReadonly: true }
27 Property { name: "errorString"; type: "string"; isReadonly: true }36 Property { name: "errorString"; type: "string"; isReadonly: true }
37 Property { name: "audioRole"; type: "AudioRole" }
28 Signal {38 Signal {
29 name: "sourceChanged"39 name: "sourceChanged"
30 Parameter { name: "source"; type: "QUrl" }40 Parameter { name: "source"; type: "QUrl" }
3141
=== modified file 'tests/mocks/QtMultimedia/audio.cpp'
--- tests/mocks/QtMultimedia/audio.cpp 2014-09-12 14:51:55 +0000
+++ tests/mocks/QtMultimedia/audio.cpp 2014-10-30 02:40:10 +0000
@@ -105,3 +105,13 @@
105 stop();105 stop();
106 }106 }
107}107}
108
109Audio::AudioRole Audio::audioRole() const
110{
111 return Audio::MultimediaRole;
112}
113
114void Audio::setAudioRole(Audio::AudioRole audioRole)
115{
116 Q_UNUSED(audioRole);
117}
108118
=== modified file 'tests/mocks/QtMultimedia/audio.h'
--- tests/mocks/QtMultimedia/audio.h 2013-11-14 12:12:05 +0000
+++ tests/mocks/QtMultimedia/audio.h 2014-10-30 02:40:10 +0000
@@ -27,11 +27,13 @@
27{27{
28 Q_OBJECT28 Q_OBJECT
29 Q_ENUMS(PlaybackState)29 Q_ENUMS(PlaybackState)
30 Q_ENUMS(AudioRole)
30 Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)31 Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
31 Q_PROPERTY(PlaybackState playbackState READ playbackState NOTIFY playbackStateChanged)32 Q_PROPERTY(PlaybackState playbackState READ playbackState NOTIFY playbackStateChanged)
32 Q_PROPERTY(int position READ position NOTIFY positionChanged)33 Q_PROPERTY(int position READ position NOTIFY positionChanged)
33 Q_PROPERTY(int duration READ duration NOTIFY durationChanged)34 Q_PROPERTY(int duration READ duration NOTIFY durationChanged)
34 Q_PROPERTY(QString errorString READ errorString NOTIFY errorStringChanged)35 Q_PROPERTY(QString errorString READ errorString NOTIFY errorStringChanged)
36 Q_PROPERTY(AudioRole audioRole READ audioRole WRITE setAudioRole)
35public:37public:
36 enum PlaybackState {38 enum PlaybackState {
37 PlayingState,39 PlayingState,
@@ -39,6 +41,13 @@
39 StoppedState41 StoppedState
40 };42 };
4143
44 enum AudioRole {
45 AlarmRole,
46 AlertRole,
47 MultimediaRole,
48 PhoneRole
49 };
50
42 explicit Audio(QObject *parent = 0);51 explicit Audio(QObject *parent = 0);
4352
44 QUrl source() const;53 QUrl source() const;
@@ -52,6 +61,9 @@
5261
53 QString errorString() const;62 QString errorString() const;
5463
64 AudioRole audioRole() const;
65 void setAudioRole(AudioRole audioRole);
66
55public Q_SLOTS:67public Q_SLOTS:
56 void pause();68 void pause();
57 void play();69 void play();

Subscribers

People subscribed via source and target branches