Merge lp:camera-app/staging into lp:camera-app

Proposed by Florian Boucault
Status: Merged
Approved by: Florian Boucault
Approved revision: 658
Merged at revision: 636
Proposed branch: lp:camera-app/staging
Merge into: lp:camera-app
Diff against target: 2752 lines (+657/-1424)
31 files modified
BottomEdgeIndicators.qml (+1/-1)
CameraApp/CMakeLists.txt (+2/-4)
CameraApp/advancedcamerasettings.cpp (+3/-3)
CameraApp/components.cpp (+9/-0)
CameraApp/qstorageinfo.cpp (+0/-378)
CameraApp/qstorageinfo.h (+0/-102)
CameraApp/qstorageinfo_p.h (+0/-84)
CameraApp/qstorageinfo_unix.cpp (+0/-442)
CameraApp/storagelocations.cpp (+121/-0)
CameraApp/storagelocations.h (+50/-0)
CameraApp/storagemonitor.cpp (+34/-1)
CameraApp/storagemonitor.h (+5/-0)
GalleryView.qml (+3/-3)
GalleryViewHeader.qml (+3/-3)
PictureReview.qml (+46/-0)
ProcessingFeedback.qml (+43/-0)
SharePopover.qml (+1/-9)
Snapshot.qml (+0/-98)
ViewFinderExportConfirmation.qml (+40/-21)
ViewFinderOverlay.qml (+103/-40)
ViewFinderOverlayLoader.qml (+4/-1)
ViewFinderView.qml (+112/-65)
cameraapplication.cpp (+0/-106)
cameraapplication.h (+0/-15)
debian/rules (+0/-4)
manifest.json.in (+1/-1)
run_tests.sh (+0/-13)
tests/autopilot/camera_app/tests/test_focus.py (+32/-28)
tests/autopilot/camera_app/tests/test_options.py (+27/-0)
tests/unittests/CMakeLists.txt (+1/-0)
tests/unittests/qstorageinfo_stub.cpp (+16/-2)
To merge this branch: bzr merge lp:camera-app/staging
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
Ubuntu Phablet Team Pending
Review via email: mp+284427@code.launchpad.net

Commit message

New release:
- Allow tap to focus up to the left/right edge of the screen
- Query the manual focus support to enable/disable tap-to-focus as the backend supports it
- Do not crash if no QCameraExposureControl is available (true with Unity 7)
- Before setting a photo capture resolution, always checks it is included in the possible resolution options
- Fix bottom edge mouse handling: ensure that clicks on the bottom edge open it and that any click outside of option buttons dismiss the bottom edge
- Various performance improvements to minimize the time between shots and between pressing the shutter button and the actual capture
- When the GPS fix is accurate to less than 100m display the GPS icon in red and don't save location
- Display error messages when disk write fails and when SD card is not writeable when switching to it
- Disable record/shuttle button while PhotoRoll hint is visible

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
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
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)
lp:camera-app/staging updated
659. By Florian Boucault

Add flake8 tests to unit tests

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'BottomEdgeIndicators.qml'
--- BottomEdgeIndicators.qml 2015-11-17 15:25:03 +0000
+++ BottomEdgeIndicators.qml 2016-02-26 17:17:00 +0000
@@ -83,7 +83,7 @@
83 Icon {83 Icon {
84 id: indicatorIcon84 id: indicatorIcon
85 anchors.fill: parent85 anchors.fill: parent
86 color: "white"86 color: modelData.colorize ? "red" : "white"
87 name: modelData && modelData.isToggle ? modelData.icon : (modelData.get(model.selectedIndex) ? modelData.get(model.selectedIndex).icon : "")87 name: modelData && modelData.isToggle ? modelData.icon : (modelData.get(model.selectedIndex) ? modelData.get(model.selectedIndex).icon : "")
88 source: name ? "image://theme/%1".arg(name) : (modelData.iconSource || "")88 source: name ? "image://theme/%1".arg(name) : (modelData.iconSource || "")
89 visible: source != ""89 visible: source != ""
9090
=== modified file 'CameraApp/CMakeLists.txt'
--- CameraApp/CMakeLists.txt 2015-11-18 06:44:35 +0000
+++ CameraApp/CMakeLists.txt 2016-02-26 17:17:00 +0000
@@ -7,8 +7,7 @@
7 fileoperations.cpp7 fileoperations.cpp
8 foldersmodel.cpp8 foldersmodel.cpp
9 storagemonitor.cpp9 storagemonitor.cpp
10 qstorageinfo.cpp10 storagelocations.cpp
11 qstorageinfo_unix.cpp
12 )11 )
1312
14set(plugin_HDRS13set(plugin_HDRS
@@ -17,8 +16,7 @@
17 fileoperations.h16 fileoperations.h
18 foldersmodel.h17 foldersmodel.h
19 storagemonitor.h18 storagemonitor.h
20 qstorageinfo.h19 storagelocations.h
21 qstorageinfo_p.h
22 )20 )
2321
24add_library(camera-qml SHARED ${plugin_SRCS} ${plugin_HDRS})22add_library(camera-qml SHARED ${plugin_SRCS} ${plugin_HDRS})
2523
=== modified file 'CameraApp/advancedcamerasettings.cpp'
--- CameraApp/advancedcamerasettings.cpp 2016-01-05 13:08:28 +0000
+++ CameraApp/advancedcamerasettings.cpp 2016-02-26 17:17:00 +0000
@@ -237,11 +237,11 @@
237237
238 m_cameraFlashControl = flashControlFromCamera(m_camera);238 m_cameraFlashControl = flashControlFromCamera(m_camera);
239 m_cameraExposureControl = exposureControlFromCamera(m_camera);239 m_cameraExposureControl = exposureControlFromCamera(m_camera);
240 QVariant exposureMode = m_hdrEnabled ? QVariant::fromValue(ExposureHdr)
241 : QVariant::fromValue(QCameraExposure::ExposureAuto);
242 m_cameraExposureControl->setValue(QCameraExposureControl::ExposureMode, exposureMode);
243240
244 if (m_cameraExposureControl) {241 if (m_cameraExposureControl) {
242 QVariant exposureMode = m_hdrEnabled ? QVariant::fromValue(ExposureHdr)
243 : QVariant::fromValue(QCameraExposure::ExposureAuto);
244 m_cameraExposureControl->setValue(QCameraExposureControl::ExposureMode, exposureMode);
245 QObject::connect(m_cameraExposureControl,245 QObject::connect(m_cameraExposureControl,
246 SIGNAL(actualValueChanged(int)),246 SIGNAL(actualValueChanged(int)),
247 this, SLOT(onExposureValueChanged(int)));247 this, SLOT(onExposureValueChanged(int)));
248248
=== modified file 'CameraApp/components.cpp'
--- CameraApp/components.cpp 2015-01-22 16:15:08 +0000
+++ CameraApp/components.cpp 2016-02-26 17:17:00 +0000
@@ -24,6 +24,14 @@
24#include "fileoperations.h"24#include "fileoperations.h"
25#include "foldersmodel.h"25#include "foldersmodel.h"
26#include "storagemonitor.h"26#include "storagemonitor.h"
27#include "storagelocations.h"
28
29static QObject* StorageLocations_singleton_factory(QQmlEngine* engine, QJSEngine* scriptEngine)
30{
31 Q_UNUSED(engine);
32 Q_UNUSED(scriptEngine);
33 return new StorageLocations();
34}
2735
28void Components::registerTypes(const char *uri)36void Components::registerTypes(const char *uri)
29{37{
@@ -34,6 +42,7 @@
34 qmlRegisterType<FileOperations>(uri, 0, 1, "FileOperations");42 qmlRegisterType<FileOperations>(uri, 0, 1, "FileOperations");
35 qmlRegisterType<FoldersModel>(uri, 0, 1, "FoldersModel");43 qmlRegisterType<FoldersModel>(uri, 0, 1, "FoldersModel");
36 qmlRegisterType<StorageMonitor>(uri, 0, 1, "StorageMonitor");44 qmlRegisterType<StorageMonitor>(uri, 0, 1, "StorageMonitor");
45 qmlRegisterSingletonType<StorageLocations>(uri, 0, 1, "StorageLocations", StorageLocations_singleton_factory);
37}46}
3847
39void Components::initializeEngine(QQmlEngine *engine, const char *uri)48void Components::initializeEngine(QQmlEngine *engine, const char *uri)
4049
=== removed file 'CameraApp/qstorageinfo.cpp'
--- CameraApp/qstorageinfo.cpp 2015-01-27 14:34:31 +0000
+++ CameraApp/qstorageinfo.cpp 1970-01-01 00:00:00 +0000
@@ -1,378 +0,0 @@
1/****************************************************************************
2**
3** Copyright (C) 2014 Ivan Komissarov <ABBAPOH@gmail.com>
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the QtCore module of the Qt Toolkit.
7**
8** This program is free software; you can redistribute it and/or modify
9** it under the terms of the GNU General Public License as published by
10** the Free Software Foundation; version 3.
11**
12** This program is distributed in the hope that it will be useful,
13** but WITHOUT ANY WARRANTY; without even the implied warranty of
14** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15** GNU General Public License for more details.
16**
17** You should have received a copy of the GNU General Public License
18** along with this program. If not, see <http://www.gnu.org/licenses/>.
19**
20****************************************************************************/
21
22#include "qstorageinfo.h"
23#include "qstorageinfo_p.h"
24
25QT_BEGIN_NAMESPACE
26
27/*!
28 \class QStorageInfo
29 \inmodule QtCore
30 \since 5.4
31 \brief Provides information about currently mounted storage and drives.
32
33 \ingroup io
34 \ingroup shared
35
36 Allows retrieving information about the volume's space, its mount point,
37 label, and filesystem name.
38
39 You can create an instance of QStorageInfo by passing the path to the
40 volume's mount point as a constructor parameter, or you can set it using
41 the setPath() method. The static mountedVolumes() method can be used to get the
42 list of all mounted filesystems.
43
44 QStorageInfo always caches the retrieved information, but you can call
45 refresh() to invalidate the cache.
46
47 The following example retrieves the most common information about the root
48 volume of the system, and prints information about it.
49
50 \snippet code/src_corelib_io_qstorageinfo.cpp 2
51*/
52
53/*!
54 Constructs an empty QStorageInfo object.
55
56 Objects created with the default constructor will be invalid and therefore
57 not ready for use.
58
59 \sa setPath(), isReady(), isValid()
60*/
61QStorageInfo::QStorageInfo()
62 : d(new QStorageInfoPrivate)
63{
64}
65
66/*!
67 Constructs a new QStorageInfo object that gives information about the volume
68 mounted at \a path.
69
70 If you pass a directory or file, the QStorageInfo object will refer to the
71 volume where this directory or file is located.
72 You can check if the created object is correct using the isValid() method.
73
74 The following example shows how to get the volume on which the application is
75 located. It is recommended to always check that the volume is ready and valid.
76
77 \snippet code/src_corelib_io_qstorageinfo.cpp 0
78
79 \sa setPath()
80*/
81QStorageInfo::QStorageInfo(const QString &path)
82 : d(new QStorageInfoPrivate)
83{
84 setPath(path);
85}
86
87/*!
88 Constructs a new QStorageInfo object that gives information about the volume
89 containing the \a dir folder.
90*/
91QStorageInfo::QStorageInfo(const QDir &dir)
92 : d(new QStorageInfoPrivate)
93{
94 setPath(dir.absolutePath());
95}
96
97/*!
98 Constructs a new QStorageInfo object that is a copy of the \a other QStorageInfo object.
99*/
100QStorageInfo::QStorageInfo(const QStorageInfo &other)
101 : d(other.d)
102{
103}
104
105/*!
106 Destroys the QStorageInfo object and frees its resources.
107*/
108QStorageInfo::~QStorageInfo()
109{
110}
111
112/*!
113 Makes a copy of the QStorageInfo object \a other and assigns it to this QStorageInfo object.
114*/
115QStorageInfo &QStorageInfo::operator=(const QStorageInfo &other)
116{
117 d = other.d;
118 return *this;
119}
120
121/*!
122 \fn QStorageInfo &QStorageInfo::operator=(QStorageInfo &&other)
123
124 Assigns \a other to this QStorageInfo instance.
125*/
126
127/*!
128 \fn void QStorageInfo::swap(QStorageInfo &other)
129
130 Swaps this volume info with \a other. This function is very fast and
131 never fails.
132*/
133
134/*!
135 Sets this QStorageInfo object to the filesystem mounted where \a path is located.
136
137 \a path can either be a root path of the filesystem, a directory, or a file
138 within that filesystem.
139
140 \sa rootPath()
141*/
142void QStorageInfo::setPath(const QString &path)
143{
144 if (d->rootPath == path)
145 return;
146 d.detach();
147 d->rootPath = path;
148 d->doStat();
149}
150
151/*!
152 Returns the mount point of the filesystem this QStorageInfo object
153 represents.
154
155 On Windows, it returns the volume letter in case the volume is not mounted to
156 a directory.
157
158 Note that the value returned by rootPath() is the real mount point of a
159 volume, and may not be equal to the value passed to the constructor or setPath()
160 method. For example, if you have only the root volume in the system, and
161 pass '/directory' to setPath(), then this method will return '/'.
162
163 \sa setPath(), device()
164*/
165QString QStorageInfo::rootPath() const
166{
167 return d->rootPath;
168}
169
170/*!
171 Returns the size (in bytes) available for the current user. It returns
172 the total size available if the user is the root user or a system administrator.
173
174 This size can be less than or equal to the free size returned by
175 bytesFree() function.
176
177 \sa bytesTotal(), bytesFree()
178*/
179qint64 QStorageInfo::bytesAvailable() const
180{
181 return d->bytesAvailable;
182}
183
184/*!
185 Returns the number of free bytes in a volume. Note that if there are
186 quotas on the filesystem, this value can be larger than the value
187 returned by bytesAvailable().
188
189 \sa bytesTotal(), bytesAvailable()
190*/
191qint64 QStorageInfo::bytesFree() const
192{
193 return d->bytesFree;
194}
195
196/*!
197 Returns the total volume size in bytes.
198
199 \sa bytesFree(), bytesAvailable()
200*/
201qint64 QStorageInfo::bytesTotal() const
202{
203 return d->bytesTotal;
204}
205
206/*!
207 Returns the type name of the filesystem.
208
209 This is a platform-dependent function, and filesystem names can vary
210 between different operating systems. For example, on Windows filesystems
211 they can be named \c NTFS, and on Linux they can be named \c ntfs-3g or \c fuseblk.
212
213 \sa name()
214*/
215QByteArray QStorageInfo::fileSystemType() const
216{
217 return d->fileSystemType;
218}
219
220/*!
221 Returns the device for this volume.
222
223 For example, on Unix filesystems (including OS X), this returns the
224 devpath like \c /dev/sda0 for local storages. On Windows, it returns the UNC
225 path starting with \c \\\\?\\ for local storages (in other words, the volume GUID).
226
227 \sa rootPath()
228*/
229QByteArray QStorageInfo::device() const
230{
231 return d->device;
232}
233
234/*!
235 Returns the human-readable name of a filesystem, usually called \c label.
236
237 Not all filesystems support this feature. In this case, the value returned by
238 this method could be empty. An empty string is returned if the file system
239 does not support labels, or if no label is set.
240
241 On Linux, retrieving the volume's label requires \c udev to be present in the
242 system.
243
244 \sa fileSystemType()
245*/
246QString QStorageInfo::name() const
247{
248 return d->name;
249}
250
251/*!
252 Returns the volume's name, if available, or the root path if not.
253*/
254QString QStorageInfo::displayName() const
255{
256 if (!d->name.isEmpty())
257 return d->name;
258 return d->rootPath;
259}
260
261/*!
262 \fn bool QStorageInfo::isRoot() const
263
264 Returns true if this QStorageInfo represents the system root volume; false
265 otherwise.
266
267 On Unix filesystems, the root volume is a volume mounted on \c /. On Windows,
268 the root volume is the volume where the OS is installed.
269
270 \sa root()
271*/
272
273/*!
274 Returns true if the current filesystem is protected from writing; false
275 otherwise.
276*/
277bool QStorageInfo::isReadOnly() const
278{
279 return d->readOnly;
280}
281
282/*!
283 Returns true if the current filesystem is ready to work; false otherwise. For
284 example, false is returned if the CD volume is not inserted.
285
286 Note that fileSystemType(), name(), bytesTotal(), bytesFree(), and
287 bytesAvailable() will return invalid data until the volume is ready.
288
289 \sa isValid()
290*/
291bool QStorageInfo::isReady() const
292{
293 return d->ready;
294}
295
296/*!
297 Returns true if the QStorageInfo specified by rootPath exists and is mounted
298 correctly.
299
300 \sa isReady()
301*/
302bool QStorageInfo::isValid() const
303{
304 return d->valid;
305}
306
307/*!
308 Resets QStorageInfo's internal cache.
309
310 QStorageInfo caches information about storage to speed up performance.
311 QStorageInfo retrieves information during object construction and/or when calling
312 the setPath() method. You have to manually reset the cache by calling this
313 function to update storage information.
314*/
315void QStorageInfo::refresh()
316{
317 d.detach();
318 d->doStat();
319}
320
321/*!
322 Returns the list of QStorageInfo objects that corresponds to the list of currently
323 mounted filesystems.
324
325 On Windows, this returns the drives visible in the \gui{My Computer} folder. On Unix
326 operating systems, it returns the list of all mounted filesystems (except for
327 pseudo filesystems).
328
329 Returns all currently mounted filesystems by default.
330
331 The example shows how to retrieve all available filesystems, skipping read-only ones.
332
333 \snippet code/src_corelib_io_qstorageinfo.cpp 1
334
335 \sa root()
336*/
337QList<QStorageInfo> QStorageInfo::mountedVolumes()
338{
339 return QStorageInfoPrivate::mountedVolumes();
340}
341
342Q_GLOBAL_STATIC_WITH_ARGS(QStorageInfo, getRoot, (QStorageInfoPrivate::root()))
343
344/*!
345 Returns a QStorageInfo object that represents the system root volume.
346
347 On Unix systems this call returns the root ('/') volume; in Windows the volume where
348 the operating system is installed.
349
350 \sa isRoot()
351*/
352QStorageInfo QStorageInfo::root()
353{
354 return *getRoot();
355}
356
357/*!
358 \fn inline bool operator==(const QStorageInfo &first, const QStorageInfo &second)
359
360 \relates QStorageInfo
361
362 Returns true if the \a first QStorageInfo object refers to the same drive or volume
363 as the \a second; otherwise it returns false.
364
365 Note that the result of comparing two invalid QStorageInfo objects is always
366 positive.
367*/
368
369/*!
370 \fn inline bool operator!=(const QStorageInfo &first, const QStorageInfo &second)
371
372 \relates QStorageInfo
373
374 Returns true if the \a first QStorageInfo object refers to a different drive or
375 volume than the \a second; otherwise returns false.
376*/
377
378QT_END_NAMESPACE
3790
=== removed file 'CameraApp/qstorageinfo.h'
--- CameraApp/qstorageinfo.h 2015-01-27 14:34:31 +0000
+++ CameraApp/qstorageinfo.h 1970-01-01 00:00:00 +0000
@@ -1,102 +0,0 @@
1/****************************************************************************
2**
3** Copyright (C) 2014 Ivan Komissarov <ABBAPOH@gmail.com>
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the QtCore module of the Qt Toolkit.
7**
8** This program is free software; you can redistribute it and/or modify
9** it under the terms of the GNU General Public License as published by
10** the Free Software Foundation; version 3.
11**
12** This program is distributed in the hope that it will be useful,
13** but WITHOUT ANY WARRANTY; without even the implied warranty of
14** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15** GNU General Public License for more details.
16**
17** You should have received a copy of the GNU General Public License
18** along with this program. If not, see <http://www.gnu.org/licenses/>.
19**
20****************************************************************************/
21
22#ifndef QSTORAGEINFO_H
23#define QSTORAGEINFO_H
24
25#include <QtCore/qbytearray.h>
26#include <QtCore/qdir.h>
27#include <QtCore/qlist.h>
28#include <QtCore/qmetatype.h>
29#include <QtCore/qstring.h>
30#include <QtCore/qshareddata.h>
31
32QT_BEGIN_NAMESPACE
33
34class QStorageInfoPrivate;
35class Q_CORE_EXPORT QStorageInfo
36{
37public:
38 QStorageInfo();
39 explicit QStorageInfo(const QString &path);
40 explicit QStorageInfo(const QDir &dir);
41 QStorageInfo(const QStorageInfo &other);
42 ~QStorageInfo();
43
44 QStorageInfo &operator=(const QStorageInfo &other);
45#ifdef Q_COMPILER_RVALUE_REFS
46 inline QStorageInfo &operator=(QStorageInfo &&other)
47 { qSwap(d, other.d); return *this; }
48#endif
49
50 inline void swap(QStorageInfo &other)
51 { qSwap(d, other.d); }
52
53 void setPath(const QString &path);
54
55 QString rootPath() const;
56 QByteArray device() const;
57 QByteArray fileSystemType() const;
58 QString name() const;
59 QString displayName() const;
60
61 qint64 bytesTotal() const;
62 qint64 bytesFree() const;
63 qint64 bytesAvailable() const;
64
65 inline bool isRoot() const;
66 bool isReadOnly() const;
67 bool isReady() const;
68 bool isValid() const;
69
70 void refresh();
71
72 static QList<QStorageInfo> mountedVolumes();
73 static QStorageInfo root();
74
75private:
76 friend class QStorageInfoPrivate;
77 friend bool operator==(const QStorageInfo &first, const QStorageInfo &second);
78 QExplicitlySharedDataPointer<QStorageInfoPrivate> d;
79};
80
81inline bool operator==(const QStorageInfo &first, const QStorageInfo &second)
82{
83 if (first.d == second.d)
84 return true;
85 return first.device() == second.device();
86}
87
88inline bool operator!=(const QStorageInfo &first, const QStorageInfo &second)
89{
90 return !(first == second);
91}
92
93inline bool QStorageInfo::isRoot() const
94{ return *this == QStorageInfo::root(); }
95
96Q_DECLARE_SHARED(QStorageInfo)
97
98QT_END_NAMESPACE
99
100Q_DECLARE_METATYPE(QStorageInfo)
101
102#endif // QSTORAGEINFO_H
1030
=== removed file 'CameraApp/qstorageinfo_p.h'
--- CameraApp/qstorageinfo_p.h 2015-01-27 14:34:31 +0000
+++ CameraApp/qstorageinfo_p.h 1970-01-01 00:00:00 +0000
@@ -1,84 +0,0 @@
1/****************************************************************************
2**
3** Copyright (C) 2014 Ivan Komissarov <ABBAPOH@gmail.com>
4** Contact: http://www.qt-project.org/legal
5**
6**
7** This file is part of the QtCore module of the Qt Toolkit.
8**
9** This program is free software; you can redistribute it and/or modify
10** it under the terms of the GNU General Public License as published by
11** the Free Software Foundation; version 3.
12**
13** This program is distributed in the hope that it will be useful,
14** but WITHOUT ANY WARRANTY; without even the implied warranty of
15** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16** GNU General Public License for more details.
17**
18** You should have received a copy of the GNU General Public License
19** along with this program. If not, see <http://www.gnu.org/licenses/>.
20**
21****************************************************************************/
22
23#ifndef QSTORAGEINFO_P_H
24#define QSTORAGEINFO_P_H
25
26//
27// W A R N I N G
28// -------------
29//
30// This file is not part of the Qt API. It exists purely as an
31// implementation detail. This header file may change from version to
32// version without notice, or even be removed.
33//
34// We mean it.
35//
36
37#include "qstorageinfo.h"
38
39QT_BEGIN_NAMESPACE
40
41class QStorageInfoPrivate : public QSharedData
42{
43public:
44 inline QStorageInfoPrivate() : QSharedData(),
45 bytesTotal(0), bytesFree(0), bytesAvailable(0),
46 readOnly(false), ready(false), valid(false)
47 {}
48
49 void initRootPath();
50 void doStat();
51
52 static QList<QStorageInfo> mountedVolumes();
53 static QStorageInfo root();
54
55protected:
56#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
57 void retreiveVolumeInfo();
58 void retreiveDiskFreeSpace();
59#elif defined(Q_OS_MAC)
60 void retrievePosixInfo();
61 void retrieveUrlProperties(bool initRootPath = false);
62 void retrieveLabel();
63#elif defined(Q_OS_UNIX)
64 void retreiveVolumeInfo();
65#endif
66
67public:
68 QString rootPath;
69 QByteArray device;
70 QByteArray fileSystemType;
71 QString name;
72
73 qint64 bytesTotal;
74 qint64 bytesFree;
75 qint64 bytesAvailable;
76
77 bool readOnly;
78 bool ready;
79 bool valid;
80};
81
82QT_END_NAMESPACE
83
84#endif // QSTORAGEINFO_P_H
850
=== removed file 'CameraApp/qstorageinfo_unix.cpp'
--- CameraApp/qstorageinfo_unix.cpp 2015-01-27 14:34:31 +0000
+++ CameraApp/qstorageinfo_unix.cpp 1970-01-01 00:00:00 +0000
@@ -1,442 +0,0 @@
1/****************************************************************************
2**
3** Copyright (C) 2014 Ivan Komissarov <ABBAPOH@gmail.com>
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the QtCore module of the Qt Toolkit.
7**
8** This program is free software; you can redistribute it and/or modify
9** it under the terms of the GNU General Public License as published by
10** the Free Software Foundation; version 3.
11**
12** This program is distributed in the hope that it will be useful,
13** but WITHOUT ANY WARRANTY; without even the implied warranty of
14** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15** GNU General Public License for more details.
16**
17** You should have received a copy of the GNU General Public License
18** along with this program. If not, see <http://www.gnu.org/licenses/>.
19**
20****************************************************************************/
21
22#include "qstorageinfo_p.h"
23
24#include <QtCore/qdiriterator.h>
25#include <QtCore/qfileinfo.h>
26#include <QtCore/qtextstream.h>
27
28// Copy this definition directly instead of importing the whole file which
29// would pull in more dependencies we don't need.
30//#include <QtCore/private/qcore_unix_p.h>
31#define EINTR_LOOP(var, cmd) \
32 do { \
33 var = cmd; \
34 } while (var == -1 && errno == EINTR)
35
36#include <errno.h>
37#include <sys/stat.h>
38
39#if defined(Q_OS_BSD4)
40# include <sys/mount.h>
41# include <sys/statvfs.h>
42#elif defined(Q_OS_ANDROID)
43# include <sys/mount.h>
44# include <sys/vfs.h>
45# include <mntent.h>
46#elif defined(Q_OS_LINUX)
47# include <mntent.h>
48# include <sys/statvfs.h>
49#elif defined(Q_OS_SOLARIS)
50# include <sys/mnttab.h>
51#else
52# include <sys/statvfs.h>
53#endif
54
55#if defined(Q_OS_BSD4)
56# define QT_STATFSBUF struct statvfs
57# define QT_STATFS ::statvfs
58#elif defined(Q_OS_ANDROID)
59# define QT_STATFS ::statfs
60# define QT_STATFSBUF struct statfs
61# if !defined(ST_RDONLY)
62# define ST_RDONLY 1 // hack for missing define on Android
63# endif
64#else
65# if defined(QT_LARGEFILE_SUPPORT)
66# define QT_STATFSBUF struct statvfs64
67# define QT_STATFS ::statvfs64
68# else
69# define QT_STATFSBUF struct statvfs
70# define QT_STATFS ::statvfs
71# endif // QT_LARGEFILE_SUPPORT
72#endif // Q_OS_BSD4
73
74QT_BEGIN_NAMESPACE
75
76static bool isPseudoFs(const QString &mountDir, const QByteArray &type)
77{
78 if (mountDir.startsWith(QLatin1String("/dev"))
79 || mountDir.startsWith(QLatin1String("/proc"))
80 || mountDir.startsWith(QLatin1String("/sys"))
81 || mountDir.startsWith(QLatin1String("/var/run"))
82 || mountDir.startsWith(QLatin1String("/var/lock"))) {
83 return true;
84 }
85 if (type == "tmpfs")
86 return true;
87#if defined(Q_OS_LINUX)
88 if (type == "rootfs" || type == "rpc_pipefs")
89 return true;
90#endif
91
92 return false;
93}
94
95class QStorageIterator
96{
97public:
98 QStorageIterator();
99 ~QStorageIterator();
100
101 inline bool isValid() const;
102 inline bool next();
103 inline QString rootPath() const;
104 inline QByteArray fileSystemType() const;
105 inline QByteArray device() const;
106private:
107#if defined(Q_OS_BSD4)
108 struct statfs *stat_buf;
109 int entryCount;
110 int currentIndex;
111#elif defined(Q_OS_SOLARIS)
112 FILE *fp;
113 mnttab mnt;
114#elif defined(Q_OS_ANDROID)
115 QFile file;
116 QByteArray m_rootPath;
117 QByteArray m_fileSystemType;
118 QByteArray m_device;
119#elif defined(Q_OS_LINUX)
120 FILE *fp;
121 mntent mnt;
122 QByteArray buffer;
123#endif
124};
125
126#if defined(Q_OS_BSD4)
127
128inline QStorageIterator::QStorageIterator()
129 : entryCount(::getmntinfo(&stat_buf, 0)),
130 currentIndex(-1)
131{
132}
133
134inline QStorageIterator::~QStorageIterator()
135{
136}
137
138inline bool QStorageIterator::isValid() const
139{
140 return entryCount != -1;
141}
142
143inline bool QStorageIterator::next()
144{
145 return ++currentIndex < entryCount;
146}
147
148inline QString QStorageIterator::rootPath() const
149{
150 return QFile::decodeName(stat_buf[currentIndex].f_mntonname);
151}
152
153inline QByteArray QStorageIterator::fileSystemType() const
154{
155 return QByteArray(stat_buf[currentIndex].f_fstypename);
156}
157
158inline QByteArray QStorageIterator::device() const
159{
160 return QByteArray(stat_buf[currentIndex].f_mntfromname);
161}
162
163#elif defined(Q_OS_SOLARIS)
164
165static const char pathMounted[] = "/etc/mnttab";
166
167inline QStorageIterator::QStorageIterator()
168{
169 const int fd = qt_safe_open(pathMounted, O_RDONLY);
170 fp = ::fdopen(fd, "r");
171}
172
173inline QStorageIterator::~QStorageIterator()
174{
175 if (fp)
176 ::fclose(fp);
177}
178
179inline bool QStorageIterator::isValid() const
180{
181 return fp != Q_NULLPTR;
182}
183
184inline bool QStorageIterator::next()
185{
186 return ::getmntent(fp, &mnt) == Q_NULLPTR;
187}
188
189inline QString QStorageIterator::rootPath() const
190{
191 return QFile::decodeName(mnt->mnt_mountp);
192}
193
194inline QByteArray QStorageIterator::fileSystemType() const
195{
196 return QByteArray(mnt->mnt_fstype);
197}
198
199inline QByteArray QStorageIterator::device() const
200{
201 return QByteArray(mnt->mnt_mntopts);
202}
203
204#elif defined(Q_OS_ANDROID)
205
206static const char pathMounted[] = "/proc/mounts";
207
208inline QStorageIterator::QStorageIterator()
209{
210 file.setFileName(pathMounted);
211 file.open(QIODevice::ReadOnly | QIODevice::Text);
212}
213
214inline QStorageIterator::~QStorageIterator()
215{
216}
217
218inline bool QStorageIterator::isValid() const
219{
220 return file.isOpen();
221}
222
223inline bool QStorageIterator::next()
224{
225 QList<QByteArray> data;
226 do {
227 const QByteArray line = file.readLine();
228 data = line.split(' ');
229 } while (data.count() < 3 && !file.atEnd());
230
231 if (file.atEnd())
232 return false;
233 m_device = data.at(0);
234 m_rootPath = data.at(1);
235 m_fileSystemType = data.at(2);
236
237 return true;
238}
239
240inline QString QStorageIterator::rootPath() const
241{
242 return QFile::decodeName(m_rootPath);
243}
244
245inline QByteArray QStorageIterator::fileSystemType() const
246{
247 return m_fileSystemType;
248}
249
250inline QByteArray QStorageIterator::device() const
251{
252 return m_device;
253}
254
255#elif defined(Q_OS_LINUX)
256
257static const char pathMounted[] = "/etc/mtab";
258static const int bufferSize = 3*PATH_MAX; // 2 paths (mount point+device) and metainfo
259
260inline QStorageIterator::QStorageIterator() :
261 buffer(QByteArray(bufferSize, 0))
262{
263 fp = ::setmntent(pathMounted, "r");
264}
265
266inline QStorageIterator::~QStorageIterator()
267{
268 if (fp)
269 ::endmntent(fp);
270}
271
272inline bool QStorageIterator::isValid() const
273{
274 return fp != Q_NULLPTR;
275}
276
277inline bool QStorageIterator::next()
278{
279 return ::getmntent_r(fp, &mnt, buffer.data(), buffer.size()) != Q_NULLPTR;
280}
281
282inline QString QStorageIterator::rootPath() const
283{
284 return QFile::decodeName(mnt.mnt_dir);
285}
286
287inline QByteArray QStorageIterator::fileSystemType() const
288{
289 return QByteArray(mnt.mnt_type);
290}
291
292inline QByteArray QStorageIterator::device() const
293{
294 return QByteArray(mnt.mnt_fsname);
295}
296
297#else
298
299inline QStorageIterator::QStorageIterator()
300{
301}
302
303inline QStorageIterator::~QStorageIterator()
304{
305}
306
307inline bool QStorageIterator::isValid() const
308{
309 return false;
310}
311
312inline bool QStorageIterator::next()
313{
314 return false;
315}
316
317inline QString QStorageIterator::rootPath() const
318{
319 return QString();
320}
321
322inline QByteArray QStorageIterator::fileSystemType() const
323{
324 return QByteArray();
325}
326
327inline QByteArray QStorageIterator::device() const
328{
329 return QByteArray();
330}
331
332#endif
333
334void QStorageInfoPrivate::initRootPath()
335{
336 rootPath = QFileInfo(rootPath).canonicalFilePath();
337
338 if (rootPath.isEmpty())
339 return;
340
341 QStorageIterator it;
342 if (!it.isValid()) {
343 rootPath = QStringLiteral("/");
344 return;
345 }
346
347 int maxLength = 0;
348 const QString oldRootPath = rootPath;
349 rootPath.clear();
350
351 while (it.next()) {
352 const QString mountDir = it.rootPath();
353 const QByteArray fsName = it.fileSystemType();
354 if (isPseudoFs(mountDir, fsName))
355 continue;
356 // we try to find most suitable entry
357 if (oldRootPath.startsWith(mountDir) && maxLength < mountDir.length()) {
358 maxLength = mountDir.length();
359 rootPath = mountDir;
360 device = it.device();
361 fileSystemType = fsName;
362 }
363 }
364}
365
366static inline QString retrieveLabel(const QByteArray &device)
367{
368#ifdef Q_OS_LINUX
369 static const char pathDiskByLabel[] = "/dev/disk/by-label";
370
371 QDirIterator it(QLatin1String(pathDiskByLabel), QDir::NoDotAndDotDot);
372 while (it.hasNext()) {
373 it.next();
374 QFileInfo fileInfo(it.fileInfo());
375 if (fileInfo.isSymLink() && fileInfo.symLinkTarget().toLocal8Bit() == device)
376 return fileInfo.fileName();
377 }
378#else
379 Q_UNUSED(device);
380#endif
381
382 return QString();
383}
384
385void QStorageInfoPrivate::doStat()
386{
387 initRootPath();
388 if (rootPath.isEmpty())
389 return;
390
391 retreiveVolumeInfo();
392 name = retrieveLabel(device);
393}
394
395void QStorageInfoPrivate::retreiveVolumeInfo()
396{
397 QT_STATFSBUF statfs_buf;
398 int result;
399 EINTR_LOOP(result, QT_STATFS(QFile::encodeName(rootPath).constData(), &statfs_buf));
400 if (result == 0) {
401 valid = true;
402 ready = true;
403
404 bytesTotal = statfs_buf.f_blocks * statfs_buf.f_bsize;
405 bytesFree = statfs_buf.f_bfree * statfs_buf.f_bsize;
406 bytesAvailable = statfs_buf.f_bavail * statfs_buf.f_bsize;
407#if defined(Q_OS_ANDROID)
408#if defined(_STATFS_F_FLAGS)
409 readOnly = (statfs_buf.f_flags & ST_RDONLY) != 0;
410#endif
411#else
412 readOnly = (statfs_buf.f_flag & ST_RDONLY) != 0;
413#endif
414 }
415}
416
417QList<QStorageInfo> QStorageInfoPrivate::mountedVolumes()
418{
419 QStorageIterator it;
420 if (!it.isValid())
421 return QList<QStorageInfo>() << root();
422
423 QList<QStorageInfo> volumes;
424
425 while (it.next()) {
426 const QString mountDir = it.rootPath();
427 const QByteArray fsName = it.fileSystemType();
428 if (isPseudoFs(mountDir, fsName))
429 continue;
430
431 volumes.append(QStorageInfo(mountDir));
432 }
433
434 return volumes;
435}
436
437QStorageInfo QStorageInfoPrivate::root()
438{
439 return QStorageInfo(QStringLiteral("/"));
440}
441
442QT_END_NAMESPACE
4430
=== added file 'CameraApp/storagelocations.cpp'
--- CameraApp/storagelocations.cpp 1970-01-01 00:00:00 +0000
+++ CameraApp/storagelocations.cpp 2016-02-26 17:17:00 +0000
@@ -0,0 +1,121 @@
1/*
2 * Copyright (C) 2016 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
17#include "storagelocations.h"
18
19#include <QCoreApplication>
20#include <QStandardPaths>
21#include <QStorageInfo>
22
23StorageLocations::StorageLocations(QObject *parent) : QObject(parent)
24{
25}
26
27QString StorageLocations::picturesLocation() const
28{
29 QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
30 if (locations.isEmpty()) {
31 return QString();
32 }
33 QString location = locations.at(0) + "/" + QCoreApplication::applicationName();
34 QDir dir;
35 // Transition from old directory 'camera' to new one; see bug #1363112
36 // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1363112
37 dir.rename(locations.at(0) + "/" + "camera", location);
38 dir.mkpath(location);
39 return location;
40}
41
42QString StorageLocations::videosLocation() const
43{
44 QStringList locations = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation);
45 if (locations.isEmpty()) {
46 return QString();
47 }
48 QString location = locations.at(0) + "/" + QCoreApplication::applicationName();
49 QDir dir;
50 // Transition from old directory 'camera' to new one; see bug #1363112
51 // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1363112
52 dir.rename(locations.at(0) + "/" + "camera", location);
53 dir.mkpath(location);
54 return location;
55}
56
57QString StorageLocations::temporaryLocation() const
58{
59 QStringList locations = QStandardPaths::standardLocations(QStandardPaths::TempLocation);
60 if (locations.isEmpty()) {
61 return QString();
62 }
63 QString location = locations.at(0);
64 QDir dir;
65 dir.mkpath(location);
66 return location;
67}
68
69QString StorageLocations::removableStorageLocation() const
70{
71 QString mediaRoot("/media/" + qgetenv("USER"));
72 Q_FOREACH(QStorageInfo volume, QStorageInfo::mountedVolumes()) {
73 if (volume.rootPath().startsWith(mediaRoot) &&
74 volume.isValid() && volume.isReady()) {
75 return volume.rootPath();
76 }
77 }
78
79 return QString();
80}
81
82QString StorageLocations::removableStoragePicturesLocation() const
83{
84 QString storageLocation = removableStorageLocation();
85 if (storageLocation.isEmpty()) {
86 return QString();
87 }
88
89 QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
90 QString pictureDir = QString(locations.at(0)).split("/").value(3);
91 if (pictureDir.isEmpty()){
92 return QString();
93 }
94 QString location = storageLocation + "/" + pictureDir + "/" + QCoreApplication::applicationName();
95 QDir dir;
96 dir.mkpath(location);
97 return location;
98}
99
100QString StorageLocations::removableStorageVideosLocation() const
101{
102 QString storageLocation = removableStorageLocation();
103 if (storageLocation.isEmpty()) {
104 return QString();
105 }
106
107 QStringList locations = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation);
108 QString movieDir = QString(locations.at(0)).split("/").value(3);
109 if (movieDir.isEmpty()){
110 return QString();
111 }
112 QString location = storageLocation + "/" + movieDir + "/" + QCoreApplication::applicationName();
113 QDir dir;
114 dir.mkpath(location);
115 return location;
116}
117
118bool StorageLocations::removableStoragePresent() const
119{
120 return !removableStorageLocation().isEmpty();
121}
0122
=== added file 'CameraApp/storagelocations.h'
--- CameraApp/storagelocations.h 1970-01-01 00:00:00 +0000
+++ CameraApp/storagelocations.h 2016-02-26 17:17:00 +0000
@@ -0,0 +1,50 @@
1/*
2 * Copyright (C) 2016 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
17#ifndef STORAGELOCATIONS_H
18#define STORAGELOCATIONS_H
19
20#include <QObject>
21
22class StorageLocations : public QObject
23{
24 Q_OBJECT
25
26 Q_PROPERTY(QString picturesLocation READ picturesLocation CONSTANT)
27 Q_PROPERTY(QString videosLocation READ videosLocation CONSTANT)
28 Q_PROPERTY(QString temporaryLocation READ temporaryLocation CONSTANT)
29 Q_PROPERTY(QString removableStorageLocation READ removableStorageLocation CONSTANT)
30 Q_PROPERTY(QString removableStoragePicturesLocation READ removableStoragePicturesLocation CONSTANT)
31 Q_PROPERTY(QString removableStorageVideosLocation READ removableStorageVideosLocation CONSTANT)
32 Q_PROPERTY(bool removableStoragePresent READ removableStoragePresent NOTIFY removableStoragePresentChanged)
33
34public:
35 explicit StorageLocations(QObject *parent = 0);
36
37 QString picturesLocation() const;
38 QString videosLocation() const;
39 QString temporaryLocation() const;
40 QString removableStorageLocation() const;
41 QString removableStoragePicturesLocation() const;
42 QString removableStorageVideosLocation() const;
43
44 bool removableStoragePresent() const;
45
46Q_SIGNALS:
47 void removableStoragePresentChanged();
48};
49
50#endif // STORAGELOCATIONS_H
051
=== modified file 'CameraApp/storagemonitor.cpp'
--- CameraApp/storagemonitor.cpp 2015-01-23 19:28:44 +0000
+++ CameraApp/storagemonitor.cpp 2016-02-26 17:17:00 +0000
@@ -15,9 +15,10 @@
15 */15 */
1616
17#include "storagemonitor.h"17#include "storagemonitor.h"
18#include "storagelocations.h"
1819
19StorageMonitor::StorageMonitor(QObject *parent) :20StorageMonitor::StorageMonitor(QObject *parent) :
20 QObject(parent), m_low(false), m_criticallyLow(false)21 QObject(parent), m_low(false), m_criticallyLow(false), m_writeable(true)
21{22{
22 m_timer.setInterval(POLL_INTERVAL);23 m_timer.setInterval(POLL_INTERVAL);
23 m_timer.setSingleShot(false);24 m_timer.setSingleShot(false);
@@ -54,6 +55,32 @@
54 }55 }
55}56}
5657
58void StorageMonitor::checkWriteable()
59{
60 bool writeable = true;
61
62 QString mediaRoot("/media/" + qgetenv("USER"));
63 if (m_storage.rootPath().startsWith(mediaRoot)) {
64 // check only for external media, assume internal media is always writeable
65 if (m_storage.isReadOnly()) {
66 writeable = false;
67 } else {
68 StorageLocations locations;
69 QDir storageRoot(locations.removableStoragePicturesLocation());
70 QFile testFile(storageRoot.absoluteFilePath(".write_test"));
71 bool opened = testFile.open(QIODevice::WriteOnly | QIODevice::Unbuffered);
72 if (!opened || testFile.write("x", 1) != 1) writeable = false;
73 testFile.close();
74 testFile.remove();
75 }
76 }
77
78 if (m_writeable != writeable) {
79 m_writeable = writeable;
80 Q_EMIT isWriteableChanged();
81 }
82}
83
57void StorageMonitor::setLocation(QString location)84void StorageMonitor::setLocation(QString location)
58{85{
59 if (location != m_location) {86 if (location != m_location) {
@@ -62,6 +89,7 @@
6289
63 m_storage.setPath(m_location);90 m_storage.setPath(m_location);
64 checkDiskSpace();91 checkDiskSpace();
92 checkWriteable();
65 if (m_storage.isValid()) {93 if (m_storage.isValid()) {
66 m_timer.start();94 m_timer.start();
67 }95 }
@@ -84,3 +112,8 @@
84{112{
85 return m_criticallyLow;113 return m_criticallyLow;
86}114}
115
116bool StorageMonitor::isWriteable() const
117{
118 return m_writeable;
119}
87120
=== modified file 'CameraApp/storagemonitor.h'
--- CameraApp/storagemonitor.h 2015-01-23 19:29:03 +0000
+++ CameraApp/storagemonitor.h 2016-02-26 17:17:00 +0000
@@ -34,6 +34,7 @@
34 Q_PROPERTY(QString location READ location WRITE setLocation NOTIFY locationChanged)34 Q_PROPERTY(QString location READ location WRITE setLocation NOTIFY locationChanged)
35 Q_PROPERTY(bool diskSpaceLow READ diskSpaceLow NOTIFY diskSpaceLowChanged)35 Q_PROPERTY(bool diskSpaceLow READ diskSpaceLow NOTIFY diskSpaceLowChanged)
36 Q_PROPERTY(bool diskSpaceCriticallyLow READ diskSpaceCriticallyLow NOTIFY diskSpaceCriticallyLowChanged)36 Q_PROPERTY(bool diskSpaceCriticallyLow READ diskSpaceCriticallyLow NOTIFY diskSpaceCriticallyLowChanged)
37 Q_PROPERTY(bool isWriteable READ isWriteable NOTIFY isWriteableChanged)
3738
38public:39public:
39 explicit StorageMonitor(QObject *parent = 0);40 explicit StorageMonitor(QObject *parent = 0);
@@ -42,21 +43,25 @@
42 void setLocation(QString location);43 void setLocation(QString location);
43 bool diskSpaceLow() const;44 bool diskSpaceLow() const;
44 bool diskSpaceCriticallyLow() const;45 bool diskSpaceCriticallyLow() const;
46 bool isWriteable() const;
4547
46Q_SIGNALS:48Q_SIGNALS:
47 void locationChanged();49 void locationChanged();
48 void diskSpaceLowChanged();50 void diskSpaceLowChanged();
49 void diskSpaceCriticallyLowChanged();51 void diskSpaceCriticallyLowChanged();
52 void isWriteableChanged();
5053
51private Q_SLOTS:54private Q_SLOTS:
52 void refresh();55 void refresh();
5356
54private:57private:
55 void checkDiskSpace();58 void checkDiskSpace();
59 void checkWriteable();
5660
57private:61private:
58 bool m_low;62 bool m_low;
59 bool m_criticallyLow;63 bool m_criticallyLow;
64 bool m_writeable;
60 QTimer m_timer;65 QTimer m_timer;
61 QString m_location;66 QString m_location;
62 QStorageInfo m_storage;67 QStorageInfo m_storage;
6368
=== modified file 'GalleryView.qml'
--- GalleryView.qml 2015-11-26 11:29:18 +0000
+++ GalleryView.qml 2016-02-26 17:17:00 +0000
@@ -30,9 +30,9 @@
30 property bool userSelectionMode: false30 property bool userSelectionMode: false
31 property Item currentView: state == "GRID" ? photogridView : slideshowView31 property Item currentView: state == "GRID" ? photogridView : slideshowView
32 property var model: FoldersModel {32 property var model: FoldersModel {
33 folders: [application.picturesLocation, application.videosLocation,33 folders: [StorageLocations.picturesLocation, StorageLocations.videosLocation,
34 application.removableStoragePicturesLocation,34 StorageLocations.removableStoragePicturesLocation,
35 application.removableStorageVideosLocation]35 StorageLocations.removableStorageVideosLocation]
36 typeFilters: !main.contentExportMode ? [ "image", "video" ]36 typeFilters: !main.contentExportMode ? [ "image", "video" ]
37 : [MimeTypeMapper.contentTypeToMimeType(main.transferContentType)]37 : [MimeTypeMapper.contentTypeToMimeType(main.transferContentType)]
38 singleSelectionOnly: main.transfer.selectionType === ContentTransfer.Single38 singleSelectionOnly: main.transfer.selectionType === ContentTransfer.Single
3939
=== modified file 'GalleryViewHeader.qml'
--- GalleryViewHeader.qml 2015-10-30 15:01:10 +0000
+++ GalleryViewHeader.qml 2016-02-26 17:17:00 +0000
@@ -73,7 +73,7 @@
73 }73 }
74 width: units.gu(8)74 width: units.gu(8)
75 iconName: "back"75 iconName: "back"
76 iconColor: theme.palette.normal.foregroundText76 iconColor: "white"
77 onClicked: editMode ? header.exitEditor() : header.exit()77 onClicked: editMode ? header.exitEditor() : header.exit()
78 }78 }
7979
@@ -81,7 +81,7 @@
81 text: main.contentExportMode || userSelectionMode ? i18n.tr("Select") :81 text: main.contentExportMode || userSelectionMode ? i18n.tr("Select") :
82 (editMode ? i18n.tr("Edit Photo") : i18n.tr("Photo Roll"))82 (editMode ? i18n.tr("Edit Photo") : i18n.tr("Photo Roll"))
83 fontSize: "x-large"83 fontSize: "x-large"
84 color: theme.palette.normal.foregroundText84 color: "white"
85 elide: Text.ElideRight85 elide: Text.ElideRight
86 Layout.fillWidth: true86 Layout.fillWidth: true
87 }87 }
@@ -228,7 +228,7 @@
228 }228 }
229 text: model.text229 text: model.text
230 elide: Text.ElideRight230 elide: Text.ElideRight
231 color: action.enabled ? theme.palette.normal.foregroundText : Qt.darker(theme.palette.normal.foregroundText, 2.0)231 color: action.enabled ? "white" : Qt.darker("white", 2.0)
232 }232 }
233233
234 Icon {234 Icon {
235235
=== added file 'PictureReview.qml'
--- PictureReview.qml 1970-01-01 00:00:00 +0000
+++ PictureReview.qml 2016-02-26 17:17:00 +0000
@@ -0,0 +1,46 @@
1/*
2 * Copyright (C) 2016 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.4
18import QtQuick.Window 2.2
19
20Item {
21 id: snapshotRoot
22 property alias source: image.source
23 property ViewFinderGeometry geometry
24 property bool loaded: image.status == Image.Ready
25
26 // Rotation is locked at the moment the picture is shoot
27 // (in case processing is long, such as with HDR)
28 function lockOrientation() {
29 image.rotation = Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
30 }
31
32 Image {
33 id: image
34 anchors.centerIn: parent
35 anchors.verticalCenterOffset: -geometry.y
36
37 asynchronous: true
38 cache: false
39 fillMode: Image.PreserveAspectFit
40 smooth: false
41 width: rotation == 0 ? geometry.width : geometry.height
42 height: rotation == 0 ? geometry.height : geometry.width
43 sourceSize.width: width
44 sourceSize.height: height
45 }
46}
047
=== added file 'ProcessingFeedback.qml'
--- ProcessingFeedback.qml 1970-01-01 00:00:00 +0000
+++ ProcessingFeedback.qml 2016-02-26 17:17:00 +0000
@@ -0,0 +1,43 @@
1/*
2 * Copyright 2016 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.4
18import Ubuntu.Components 1.3
19
20Item {
21 id: processingFeedback
22
23 property bool processing: false
24
25 Timer {
26 interval: 2000
27 running: processing
28 onTriggered: spinner.running = true
29 }
30
31 onProcessingChanged: if (!processing) spinner.running = false
32
33 ActivityIndicator {
34 id: spinner
35 opacity: running ? 1.0 : 0.0
36 Behavior on opacity {
37 OpacityAnimator {
38 duration: UbuntuAnimation.SnapDuration
39 easing: UbuntuAnimation.StandardEasing
40 }
41 }
42 }
43}
044
=== modified file 'SharePopover.qml'
--- SharePopover.qml 2015-10-22 12:46:34 +0000
+++ SharePopover.qml 2016-02-26 17:17:00 +0000
@@ -17,7 +17,7 @@
17import QtQuick 2.417import QtQuick 2.4
18import Ubuntu.Components 1.318import Ubuntu.Components 1.3
19import Ubuntu.Components.Popups 1.319import Ubuntu.Components.Popups 1.3
20import Ubuntu.Content 0.120import Ubuntu.Content 1.3
2121
22PopupBase {22PopupBase {
23 property var transferContentType 23 property var transferContentType
@@ -31,17 +31,9 @@
31 contentPeerPicker.peerSelected.connect(contentPeerSelected);31 contentPeerPicker.peerSelected.connect(contentPeerSelected);
32 }32 }
3333
34 // FIXME: ContentPeerPicker should either have a background or not, not half of one
35 Rectangle {
36 anchors.fill: parent
37 color: theme.palette.normal.overlay
38 }
39
40 ContentPeerPicker {34 ContentPeerPicker {
41 id: contentPeerPicker35 id: contentPeerPicker
42 // FIXME: ContentPeerPicker should define an implicit size and not refer to its parent36 // FIXME: ContentPeerPicker should define an implicit size and not refer to its parent
43 // FIXME: ContentPeerPicker should not be visible: false by default
44 visible: true
45 Component.onCompleted: {37 Component.onCompleted: {
46 contentType = parent.transferContentType;38 contentType = parent.transferContentType;
47 }39 }
4840
=== removed file 'Snapshot.qml'
--- Snapshot.qml 2015-10-22 12:21:14 +0000
+++ Snapshot.qml 1970-01-01 00:00:00 +0000
@@ -1,98 +0,0 @@
1/*
2 * Copyright (C) 2012 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.4
18import QtQuick.Window 2.2
19import Ubuntu.Components 1.3
20
21Item {
22 id: snapshotRoot
23 property alias source: snapshot.source
24 property alias sliding: shoot.running
25 property int orientation
26 property ViewFinderGeometry geometry
27 property bool deviceDefaultIsPortrait: true
28 property bool loading: snapshot.status == Image.Loading
29
30 function startOutAnimation() {
31 shoot.restart()
32 }
33
34 visible: false
35
36 Item {
37 id: container
38 width: parent.width
39 height: parent.height
40
41 Image {
42 id: snapshot
43 anchors.centerIn: parent
44 anchors.verticalCenterOffset: -geometry.y
45 rotation: snapshotRoot.orientation * -1
46
47 asynchronous: true
48 cache: false
49 fillMode: Image.PreserveAspectFit
50 smooth: false
51 width: deviceDefaultIsPortrait ? geometry.height : geometry.width
52 height: deviceDefaultIsPortrait ? geometry.width : geometry.height
53 sourceSize.width: width
54 sourceSize.height: height
55 }
56
57 Image {
58 id: shadow
59
60 property bool rotated: (snapshot.rotation % 180) != 0
61 height: rotated ? snapshot.width : snapshot.height
62 width: units.gu(2)
63 x: (container.width - (rotated ? snapshot.height : snapshot.width)) / 2 - width
64 source: "assets/shadow.png"
65 fillMode: Image.Stretch
66 asynchronous: true
67 cache: false
68 }
69 }
70 property int orientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
71 property var angleToOrientation: {0: "PORTRAIT",
72 90: "LANDSCAPE",
73 270: "INVERTED_LANDSCAPE"}
74
75 SequentialAnimation {
76 id: shoot
77
78 PropertyAction { target: snapshotRoot; property: "visible"; value: true }
79 PauseAnimation { duration: 150 }
80 XAnimator {
81 target: container
82 to: angleToOrientation[orientationAngle] == "PORTRAIT" ? container.width + shadow.width : 0
83 duration: UbuntuAnimation.BriskDuration
84 easing: UbuntuAnimation.StandardEasing
85 }
86 YAnimator {
87 target: container
88 to: angleToOrientation[orientationAngle] == "LANDSCAPE" ? container.height + shadow.width :
89 angleToOrientation[orientationAngle] == "INVERTED_LANDSCAPE" ? -(container.height + shadow.width) : 0
90 duration: UbuntuAnimation.BriskDuration
91 easing: UbuntuAnimation.StandardEasing
92 }
93 PropertyAction { target: snapshot; property: "source"; value: ""}
94 PropertyAction { target: snapshotRoot; property: "visible"; value: false }
95 PropertyAction { target: container; property: "x"; value: 0 }
96 PropertyAction { target: container; property: "y"; value: 0 }
97 }
98}
990
=== modified file 'ViewFinderExportConfirmation.qml'
--- ViewFinderExportConfirmation.qml 2015-12-10 12:17:49 +0000
+++ ViewFinderExportConfirmation.qml 2016-02-26 17:17:00 +0000
@@ -22,39 +22,56 @@
2222
23 property bool isVideo23 property bool isVideo
24 property string mediaPath24 property string mediaPath
25 property Snapshot snapshot25 property bool waitingForPictureCapture: false
2626
27 function confirmExport(path) {27 signal hideRequested()
28 viewFinder.visible = false;28 signal showRequested()
29 viewFinderOverlay.visible = false;29 property ViewFinderGeometry viewFinderGeometry
30 mediaPath = path;
31 if (!isVideo) snapshot.visible = true;
32 visible = true;
33 }
34
35 function hide() {
36 viewFinder.visible = true;
37 viewFinderOverlay.visible = true;
38 snapshot.source = "";
39 snapshot.visible = false;
40 visible = false;
41 }
4230
43 visible: false31 visible: false
4432
33 // For videos show immediately without waiting for the preview to load,
34 // since we will show a progress indicator instead of the preview
35 onMediaPathChanged: if (mediaPath && isVideo) showRequested()
36
37 function photoCaptureStarted() {
38 controls.item.lockPictureOrientation()
39 waitingForPictureCapture = true
40 }
41
45 Loader {42 Loader {
43 id: controls
46 anchors.fill: parent44 anchors.fill: parent
47 asynchronous: true45 asynchronous: true
48 sourceComponent: Component {46 sourceComponent: Component {
49 Item {47 Item {
48 function lockPictureOrientation() { pictureReview.lockOrientation() }
49
50 VideoReview {50 VideoReview {
51 id: videoReview51 id: videoReview
52 anchors.fill: parent52 anchors.fill: parent
53 bottomMargin: buttons.height53 bottomMargin: buttons.height
54 videoPath: mediaPath54 videoPath: isVideo ? mediaPath : ""
55 visible: isVideo55 visible: isVideo
56 }56 }
5757
58 PictureReview {
59 id: pictureReview
60 anchors.fill: parent
61 visible: !isVideo
62 geometry: viewFinderGeometry
63 source: !isVideo ? mediaPath : ""
64
65 // Show export confirmation only when the snapshot is loaded to prevent the
66 // screen being black while the image loads
67 onLoadedChanged: {
68 if (loaded) {
69 viewFinderExportConfirmation.showRequested()
70 waitingForPictureCapture = false
71 }
72 }
73 }
74
58 Item {75 Item {
59 id: buttons76 id: buttons
60 anchors.bottom: parent.bottom77 anchors.bottom: parent.bottom
@@ -74,7 +91,7 @@
74 }91 }
7592
76 iconName: "reload"93 iconName: "reload"
77 onClicked: viewFinderExportConfirmation.hide()94 onClicked: hideRequested()
78 }95 }
7996
80 CircleButton {97 CircleButton {
@@ -90,8 +107,9 @@
90107
91 iconName: "ok"108 iconName: "ok"
92 onClicked: {109 onClicked: {
93 viewFinderExportConfirmation.hide();110 hideRequested();
94 main.exportContent([mediaPath]);111 main.exportContent([mediaPath]);
112 mediaPath = "";
95 }113 }
96 }114 }
97115
@@ -108,8 +126,9 @@
108126
109 iconName: "close"127 iconName: "close"
110 onClicked: {128 onClicked: {
111 viewFinderExportConfirmation.hide();129 hideRequested();
112 main.cancelExport();130 main.cancelExport();
131 mediaPath = "";
113 }132 }
114 }133 }
115 }134 }
116135
=== modified file 'ViewFinderOverlay.qml'
--- ViewFinderOverlay.qml 2016-01-11 15:07:58 +0000
+++ ViewFinderOverlay.qml 2016-02-26 17:17:00 +0000
@@ -32,6 +32,7 @@
32 property real revealProgress: noSpaceHint.visible ? 1.0 : bottomEdge.progress32 property real revealProgress: noSpaceHint.visible ? 1.0 : bottomEdge.progress
33 property var controls: controls33 property var controls: controls
34 property var settings: settings34 property var settings: settings
35 property bool readyForCapture
3536
36 function showFocusRing(x, y) {37 function showFocusRing(x, y) {
37 focusRing.center = Qt.point(x, y);38 focusRing.center = Qt.point(x, y);
@@ -217,12 +218,31 @@
217 photoResolutionOptionsModel.insert(1, optionFitting);218 photoResolutionOptionsModel.insert(1, optionFitting);
218 }219 }
219220
221 // If resolution setting is not supported select the resolution automatically
220 var photoResolution = settings["photoResolution" + camera.advanced.activeCameraIndex];222 var photoResolution = settings["photoResolution" + camera.advanced.activeCameraIndex];
221 // If resolution setting chosen is not supported select the fitting resolution223 if (!isResolutionAnOption(photoResolution)) {
222 if (photoResolution != optionFitting.value &&224 settings["photoResolution" + camera.advanced.activeCameraIndex] = getAutomaticResolution();
223 photoResolution != optionMaximum.value) {225 }
224 settings["photoResolution" + camera.advanced.activeCameraIndex] = optionFitting.value;226 }
225 }227
228 function getAutomaticResolution() {
229 var fittingResolution = sizeToString(camera.advanced.fittingResolution);
230 var maximumResolution = sizeToString(camera.advanced.maximumResolution);
231 if (isResolutionAnOption(fittingResolution)) {
232 return fittingResolution;
233 } else {
234 return maximumResolution;
235 }
236 }
237
238 function isResolutionAnOption(resolution) {
239 for (var i=0; i<photoResolutionOptionsModel.count; i++) {
240 var option = photoResolutionOptionsModel.get(i);
241 if (option.value == resolution) {
242 return true;
243 }
244 }
245 return false;
226 }246 }
227247
228 function updateResolutionOptions() {248 function updateResolutionOptions() {
@@ -249,9 +269,9 @@
249 settings.videoResolution = sizeToString(camera.advanced.videoRecorderResolution);269 settings.videoResolution = sizeToString(camera.advanced.videoRecorderResolution);
250 updateResolutionOptions();270 updateResolutionOptions();
251271
252 // If no resolution has ever been chosen, select the one that fits the screen272 // If no resolution has ever been chosen, select one automatically
253 if (!hasPhotoResolutionSetting) {273 if (!hasPhotoResolutionSetting) {
254 settings["photoResolution" + camera.advanced.activeCameraIndex] = sizeToString(camera.advanced.fittingResolution);274 settings["photoResolution" + camera.advanced.activeCameraIndex] = getAutomaticResolution();
255 }275 }
256 }276 }
257 }277 }
@@ -288,16 +308,6 @@
288 && !camera.photoCaptureInProgress308 && !camera.photoCaptureInProgress
289 opacity: enabled ? 1.0 : 0.3309 opacity: enabled ? 1.0 : 0.3
290310
291 Item {
292 /* Use the 'trigger' feature of Panel so that tapping on the Panel
293 has the same effect as tapping outside of it (bottomEdgeClose) */
294 id: clickReceiver
295 anchors.fill: parent
296 function trigger() {
297 optionsOverlayClose();
298 }
299 }
300
301 /* At startup, opened is false and 'bottomEdge.height' is 0 until311 /* At startup, opened is false and 'bottomEdge.height' is 0 until
302 optionsOverlayLoader has finished loading. When that happens312 optionsOverlayLoader has finished loading. When that happens
303 'bottomEdge.height' becomes non 0 and 'bottomEdge.position' which313 'bottomEdge.height' becomes non 0 and 'bottomEdge.position' which
@@ -321,6 +331,7 @@
321 property bool available: true331 property bool available: true
322 property bool visible: true332 property bool visible: true
323 property bool showInIndicators: true333 property bool showInIndicators: true
334 property bool colorize: !positionSource.isPrecise
324335
325 ListElement {336 ListElement {
326 icon: ""337 icon: ""
@@ -492,7 +503,7 @@
492 property string label: i18n.tr("SD")503 property string label: i18n.tr("SD")
493 property bool isToggle: true504 property bool isToggle: true
494 property int selectedIndex: bottomEdge.indexForValue(removableStorageOptionsModel, settings.preferRemovableStorage)505 property int selectedIndex: bottomEdge.indexForValue(removableStorageOptionsModel, settings.preferRemovableStorage)
495 property bool available: application.removableStoragePresent506 property bool available: StorageLocations.removableStoragePresent
496 property bool visible: available507 property bool visible: available
497508
498 ListElement {509 ListElement {
@@ -555,12 +566,12 @@
555 }566 }
556 ]567 ]
557568
558 /* FIXME: application.removableStoragePresent is not updated dynamically.569 /* FIXME: StorageLocations.removableStoragePresent is not updated dynamically.
559 Workaround that by reading it when the bottom edge is opened/closed.570 Workaround that by reading it when the bottom edge is opened/closed.
560 */571 */
561 Connections {572 Connections {
562 target: bottomEdge573 target: bottomEdge
563 onOpenedChanged: removableStorageOptionsModel.available = application.removableStoragePresent574 onOpenedChanged: removableStorageOptionsModel.available = StorageLocations.removableStoragePresent
564 }575 }
565576
566 function indexForValue(model, value) {577 function indexForValue(model, value) {
@@ -601,6 +612,24 @@
601 }612 }
602 }613 }
603 }614 }
615
616 triggerSize: units.gu(3)
617
618 Item {
619 /* Use the 'trigger' feature of Panel so that tapping on the Panel
620 can be acted upon */
621 id: clickReceiver
622 anchors.fill: parent
623 anchors.topMargin: -bottomEdge.triggerSize
624
625 function trigger() {
626 if (bottomEdge.opened) {
627 optionsOverlayClose();
628 } else {
629 bottomEdge.open();
630 }
631 }
632 }
604 }633 }
605 }634 }
606635
@@ -669,11 +698,11 @@
669698
670 if (camera.captureMode == Camera.CaptureVideo) {699 if (camera.captureMode == Camera.CaptureVideo) {
671 if (main.contentExportMode) {700 if (main.contentExportMode) {
672 camera.videoRecorder.outputLocation = application.temporaryLocation;701 camera.videoRecorder.outputLocation = StorageLocations.temporaryLocation;
673 } else if (application.removableStoragePresent && settings.preferRemovableStorage) {702 } else if (StorageLocations.removableStoragePresent && settings.preferRemovableStorage) {
674 camera.videoRecorder.outputLocation = application.removableStorageVideosLocation;703 camera.videoRecorder.outputLocation = StorageLocations.removableStorageVideosLocation;
675 } else {704 } else {
676 camera.videoRecorder.outputLocation = application.videosLocation;705 camera.videoRecorder.outputLocation = StorageLocations.videosLocation;
677 }706 }
678707
679 if (camera.videoRecorder.recorderState == CameraRecorder.StoppedState) {708 if (camera.videoRecorder.recorderState == CameraRecorder.StoppedState) {
@@ -684,11 +713,10 @@
684 if (!main.contentExportMode) {713 if (!main.contentExportMode) {
685 shootFeedback.start();714 shootFeedback.start();
686 }715 }
716 camera.photoCaptureInProgress = true;
687 camera.imageCapture.setMetadata("Orientation", orientation);717 camera.imageCapture.setMetadata("Orientation", orientation);
688 var position = positionSource.position;718 var position = positionSource.position;
689 if (settings.gpsEnabled && positionSource.valid719 if (settings.gpsEnabled && positionSource.isPrecise) {
690 && position.latitudeValid
691 && position.longitudeValid) {
692 camera.imageCapture.setMetadata("GPSLatitude", position.coordinate.latitude);720 camera.imageCapture.setMetadata("GPSLatitude", position.coordinate.latitude);
693 camera.imageCapture.setMetadata("GPSLongitude", position.coordinate.longitude);721 camera.imageCapture.setMetadata("GPSLongitude", position.coordinate.longitude);
694 camera.imageCapture.setMetadata("GPSTimeStamp", position.timestamp);722 camera.imageCapture.setMetadata("GPSTimeStamp", position.timestamp);
@@ -698,13 +726,12 @@
698 }726 }
699 }727 }
700728
701 camera.photoCaptureInProgress = true;
702 if (main.contentExportMode) {729 if (main.contentExportMode) {
703 camera.imageCapture.captureToLocation(application.temporaryLocation);730 camera.imageCapture.captureToLocation(StorageLocations.temporaryLocation);
704 } else if (application.removableStoragePresent && settings.preferRemovableStorage) {731 } else if (StorageLocations.removableStoragePresent && settings.preferRemovableStorage) {
705 camera.imageCapture.captureToLocation(application.removableStoragePicturesLocation);732 camera.imageCapture.captureToLocation(StorageLocations.removableStoragePicturesLocation);
706 } else {733 } else {
707 camera.imageCapture.captureToLocation(application.picturesLocation);734 camera.imageCapture.captureToLocation(StorageLocations.picturesLocation);
708 }735 }
709 }736 }
710 }737 }
@@ -760,6 +787,11 @@
760 id: positionSource787 id: positionSource
761 updateInterval: 1000788 updateInterval: 1000
762 active: settings.gpsEnabled789 active: settings.gpsEnabled
790 property bool isPrecise: valid
791 && position.latitudeValid
792 && position.longitudeValid
793 && (!position.horizontalAccuracyValid ||
794 position.horizontalAccuracy <= 100)
763 }795 }
764796
765 Connections {797 Connections {
@@ -800,7 +832,7 @@
800 horizontalCenter: parent.horizontalCenter832 horizontalCenter: parent.horizontalCenter
801 }833 }
802834
803 enabled: camera.imageCapture.ready && !storageMonitor.diskSpaceCriticallyLow835 enabled: viewFinderOverlay.readyForCapture && !storageMonitor.diskSpaceCriticallyLow
804 state: (camera.captureMode == Camera.CaptureVideo) ?836 state: (camera.captureMode == Camera.CaptureVideo) ?
805 ((camera.videoRecorder.recorderState == CameraRecorder.StoppedState) ? "record_off" : "record_on") :837 ((camera.videoRecorder.recorderState == CameraRecorder.StoppedState) ? "record_off" : "record_on") :
806 "camera"838 "camera"
@@ -878,15 +910,19 @@
878910
879 MouseArea {911 MouseArea {
880 id: manualFocusMouseArea912 id: manualFocusMouseArea
881 anchors.fill: parent913 anchors {
882 enabled: !camera.photoCaptureInProgress914 fill: parent
915 // Pinch gestures need more clearance at the edges of the screen, but
916 // tap to focus should be safe all the way to the edges themselves instead.
917 leftMargin: -bottomEdgeIndicators.height
918 rightMargin: -bottomEdgeIndicators.height
919 }
920 enabled: camera.focus.isFocusPointModeSupported(Camera.FocusPointCustom) &&
921 !camera.photoCaptureInProgress
883 onClicked: {922 onClicked: {
884 camera.manualFocus(mouse.x, mouse.y);923 camera.manualFocus(mouse.x, mouse.y);
885 mouse.accepted = false;924 mouse.accepted = false;
886 }925 }
887 // FIXME: calling 'isFocusPointModeSupported' fails with
888 // "Error: Unknown method parameter type: QDeclarativeCamera::FocusPointMode"
889 //enabled: camera.focus.isFocusPointModeSupported(Camera.FocusPointCustom)
890 }926 }
891 }927 }
892928
@@ -941,16 +977,29 @@
941 }977 }
942 }978 }
943979
980 ProcessingFeedback {
981 anchors {
982 top: parent.top
983 topMargin: units.gu(2)
984 left: parent.left
985 leftMargin: units.gu(2)
986 }
987 processing: camera.photoCaptureInProgress
988 }
989
944 StorageMonitor {990 StorageMonitor {
945 id: storageMonitor991 id: storageMonitor
946 location: (application.removableStoragePresent && settings.preferRemovableStorage) ?992 location: (StorageLocations.removableStoragePresent && settings.preferRemovableStorage) ?
947 application.removableStorageLocation : application.videosLocation993 StorageLocations.removableStorageLocation : StorageLocations.videosLocation
948 onDiskSpaceLowChanged: if (storageMonitor.diskSpaceLow && !storageMonitor.diskSpaceCriticallyLow) {994 onDiskSpaceLowChanged: if (storageMonitor.diskSpaceLow && !storageMonitor.diskSpaceCriticallyLow) {
949 PopupUtils.open(freeSpaceLowDialogComponent);995 PopupUtils.open(freeSpaceLowDialogComponent);
950 }996 }
951 onDiskSpaceCriticallyLowChanged: if (storageMonitor.diskSpaceCriticallyLow) {997 onDiskSpaceCriticallyLowChanged: if (storageMonitor.diskSpaceCriticallyLow) {
952 camera.videoRecorder.stop();998 camera.videoRecorder.stop();
953 }999 }
1000 onIsWriteableChanged: if (!isWriteable && !diskSpaceLow && !main.contentExportMode) {
1001 PopupUtils.open(readOnlyMediaDialogComponent);
1002 }
954 }1003 }
9551004
956 NoSpaceHint {1005 NoSpaceHint {
@@ -974,6 +1023,20 @@
974 }1023 }
975 }1024 }
9761025
1026 Component {
1027 id: readOnlyMediaDialogComponent
1028 Dialog {
1029 id: readOnlyMediaDialog
1030 objectName: "readOnlyMediaDialog"
1031 title: i18n.tr("External storage not writeable")
1032 text: i18n.tr("It does not seem possible to write to your external storage media. Trying to eject and insert it again might solve the issue, or you might need to format it.")
1033 Button {
1034 text: i18n.tr("Cancel")
1035 onClicked: PopupUtils.close(readOnlyMediaDialog)
1036 }
1037 }
1038 }
1039
977 Connections {1040 Connections {
978 id: permissionErrorMonitor1041 id: permissionErrorMonitor
979 property var currentPermissionsDialog: null1042 property var currentPermissionsDialog: null
9801043
=== modified file 'ViewFinderOverlayLoader.qml'
--- ViewFinderOverlayLoader.qml 2016-01-07 08:03:57 +0000
+++ ViewFinderOverlayLoader.qml 2016-02-26 17:17:00 +0000
@@ -24,6 +24,7 @@
24 property real revealProgress: loader.item ? loader.item.revealProgress : 024 property real revealProgress: loader.item ? loader.item.revealProgress : 0
25 property var controls: loader.item ? loader.item.controls : null25 property var controls: loader.item ? loader.item.controls : null
26 property var settings: loader.item.settings26 property var settings: loader.item.settings
27 property bool readyForCapture
2728
28 function showFocusRing(x, y) {29 function showFocusRing(x, y) {
29 loader.item.showFocusRing(x, y);30 loader.item.showFocusRing(x, y);
@@ -35,6 +36,8 @@
3536
36 asynchronous: true37 asynchronous: true
37 Component.onCompleted: {38 Component.onCompleted: {
38 loader.setSource("ViewFinderOverlay.qml", { "camera": loader.camera });39 loader.setSource("ViewFinderOverlay.qml", { "camera": loader.camera,
40 "readyForCapture": Qt.binding(function() { return loader.readyForCapture})
41 });
39 }42 }
40}43}
4144
=== modified file 'ViewFinderView.qml'
--- ViewFinderView.qml 2016-01-14 15:55:52 +0000
+++ ViewFinderView.qml 2016-02-26 17:17:00 +0000
@@ -17,6 +17,7 @@
17import QtQuick 2.417import QtQuick 2.4
18import QtQuick.Window 2.218import QtQuick.Window 2.2
19import Ubuntu.Components 1.319import Ubuntu.Components 1.3
20import Ubuntu.Components.Popups 1.3
20import QtMultimedia 5.021import QtMultimedia 5.0
21import CameraApp 0.122import CameraApp 0.1
22import QtGraphicalEffects 1.023import QtGraphicalEffects 1.0
@@ -96,36 +97,41 @@
96 property bool switchInProgress: false97 property bool switchInProgress: false
97 property bool photoCaptureInProgress: false98 property bool photoCaptureInProgress: false
9899
100 onPhotoCaptureInProgressChanged: {
101 if (main.contentExportMode && camera.photoCaptureInProgress) {
102 viewFinderExportConfirmation.photoCaptureStarted();
103 }
104 }
105
99 imageCapture {106 imageCapture {
100 onReadyChanged: {107 onReadyChanged: {
101 if (camera.imageCapture.ready && main.transfer) {108 if (camera.imageCapture.ready) {
102 if (main.transfer.contentType === ContentType.Videos) {109 if (camera.photoCaptureInProgress) {
103 viewFinderView.captureMode = Camera.CaptureVideo;110 if (photoRollHint.necessary && !main.transfer) photoRollHint.enable();
104 } else {111 camera.photoCaptureInProgress = false;
105 viewFinderView.captureMode = Camera.CaptureStillImage;112 }
113
114 if (main.transfer) {
115 if (main.transfer.contentType === ContentType.Videos) {
116 viewFinderView.captureMode = Camera.CaptureVideo;
117 } else {
118 viewFinderView.captureMode = Camera.CaptureStillImage;
119 }
106 }120 }
107 }121 }
108 }122 }
123
109 onCaptureFailed: {124 onCaptureFailed: {
125 console.log("Image capture failed for request " + requestId + ": " + message);
110 camera.photoCaptureInProgress = false;126 camera.photoCaptureInProgress = false;
111 console.log("Capture failed for request " + requestId + ": " + message);127 viewFinderOverlay.visible = true;
112 }128 PopupUtils.open(captureFailedDialogComponent);
113 onImageCaptured: {129 }
114 snapshot.source = preview;130
115 if (!main.contentExportMode) {
116 viewFinderOverlay.visible = true;
117 snapshot.startOutAnimation();
118 if (photoRollHint.necessary) {
119 photoRollHint.enable();
120 }
121 }
122 }
123 onImageSaved: {131 onImageSaved: {
124 if (main.contentExportMode) {132 if (main.contentExportMode) viewFinderExportConfirmation.mediaPath = path;
125 viewFinderExportConfirmation.confirmExport(path);133
126 }
127 viewFinderView.photoTaken(path);134 viewFinderView.photoTaken(path);
128 camera.photoCaptureInProgress = false;
129 metricPhotos.increment();135 metricPhotos.increment();
130 console.log("Picture saved as " + path);136 console.log("Picture saved as " + path);
131 }137 }
@@ -135,15 +141,19 @@
135 onRecorderStateChanged: {141 onRecorderStateChanged: {
136 if (videoRecorder.recorderState === CameraRecorder.StoppedState) {142 if (videoRecorder.recorderState === CameraRecorder.StoppedState) {
137 metricVideos.increment()143 metricVideos.increment()
138 viewFinderOverlay.visible = true;
139 viewFinderView.videoShot(videoRecorder.actualLocation);144 viewFinderView.videoShot(videoRecorder.actualLocation);
140 if (main.contentExportMode) {145 if (main.contentExportMode) {
141 viewFinderExportConfirmation.confirmExport(videoRecorder.actualLocation);146 viewFinderExportConfirmation.mediaPath = videoRecorder.actualLocation
142 } else if (photoRollHint.necessary) {147 } else if (photoRollHint.necessary) {
143 photoRollHint.enable();148 photoRollHint.enable();
144 }149 }
145 }150 }
146 }151 }
152 onErrorCodeChanged: {
153 if (videoRecorder.errorCode !== CameraRecorder.NoError) {
154 PopupUtils.open(captureFailedDialogComponent);
155 }
156 }
147 }157 }
148 }158 }
149159
@@ -235,18 +245,21 @@
235 width: parent.width245 width: parent.width
236 height: parent.height246 height: parent.height
237 source: camera247 source: camera
248 opacity: ((main.contentExportMode && viewFinderExportConfirmation.waitingForPictureCapture) ||
249 (!main.contentExportMode && camera.photoCaptureInProgress && !camera.imageCapture.ready))
250 ? 0.1 : 1.0
238251
239 /* This rotation need to be applied since the camera hardware in the252 /* This rotation need to be applied since the camera hardware in the
240 Galaxy Nexus phone is mounted at an angle inside the device, so the video253 Galaxy Nexus phone is mounted at an angle inside the device, so the video
241 feed is rotated too.254 feed is rotated too.
242 FIXME: This should come from a system configuration option so that we255 FIXME: This should come from a system configuration option so that we
243 don't have to have a different codebase for each different device we want256 don't have to have a different codebase for each different device we want
244 to run on. Android has that information and QML has an API to reflect it:257 to run on. Android has that information and QML has an API to reflect it:
245 the camera.orientation property. Unfortunately it is not hooked up yet.258 the camera.orientation property. Unfortunately it is not hooked up yet.
246259
247 Ref.: http://doc.qt.io/qt-5/qml-qtmultimedia-camera.html#orientation-prop260 Ref.: http://doc.qt.io/qt-5/qml-qtmultimedia-camera.html#orientation-prop
248 http://doc.qt.io/qt-5/qcamerainfocontrol.html#cameraOrientation261 http://doc.qt.io/qt-5/qcamerainfocontrol.html#cameraOrientation
249 http://developer.android.com/reference/android/hardware/Camera.CameraInfo.html#orientation262 http://developer.android.com/reference/android/hardware/Camera.CameraInfo.html#orientation
250 */263 */
251 Component.onCompleted: {264 Component.onCompleted: {
252 // Set orientation only at startup because later on Screen.primaryOrientation265 // Set orientation only at startup because later on Screen.primaryOrientation
@@ -254,14 +267,6 @@
254 orientation = Screen.primaryOrientation === Qt.PortraitOrientation ? -90 : 0;267 orientation = Screen.primaryOrientation === Qt.PortraitOrientation ? -90 : 0;
255 }268 }
256269
257 /* Convenience item tracking the real position and size of the real video feed.
258 Having this helps since these values depend on a lot of rules:
259 - the feed is automatically scaled to fit the viewfinder
260 - the viewfinder might apply a rotation to the feed, depending on device orientation
261 - the resolution and aspect ratio of the feed changes depending on the active camera
262 The item is also separated in a component so it can be unit tested.
263 */
264
265 transform: Rotation {270 transform: Rotation {
266 origin.x: viewFinder.width / 2271 origin.x: viewFinder.width / 2
267 origin.y: viewFinder.height / 2272 origin.y: viewFinder.height / 2
@@ -270,6 +275,13 @@
270 }275 }
271 }276 }
272277
278 /* Convenience item tracking the real position and size of the real video feed.
279 Having this helps since these values depend on a lot of rules:
280 - the feed is automatically scaled to fit the viewfinder
281 - the viewfinder might apply a rotation to the feed, depending on device orientation
282 - the resolution and aspect ratio of the feed changes depending on the active camera
283 The item is also separated in a component so it can be unit tested.
284 */
273 ViewFinderGeometry {285 ViewFinderGeometry {
274 id: viewFinderGeometry286 id: viewFinderGeometry
275 anchors.centerIn: parent287 anchors.centerIn: parent
@@ -330,12 +342,10 @@
330 anchors.fill: parent342 anchors.fill: parent
331343
332 function start() {344 function start() {
333 viewFinderOverlay.visible = false;
334 }345 }
335346
336 function stop() {347 function stop() {
337 remainingSecsLabel.text = "";348 remainingSecsLabel.text = "";
338 viewFinderOverlay.visible = true;
339 }349 }
340350
341 function showRemainingSecs(secs) {351 function showRemainingSecs(secs) {
@@ -384,7 +394,6 @@
384394
385 function start() {395 function start() {
386 shootFeedback.opacity = 1.0;396 shootFeedback.opacity = 1.0;
387 viewFinderOverlay.visible = false;
388 shootFeedbackAnimation.restart();397 shootFeedbackAnimation.restart();
389 }398 }
390399
@@ -393,7 +402,7 @@
393 target: shootFeedback402 target: shootFeedback
394 from: 1.0403 from: 1.0
395 to: 0.0404 to: 0.0
396 duration: 50405 duration: UbuntuAnimation.SnapDuration
397 easing: UbuntuAnimation.StandardEasing406 easing: UbuntuAnimation.StandardEasing
398 }407 }
399 }408 }
@@ -410,38 +419,76 @@
410 visible: radius !== 0419 visible: radius !== 0
411 }420 }
412421
422 PhotoRollHint {
423 id: photoRollHint
424 anchors.fill: parent
425 visible: enabled
426
427 Connections {
428 target: viewFinderView
429 onInViewChanged: if (!viewFinderView.inView) photoRollHint.disable()
430 }
431 }
432
413 ViewFinderOverlayLoader {433 ViewFinderOverlayLoader {
414 id: viewFinderOverlay434 id: viewFinderOverlay
415435
416 anchors.fill: parent436 anchors.fill: parent
417 camera: camera437 camera: camera
418 opacity: status == Loader.Ready && overlayVisible && !photoRollHint.enabled ? 1.0 : 0.0438 opacity: status == Loader.Ready && overlayVisible && !photoRollHint.enabled ? 1.0 : 0.0
419 Behavior on opacity {UbuntuNumberAnimation {duration: UbuntuAnimation.SnapDuration}}439 readyForCapture: main.contentExportMode &&
420 }440 viewFinderExportConfirmation.waitingForPictureCapture ? false : camera.imageCapture.ready
421441
422 PhotoRollHint {442 Behavior on opacity {
423 id: photoRollHint443 enabled: !photoRollHint.enabled
424 anchors.fill: parent444 UbuntuNumberAnimation {duration: UbuntuAnimation.SnapDuration}
425 visible: enabled && !snapshot.loading445 }
426446
427 Connections {447 // Tapping anywhere on the screen should not trigger any camera
428 target: viewFinderView448 // controls while PhotoRoll hint is visible
429 onInViewChanged: if (!viewFinderView.inView) photoRollHint.disable()449 MouseArea {
430 }450 anchors.fill: parent
431 }451 enabled: photoRollHint.visible
432452 }
433 Snapshot {
434 id: snapshot
435 anchors.fill: parent
436 orientation: viewFinder.orientation
437 geometry: viewFinderGeometry
438 deviceDefaultIsPortrait: Screen.primaryOrientation === Qt.PortraitOrientation
439 }453 }
440454
441 ViewFinderExportConfirmation {455 ViewFinderExportConfirmation {
442 id: viewFinderExportConfirmation456 id: viewFinderExportConfirmation
443 anchors.fill: parent457 anchors.fill: parent
444 snapshot: snapshot458
445 isVideo: main.transfer.contentType == ContentType.Videos459 isVideo: main.transfer.contentType == ContentType.Videos
460 viewFinderGeometry: viewFinderGeometry
461
462 onShowRequested: {
463 viewFinder.visible = false;
464 viewFinderOverlay.visible = false;
465 visible = true;
466 }
467
468 onHideRequested: {
469 viewFinder.visible = true;
470 viewFinderOverlay.visible = true;
471 visible = false;
472 }
473 }
474
475 Component {
476 id: captureFailedDialogComponent
477 Dialog {
478 id: captureFailedDialog
479 objectName: "captureFailedDialog"
480 title: i18n.tr("Capture failed")
481
482 // If we are capturing to an SD card the problem can be a broken card, otherwise it is probably a
483 // crash in the driver and a reboot might fix things.
484 text: StorageLocations.removableStorageLocation && viewFinderOverlay.settings.preferRemovableStorage ?
485 i18n.tr("Replacing your external media, formatting it, or restarting the device might fix the problem.") :
486 i18n.tr("Restarting your device might fix the problem.")
487
488 Button {
489 text: i18n.tr("Cancel")
490 onClicked: PopupUtils.close(captureFailedDialog)
491 }
492 }
446 }493 }
447}494}
448495
=== modified file 'cameraapplication.cpp'
--- cameraapplication.cpp 2015-11-25 17:47:00 +0000
+++ cameraapplication.cpp 2016-02-26 17:17:00 +0000
@@ -19,14 +19,8 @@
1919
20#include "cameraapplication.h"20#include "cameraapplication.h"
2121
22#include <QtCore/QDir>
23#include <QtCore/QUrl>
24#include <QtCore/QDebug>22#include <QtCore/QDebug>
25#include <QtCore/QStringList>
26#include <QtCore/QLibrary>23#include <QtCore/QLibrary>
27#include <QtCore/QStandardPaths>
28#include <QtCore/QDir>
29#include <QDate>
30#include <QQmlContext>24#include <QQmlContext>
31#include <QQmlEngine>25#include <QQmlEngine>
32#include <QScreen>26#include <QScreen>
@@ -95,103 +89,3 @@
9589
96 return true;90 return true;
97}91}
98
99QString CameraApplication::picturesLocation() const
100{
101 QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
102 if (locations.isEmpty()) {
103 return QString();
104 }
105 QString location = locations.at(0) + "/" + QCoreApplication::applicationName();
106 QDir dir;
107 // Transition from old directory 'camera' to new one; see bug #1363112
108 // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1363112
109 dir.rename(locations.at(0) + "/" + "camera", location);
110 dir.mkpath(location);
111 return location;
112}
113
114QString CameraApplication::videosLocation() const
115{
116 QStringList locations = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation);
117 if (locations.isEmpty()) {
118 return QString();
119 }
120 QString location = locations.at(0) + "/" + QCoreApplication::applicationName();
121 QDir dir;
122 // Transition from old directory 'camera' to new one; see bug #1363112
123 // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1363112
124 dir.rename(locations.at(0) + "/" + "camera", location);
125 dir.mkpath(location);
126 return location;
127}
128
129QString CameraApplication::temporaryLocation() const
130{
131 QStringList locations = QStandardPaths::standardLocations(QStandardPaths::TempLocation);
132 if (locations.isEmpty()) {
133 return QString();
134 }
135 QString location = locations.at(0);
136 QDir dir;
137 dir.mkpath(location);
138 return location;
139}
140
141bool CameraApplication::removableStoragePresent() const
142{
143 return !removableStorageLocation().isEmpty();
144}
145
146QString CameraApplication::removableStorageLocation() const
147{
148 /* FIXME: when Qt5.4 is available, switch to using newly introduced
149 * QStorageInfo API.
150 * Ref.: http://doc-snapshot.qt-project.org/qt5-5.4/qstorageinfo.html
151 */
152 QString userName = qgetenv("USER");
153 QDir media("/media/" + userName);
154 QStringList mediaDirs = media.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
155
156 if (mediaDirs.size() > 0) {
157 return QString("/media/" + userName + "/" + mediaDirs.at(0));
158 } else {
159 return QString();
160 }
161}
162
163QString CameraApplication::removableStoragePicturesLocation() const
164{
165 QString storageLocation = removableStorageLocation();
166 if (storageLocation.isEmpty()) {
167 return QString();
168 }
169
170 QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
171 QString pictureDir = QString(locations.at(0)).split("/").value(3);
172 if (pictureDir.isEmpty()){
173 return QString();
174 }
175 QString location = storageLocation + "/" + pictureDir + "/" + QCoreApplication::applicationName();
176 QDir dir;
177 dir.mkpath(location);
178 return location;
179}
180
181QString CameraApplication::removableStorageVideosLocation() const
182{
183 QString storageLocation = removableStorageLocation();
184 if (storageLocation.isEmpty()) {
185 return QString();
186 }
187
188 QStringList locations = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation);
189 QString movieDir = QString(locations.at(0)).split("/").value(3);
190 if (movieDir.isEmpty()){
191 return QString();
192 }
193 QString location = storageLocation + "/" + movieDir + "/" + QCoreApplication::applicationName();
194 QDir dir;
195 dir.mkpath(location);
196 return location;
197}
19892
=== modified file 'cameraapplication.h'
--- cameraapplication.h 2015-11-25 17:47:00 +0000
+++ cameraapplication.h 2016-02-26 17:17:00 +0000
@@ -29,29 +29,14 @@
29{29{
30 Q_OBJECT30 Q_OBJECT
31 Q_PROPERTY(bool desktopMode READ isDesktopMode CONSTANT)31 Q_PROPERTY(bool desktopMode READ isDesktopMode CONSTANT)
32 Q_PROPERTY(QString picturesLocation READ picturesLocation CONSTANT)
33 Q_PROPERTY(QString videosLocation READ videosLocation CONSTANT)
34 Q_PROPERTY(QString temporaryLocation READ temporaryLocation CONSTANT)
35 Q_PROPERTY(bool removableStoragePresent READ removableStoragePresent NOTIFY removableStoragePresentChanged)
36 Q_PROPERTY(QString removableStorageLocation READ removableStorageLocation CONSTANT)
37 Q_PROPERTY(QString removableStoragePicturesLocation READ removableStoragePicturesLocation CONSTANT)
38 Q_PROPERTY(QString removableStorageVideosLocation READ removableStorageVideosLocation CONSTANT)
3932
40public:33public:
41 CameraApplication(int &argc, char **argv);34 CameraApplication(int &argc, char **argv);
42 virtual ~CameraApplication();35 virtual ~CameraApplication();
43 bool setup();36 bool setup();
44 bool isDesktopMode() const;37 bool isDesktopMode() const;
45 QString picturesLocation() const;
46 QString videosLocation() const;
47 QString temporaryLocation() const;
48 bool removableStoragePresent() const;
49 QString removableStorageLocation() const;
50 QString removableStoragePicturesLocation() const;
51 QString removableStorageVideosLocation() const;
5238
53Q_SIGNALS:39Q_SIGNALS:
54 void removableStoragePresentChanged();
5540
56private:41private:
57 QScopedPointer<QQmlApplicationEngine> m_engine;42 QScopedPointer<QQmlApplicationEngine> m_engine;
5843
=== modified file 'debian/rules'
--- debian/rules 2015-11-18 06:44:35 +0000
+++ debian/rules 2016-02-26 17:17:00 +0000
@@ -11,10 +11,6 @@
11override_dh_auto_configure:11override_dh_auto_configure:
12 dh_auto_configure -- -DCLICK_MODE=OFF -DINSTALL_TESTS=ON12 dh_auto_configure -- -DCLICK_MODE=OFF -DINSTALL_TESTS=ON
1313
14override_dh_auto_test:
15 python3 -m flake8 .
16 dh_auto_test
17
18override_dh_install:14override_dh_install:
19 dh_install --fail-missing15 dh_install --fail-missing
2016
2117
=== modified file 'manifest.json.in'
--- manifest.json.in 2015-11-25 17:56:59 +0000
+++ manifest.json.in 2016-02-26 17:17:00 +0000
@@ -1,6 +1,6 @@
1{1{
2 "description": "An application to take pictures and videos with the device cameras",2 "description": "An application to take pictures and videos with the device cameras",
3 "framework": "ubuntu-sdk-15.04.3",3 "framework": "ubuntu-sdk-15.04.4",
4 "architecture": "@CLICK_ARCH@",4 "architecture": "@CLICK_ARCH@",
5 "hooks": {5 "hooks": {
6 "camera": {6 "camera": {
77
=== removed file 'run_tests.sh'
--- run_tests.sh 2012-10-24 15:18:16 +0000
+++ run_tests.sh 1970-01-01 00:00:00 +0000
@@ -1,13 +0,0 @@
1#!/bin/bash
2export PATH=/opt/qt5/bin:$PATH
3cd tests/autopilot
4
5echo running with arg: $1
6
7if [ "$1" == "" ]; then
8 autopilot run camera_app
9else
10 autopilot run -o ../../$1 -f xml -r -rd ../../ camera_app
11fi
12
13exit 0
140
=== modified file 'tests/autopilot/camera_app/tests/test_focus.py'
--- tests/autopilot/camera_app/tests/test_focus.py 2015-10-05 13:14:12 +0000
+++ tests/autopilot/camera_app/tests/test_focus.py 2016-02-26 17:17:00 +0000
@@ -28,6 +28,20 @@
28 def tearDown(self):28 def tearDown(self):
29 super(TestFocus, self).tearDown()29 super(TestFocus, self).tearDown()
3030
31 def verify_focus_ring_after_click_at(self, ring, x, y):
32 # The focus ring should be invisible in the beginning
33 self.assertThat(ring.opacity, Eventually(Equals(0.0)))
34
35 # Click in the designated spot
36 self.pointing_device.move(x, y)
37 self.pointing_device.click()
38
39 # The focus ring sould be visible now
40 self.assertThat(ring.opacity, Eventually(GreaterThan(0.5)))
41
42 # After some seconds the focus ring should fade out
43 self.assertThat(ring.opacity, Eventually(Equals(0.0)))
44
31 """Test focusing in an area where we know the picture is"""45 """Test focusing in an area where we know the picture is"""
32 @unittest.skipIf(model() == 'Galaxy Nexus', 'Unusable with Mir on maguro')46 @unittest.skipIf(model() == 'Galaxy Nexus', 'Unusable with Mir on maguro')
33 def test_focus_valid_and_disappear(self):47 def test_focus_valid_and_disappear(self):
@@ -36,21 +50,16 @@
36 switch_cameras = self.main_window.get_swap_camera_button()50 switch_cameras = self.main_window.get_swap_camera_button()
37 exposure_button = self.main_window.get_exposure_button()51 exposure_button = self.main_window.get_exposure_button()
3852
39 # The focus ring should be invisible in the beginning53 # Click in the center of the viewfinder area
40 self.assertThat(focus_ring.opacity, Eventually(Equals(0.0)))54 mid_x, mid_y = self.get_center(feed)
4155 self.verify_focus_ring_after_click_at(focus_ring, mid_x, mid_y)
42 self.pointing_device.move_to_object(feed)56
43 self.pointing_device.click()57 # Then try on the side edges and top edge to verify they
44 click_coords = list(self.pointing_device.position())58 # are focusable too
4559 self.verify_focus_ring_after_click_at(focus_ring, 1, mid_y)
46 # The focus ring sould be visible and centered to the mouse click60 self.verify_focus_ring_after_click_at(focus_ring, feed.width - 1,
47 # coords now61 mid_y)
48 # focus_ring_center = self.get_center(focus_ring)62 self.verify_focus_ring_after_click_at(focus_ring, mid_x, 1)
49 self.assertThat(focus_ring.opacity, Eventually(GreaterThan(0.5)))
50# self.assertEquals(focus_ring_center, click_coords)
51
52 # After some seconds the focus ring should fade out
53 self.assertThat(focus_ring.opacity, Eventually(Equals(0.0)))
5463
55 # Switch cameras, wait for camera to settle, and try again64 # Switch cameras, wait for camera to settle, and try again
56 self.pointing_device.move_to_object(switch_cameras)65 self.pointing_device.move_to_object(switch_cameras)
@@ -58,19 +67,14 @@
58 self.assertThat(exposure_button.enabled, Eventually(Equals(True)))67 self.assertThat(exposure_button.enabled, Eventually(Equals(True)))
5968
60 # Click in the center of the viewfinder area69 # Click in the center of the viewfinder area
61 click_coords = [feed.globalRect[2] // 2 + feed.globalRect[0],70 self.verify_focus_ring_after_click_at(focus_ring, mid_x, mid_y)
62 feed.globalRect[3] // 2 + feed.globalRect[1]]71
63 self.pointing_device.move(click_coords[0], click_coords[1])72 # Then try on the side edges and top edge to verify they
64 self.pointing_device.click()73 # are focusable too
6574 self.verify_focus_ring_after_click_at(focus_ring, 1, mid_y)
66 # The focus ring sould be visible and centered to the mouse75 self.verify_focus_ring_after_click_at(focus_ring, feed.width - 1,
67 # click coords now76 mid_y)
68 # focus_ring_center = self.get_center(focus_ring)77 self.verify_focus_ring_after_click_at(focus_ring, mid_x, 1)
69 self.assertThat(focus_ring.opacity, Eventually(GreaterThan(0.5)))
70# self.assertEquals(focus_ring_center, click_coords)
71
72 # After some seconds the focus ring should fade out
73 self.assertThat(focus_ring.opacity, Eventually(Equals(0.0)))
7478
75 @unittest.skipIf(model() == 'Galaxy Nexus', 'Unusable with Mir on maguro')79 @unittest.skipIf(model() == 'Galaxy Nexus', 'Unusable with Mir on maguro')
76 def test_focus_invalid(self):80 def test_focus_invalid(self):
7781
=== modified file 'tests/autopilot/camera_app/tests/test_options.py'
--- tests/autopilot/camera_app/tests/test_options.py 2015-11-20 15:01:02 +0000
+++ tests/autopilot/camera_app/tests/test_options.py 2016-02-26 17:17:00 +0000
@@ -42,6 +42,33 @@
42 # check overlay is closed42 # check overlay is closed
43 self.assertThat(bottom_edge.opened, Eventually(Equals(False)))43 self.assertThat(bottom_edge.opened, Eventually(Equals(False)))
4444
45 # try opening and closing by tapping on the bottom of the viewfinder
46 bottom_edge = self.main_window.get_bottom_edge()
47 bottom_edge.open()
48
49 # check overlay is opened
50 self.assertThat(bottom_edge.opened, Eventually(Equals(True)))
51
52 # tap on the bottom of the viewfinder to close overlay
53 viewfinder = self.main_window.get_viewfinder()
54 x = viewfinder.globalRect.x + viewfinder.width / 2.0
55 y = viewfinder.globalRect.y + viewfinder.height - 1.0
56 self.pointing_device.move(x, y)
57 self.pointing_device.click()
58
59 # check overlay is closed
60 self.assertThat(bottom_edge.opened, Eventually(Equals(False)))
61
62 """Test that the options overlay opens properly by tapping on the hint"""
63 def test_overlay_open_tapping_hint(self):
64 options_hint = self.app.wait_select_single(objectName="indicatorsRow")
65 self.pointing_device.move_to_object(options_hint)
66 self.pointing_device.click()
67
68 # check overlay is opened
69 bottom_edge = self.main_window.get_bottom_edge()
70 self.assertThat(bottom_edge.opened, Eventually(Equals(True)))
71
45 """Test toggling on/off grid lines option"""72 """Test toggling on/off grid lines option"""
46 def test_toggle_grid_lines(self):73 def test_toggle_grid_lines(self):
47 gridlines = self.app.wait_select_single(74 gridlines = self.app.wait_select_single(
4875
=== modified file 'tests/unittests/CMakeLists.txt'
--- tests/unittests/CMakeLists.txt 2015-03-12 22:36:12 +0000
+++ tests/unittests/CMakeLists.txt 2016-02-26 17:17:00 +0000
@@ -33,6 +33,7 @@
3333
34qt5_use_modules(tst_storagemonitor Widgets Core Quick Qml Test)34qt5_use_modules(tst_storagemonitor Widgets Core Quick Qml Test)
35add_test(tst_storagemonitor tst_storagemonitor -xunitxml -o test.xml)35add_test(tst_storagemonitor tst_storagemonitor -xunitxml -o test.xml)
36add_test(flake8 python3 -m flake8 ${CMAKE_SOURCE_DIR}/tests/autopilot)
36set_tests_properties(tst_storagemonitor PROPERTIES37set_tests_properties(tst_storagemonitor PROPERTIES
37 TIMEOUT ${CTEST_TESTING_TIMEOUT}38 TIMEOUT ${CTEST_TESTING_TIMEOUT}
38 ENVIRONMENT "QT_QPA_PLATFORM=minimal"39 ENVIRONMENT "QT_QPA_PLATFORM=minimal"
3940
=== modified file 'tests/unittests/qstorageinfo_stub.cpp'
--- tests/unittests/qstorageinfo_stub.cpp 2015-01-26 10:57:36 +0000
+++ tests/unittests/qstorageinfo_stub.cpp 2016-02-26 17:17:00 +0000
@@ -14,14 +14,23 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */15 */
1616
17#include "qstorageinfo.h"17#include <QStorageInfo>
18#include "qstorageinfo_p.h"
19#include "storageinfocontrol.h"18#include "storageinfocontrol.h"
2019
21#include <QDebug>20#include <QDebug>
2221
23QString location;22QString location;
2423
24class QStorageInfoPrivateRef {
25public:
26 bool deref() { return false; }
27};
28
29class QStorageInfoPrivate {
30public:
31 QStorageInfoPrivateRef ref;
32};
33
25QStorageInfo::QStorageInfo()34QStorageInfo::QStorageInfo()
26{35{
27}36}
@@ -47,6 +56,11 @@
47 location = path;56 location = path;
48}57}
4958
59QString QStorageInfo::rootPath() const
60{
61 return location;
62}
63
50qint64 QStorageInfo::bytesAvailable() const64qint64 QStorageInfo::bytesAvailable() const
51{65{
52 return StorageInfoControl::instance()->freeSpaceMap[location];66 return StorageInfoControl::instance()->freeSpaceMap[location];

Subscribers

People subscribed via source and target branches