Merge lp:~mzanetti/reminders-app/shoot into lp:reminders-app

Proposed by Michael Zanetti
Status: Merged
Approved by: Michael Zanetti
Approved revision: 39
Merged at revision: 50
Proposed branch: lp:~mzanetti/reminders-app/shoot
Merge into: lp:reminders-app
Prerequisite: lp:~mzanetti/reminders-app/cleanup
Diff against target: 409 lines (+235/-19)
10 files modified
CMakeLists.txt (+2/-2)
src/app/CMakeLists.txt (+4/-1)
src/app/camerahelper.cpp (+52/-0)
src/app/camerahelper.h (+39/-0)
src/app/main.cpp (+21/-12)
src/app/qml/ui/CameraPage.qml (+70/-0)
src/app/qml/ui/EditNotePage.qml (+4/-0)
src/plugin/Evernote/note.cpp (+11/-0)
src/plugin/Evernote/resource.cpp (+21/-4)
src/plugin/Evernote/utils/enmldocument.cpp (+11/-0)
To merge this branch: bzr merge lp:~mzanetti/reminders-app/shoot
Reviewer Review Type Date Requested Status
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Riccardo Padovani Approve
David Planella Pending
Review via email: mp+203418@code.launchpad.net

This proposal supersedes a proposal from 2014-01-24.

Commit message

Add feature to take a picture with the camera and attach it to the note.

Description of the change

Note: This changes the main.cpp to be mandatory. The CameraHelper class really doesn't belong to the evernote plugin and with the current Ubuntu SDK it's impossible to use the QML Camera element with QML only.

Because of this I added the CameraHelper to the main.cpp which in turn requires us to use the built reminders binary to launch instead of qmlscene.

To post a comment you must log in.
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
David Planella (dpm) wrote : Posted in a previous version of this proposal

The branch worked nicely, but I noticed a couple of things:

1) The camera pictures on the device (Nexus 4) are offset 90 degrees counterclockwise
2) It uses the default save location of the Camera component (e.g. /home/phablet/Pictures/image20140127_0001.jpg), which would require extra apparmor permissions and a change to the manifest. It might be ok, but we should discuss whether we want the Reminders shots stored or not.

I'm not sure it's related to this change, but I've noticed the same thing I did when I tested a previous branch:

- After creating a note and taking a shot, once saved and back on the notes list, the "add note" button in the toolbar apparently no longer works. However, it does work: but it takes about 30 seconds to react. I saw this in the debug output, but I'm not sure it's related:

Got a transport exception: Could not refill buffer . Trying to reestablish connection...
Thrift: Mon Jan 27 09:08:53 2014 SSL_shutdown: error code: 0

I'll try to reproduce and file a bug properly. I just wanted to note that with the recent branches (from Content Hub onwards) have brought up this issue.

review: Needs Fixing
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
lp:~mzanetti/reminders-app/shoot updated
38. By Michael Zanetti

debug--

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
lp:~mzanetti/reminders-app/shoot updated
39. By Michael Zanetti

return something in CameraHelper::save()

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Riccardo Padovani (rpadovani) wrote :

Works well, I can add take without problems

> 1) The camera pictures on the device (Nexus 4) are offset 90 degrees counterclockwise

Not anymore

> - After creating a note and taking a shot, once saved and back on the notes list, the "add note" button in the
> toolbar apparently no longer works. However, it does work: but it takes about 30 seconds to react. I saw this in the > debug output, but I'm not sure it's related:

Confirm that add note button doesn't work for ~30 sec, but I haven't error messages on my terminal

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

> Confirm that add note button doesn't work for ~30 sec, but I haven't error
> messages on my terminal

This happens if we're waiting for a response from the evernote server and it takes so long. Given that we don't support offline note editing with later syncing, the current code needs to wait for a confirmation when creating a new note. This should obviously change in the future, but out of the scope of this merge.

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) :
review: Approve (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2014-01-28 12:18:59 +0000
3+++ CMakeLists.txt 2014-01-28 12:18:59 +0000
4@@ -33,11 +33,11 @@
5 set(CMAKE_INSTALL_PREFIX /)
6 set(CMAKE_INSTALL_BINDIR /)
7 set(DATA_DIR /)
8- set(EXEC "qmlscene $@ -I .${QT_IMPORTS_DIR} qml/${MAIN_QML}")
9+ set(EXEC "./reminders -I .${QT_IMPORTS_DIR}")
10 set(DESKTOP_DIR ${DATA_DIR})
11 else(CLICK_MODE)
12 set(DATA_DIR ${CMAKE_INSTALL_DATADIR}/${APP_HARDCODE})
13- set(EXEC "qmlscene $@ ${CMAKE_INSTALL_PREFIX}/${DATA_DIR}/qml/${MAIN_QML}")
14+ set(EXEC "reminders")
15 set(ICON "${CMAKE_INSTALL_PREFIX}/${DATA_DIR}/${ICON}")
16 set(DESKTOP_DIR ${CMAKE_INSTALL_DATADIR}/applications)
17 endif(CLICK_MODE)
18
19=== modified file 'src/app/CMakeLists.txt'
20--- src/app/CMakeLists.txt 2014-01-28 12:18:59 +0000
21+++ src/app/CMakeLists.txt 2014-01-28 12:18:59 +0000
22@@ -2,6 +2,7 @@
23
24 set(reminders_SRCS
25 main.cpp
26+ camerahelper.cpp
27 ${QML_SRCS}
28 )
29
30@@ -15,6 +16,8 @@
31 )
32
33 install(DIRECTORY qml DESTINATION ${DATA_DIR})
34-if(NOT CLICK_MODE)
35+if(CLICK_MODE)
36+ install(TARGETS reminders DESTINATION ${DATA_DIR})
37+else()
38 install(TARGETS reminders RUNTIME DESTINATION bin)
39 endif()
40
41=== added file 'src/app/camerahelper.cpp'
42--- src/app/camerahelper.cpp 1970-01-01 00:00:00 +0000
43+++ src/app/camerahelper.cpp 2014-01-28 12:18:59 +0000
44@@ -0,0 +1,52 @@
45+/*
46+ * Copyright: 2013 Canonical, Ltd
47+ *
48+ * This file is part of reminders
49+ *
50+ * reminders is free software: you can redistribute it and/or modify
51+ * it under the terms of the GNU General Public License as published by
52+ * the Free Software Foundation, either version 3 of the License, or
53+ * (at your option) any later version.
54+ *
55+ * reminders is distributed in the hope that it will be useful,
56+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
57+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
58+ * GNU General Public License for more details.
59+ *
60+ * You should have received a copy of the GNU General Public License
61+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
62+ *
63+ * Authors: Michael Zanetti <michael.zanetti@canonical.com>
64+ */
65+
66+#include "camerahelper.h"
67+
68+#include <QStandardPaths>
69+#include <QCoreApplication>
70+#include <QImage>
71+#include <QTransform>
72+
73+CameraHelper::CameraHelper(QObject *parent):
74+ QObject(parent)
75+{
76+
77+}
78+
79+QString CameraHelper::importLocation() const
80+{
81+ QString homePath = QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first();
82+ QString appName = QCoreApplication::applicationName();
83+ return homePath + "/.cache/" + appName + '/';
84+}
85+
86+bool CameraHelper::rotate(const QString &imageFile, int angle)
87+{
88+ QImage image;
89+ if (!image.load(imageFile)) {
90+ return false;
91+ }
92+ QTransform transform;
93+ transform.rotate(angle);
94+ image = image.transformed(transform);
95+ return image.save(imageFile);
96+}
97
98=== added file 'src/app/camerahelper.h'
99--- src/app/camerahelper.h 1970-01-01 00:00:00 +0000
100+++ src/app/camerahelper.h 2014-01-28 12:18:59 +0000
101@@ -0,0 +1,39 @@
102+/*
103+ * Copyright: 2013 Canonical, Ltd
104+ *
105+ * This file is part of reminders
106+ *
107+ * reminders is free software: you can redistribute it and/or modify
108+ * it under the terms of the GNU General Public License as published by
109+ * the Free Software Foundation, either version 3 of the License, or
110+ * (at your option) any later version.
111+ *
112+ * reminders is distributed in the hope that it will be useful,
113+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
114+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
115+ * GNU General Public License for more details.
116+ *
117+ * You should have received a copy of the GNU General Public License
118+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
119+ *
120+ * Authors: Michael Zanetti <michael.zanetti@canonical.com>
121+ */
122+
123+#ifndef CAMERAHELPER_H
124+#define CAMERAHELPER_H
125+
126+#include <QObject>
127+
128+class CameraHelper: public QObject
129+{
130+ Q_OBJECT
131+ Q_PROPERTY(QString importLocation READ importLocation CONSTANT)
132+public:
133+ CameraHelper(QObject *parent = 0);
134+
135+ QString importLocation() const;
136+
137+ Q_INVOKABLE bool rotate(const QString &imageFile, int angle);
138+};
139+
140+#endif
141
142=== modified file 'src/app/main.cpp'
143--- src/app/main.cpp 2014-01-28 12:18:59 +0000
144+++ src/app/main.cpp 2014-01-28 12:18:59 +0000
145@@ -19,34 +19,43 @@
146 * Authors: Michael Zanetti <michael.zanetti@canonical.com>
147 */
148
149+#include "camerahelper.h"
150+
151 #include <QtGui/QGuiApplication>
152 #include <QtQuick/QQuickView>
153 #include <QtQml/QtQml>
154
155 #include <QDebug>
156
157-/*
158- * This is just a minimalistic main to fire up our own qml scene which has the
159- * import path for the plugin preconfigured. This is just used for easier
160- * development and while we can ship this, we could also run the app ourselves
161- * with:
162- * qmlscene -I /path/to/plugin/ reminders.qml
163- */
164-
165 int main(int argc, char *argv[])
166 {
167-
168- // Do the same as qmlscene does
169 QGuiApplication a(argc, argv);
170 QQuickView view;
171 view.setResizeMode(QQuickView::SizeRootObjectToView);
172
173- // Additionally add the -I ../plugin to load the plugin
174+ // Set up import paths
175 QStringList importPathList = view.engine()->importPathList();
176 importPathList.append(QDir::currentPath() + "/../plugin/");
177+
178+ QStringList args = a.arguments();
179+ for (int i = 0; i < args.count(); i++) {
180+ if (args.at(i) == "-I" && args.count() > i + 1) {
181+ QString addedPath = args.at(i+1);
182+ if (addedPath.startsWith('.')) {
183+ addedPath = addedPath.right(addedPath.length() - 1);
184+ addedPath.prepend(QDir::currentPath());
185+ }
186+ importPathList.append(addedPath);
187+ }
188+ }
189+
190 view.engine()->setImportPathList(importPathList);
191
192- // and directly load the qml file
193+ // Set up camera helper
194+ CameraHelper helper;
195+ view.engine()->rootContext()->setContextProperty("cameraHelper", &helper);
196+
197+ // load the qml file
198 view.setSource(QUrl::fromLocalFile("qml/reminders.qml"));
199
200 view.show();
201
202=== added file 'src/app/qml/ui/CameraPage.qml'
203--- src/app/qml/ui/CameraPage.qml 1970-01-01 00:00:00 +0000
204+++ src/app/qml/ui/CameraPage.qml 2014-01-28 12:18:59 +0000
205@@ -0,0 +1,70 @@
206+/*
207+ * Copyright: 2013 Canonical, Ltd
208+ *
209+ * This file is part of reminders-app
210+ *
211+ * reminders-app is free software: you can redistribute it and/or modify
212+ * it under the terms of the GNU General Public License as published by
213+ * the Free Software Foundation; version 3.
214+ *
215+ * reminders-app is distributed in the hope that it will be useful,
216+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
217+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
218+ * GNU General Public License for more details.
219+ *
220+ * You should have received a copy of the GNU General Public License
221+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
222+ */
223+
224+import QtQuick 2.0
225+import Ubuntu.Components 0.1
226+import QtMultimedia 5.0
227+import QtQuick.Window 2.0
228+import Evernote 0.1
229+
230+Page {
231+ id: root
232+ property var note
233+ property int position
234+
235+ tools: ToolbarItems {
236+ locked: true
237+ opened: true
238+ ToolbarButton {
239+ text: "Shoot"
240+ iconName: "camera-symbolic"
241+ onTriggered: camera.imageCapture.captureToLocation(cameraHelper.importLocation);
242+ }
243+ }
244+
245+ Camera {
246+ id: camera
247+ flash.mode: Camera.FlashTorch
248+ focus.focusMode: Camera.FocusContinuous
249+ focus.focusPointMode: Camera.FocusPointAuto
250+
251+ imageCapture {
252+
253+ onImageSaved: {
254+ if (videoOutput.orientation != 0) {
255+ cameraHelper.rotate(path, -videoOutput.orientation)
256+ }
257+
258+ root.note.attachFile(root.position, path)
259+ print("got image", path)
260+ pagestack.pop();
261+ }
262+ }
263+ }
264+
265+ VideoOutput {
266+ id: videoOutput
267+ anchors {
268+ fill: parent
269+ }
270+ fillMode: Image.PreserveAspectCrop
271+ orientation: Screen.primaryOrientation === Qt.PortraitOrientation ? -90 : 0
272+ source: camera
273+ focus: visible
274+ }
275+}
276
277=== modified file 'src/app/qml/ui/EditNotePage.qml'
278--- src/app/qml/ui/EditNotePage.qml 2014-01-28 12:18:59 +0000
279+++ src/app/qml/ui/EditNotePage.qml 2014-01-28 12:18:59 +0000
280@@ -172,6 +172,10 @@
281 MouseArea {
282 anchors.fill: parent
283 onClicked: {
284+ priv.insertPosition = noteTextArea.cursorPosition;
285+ note.richTextContent = noteTextArea.text;
286+
287+ pagestack.push(Qt.resolvedUrl("CameraPage.qml"), {note: root.note, position: priv.insertPosition})
288 }
289 }
290 }
291
292=== modified file 'src/plugin/Evernote/note.cpp'
293--- src/plugin/Evernote/note.cpp 2014-01-28 12:18:59 +0000
294+++ src/plugin/Evernote/note.cpp 2014-01-28 12:18:59 +0000
295@@ -242,9 +242,20 @@
296
297 void Note::attachFile(int position, const QUrl &fileName)
298 {
299+ QFile importedFile(fileName.toString());
300+ if (!importedFile.exists()) {
301+ return;
302+ }
303+
304+ qDebug() << "attaching file" << position << fileName;
305 Resource *resource = addResource(fileName.path());
306 m_content.attachFile(position, fileName.path(), resource->hash(), resource->type());
307 emit contentChanged();
308+
309+ // Cleanup imported file.
310+ // TODO: If the app should be extended to allow attaching other files, and we somehow
311+ // can browse to unconfined files, this needs to be made conditional to not delete those files!
312+ importedFile.remove();
313 }
314
315 Note *Note::clone()
316
317=== modified file 'src/plugin/Evernote/resource.cpp'
318--- src/plugin/Evernote/resource.cpp 2014-01-28 12:18:59 +0000
319+++ src/plugin/Evernote/resource.cpp 2014-01-28 12:18:59 +0000
320@@ -24,6 +24,7 @@
321 #include <QStandardPaths>
322 #include <QDebug>
323 #include <QCryptographicHash>
324+#include <QFileInfo>
325
326 Resource::Resource(const QByteArray &data, const QString &hash, const QString &fileName, const QString &type, QObject *parent):
327 m_hash(hash),
328@@ -45,18 +46,18 @@
329 }
330 }
331
332-Resource::Resource(const QString &path, QObject *parent):
333- m_filePath(path)
334+Resource::Resource(const QString &path, QObject *parent)
335 {
336+
337 QFile file(path);
338 if (!file.open(QFile::ReadOnly)) {
339 qWarning() << "Cannot open file for reading...";
340 return;
341 }
342-
343 QByteArray fileContent = file.readAll();
344- m_hash = QCryptographicHash::hash(fileContent, QCryptographicHash::Md5).toHex();
345+ file.close();
346
347+ m_hash = QCryptographicHash::hash(fileContent, QCryptographicHash::Md5).toHex();
348 m_fileName = path.split('/').last();
349 if (m_fileName.endsWith(".png")) {
350 m_type = "image/png";
351@@ -65,6 +66,22 @@
352 } else {
353 qWarning() << "cannot determine mime type of file" << m_fileName;
354 }
355+
356+ m_filePath = QStandardPaths::standardLocations(QStandardPaths::CacheLocation).first() + "/" + m_hash + "." + m_type.split('/').last();
357+
358+ QFile copy(m_filePath);
359+ if (!copy.exists()) {
360+
361+ if (!copy.open(QFile::WriteOnly)) {
362+ qWarning() << "error writing file" << m_filePath;
363+ return;
364+ }
365+ copy.write(fileContent);
366+ copy.close();
367+ }
368+
369+
370+ qDebug() << "created resource" << m_hash;
371 }
372
373 QString Resource::hash() const
374
375=== modified file 'src/plugin/Evernote/utils/enmldocument.cpp'
376--- src/plugin/Evernote/utils/enmldocument.cpp 2014-01-28 12:18:59 +0000
377+++ src/plugin/Evernote/utils/enmldocument.cpp 2014-01-28 12:18:59 +0000
378@@ -379,6 +379,7 @@
379 writer.writeDTD("<!DOCTYPE en-note SYSTEM \"http://xml.evernote.com/pub/enml2.dtd\">");
380
381 int textPos = 0;
382+ bool inserted = false;
383
384 while (!reader.atEnd() && !reader.hasError()) {
385 QXmlStreamReader::TokenType token = reader.readNext();
386@@ -397,6 +398,7 @@
387 writer.writeAttribute("hash", hash);
388 writer.writeAttribute("type", type);
389 writer.writeEndElement();
390+ inserted = true;
391
392 writer.writeCharacters(textString.right(textString.length() - (position - textPos)));
393 } else {
394@@ -405,6 +407,15 @@
395 textPos += textString.length();
396 }
397 if (token == QXmlStreamReader::EndElement) {
398+
399+ // The above logic would fail on an empty note
400+ if (reader.name() == "en-note" && !inserted) {
401+ writer.writeStartElement("en-media");
402+ writer.writeAttribute("hash", hash);
403+ writer.writeAttribute("type", type);
404+ writer.writeEndElement();
405+ }
406+
407 writer.writeEndElement();
408 }
409 }

Subscribers

People subscribed via source and target branches