Merge lp:~renatofilho/address-book-app/release-2014-06-18 into lp:address-book-app

Proposed by Renato Araujo Oliveira Filho
Status: Merged
Merged at revision: 212
Proposed branch: lp:~renatofilho/address-book-app/release-2014-06-18
Merge into: lp:address-book-app
Diff against target: 1955 lines (+671/-514)
36 files modified
src/app/imagescalethread.cpp (+1/-1)
src/imports/Common/ContactDetailBase.qml (+1/-0)
src/imports/Common/ContactDetailItem.qml (+1/-0)
src/imports/ContactEdit/AddFieldDialog.qml (+142/-0)
src/imports/ContactEdit/AvatarImport.qml (+78/-0)
src/imports/ContactEdit/CMakeLists.txt (+2/-0)
src/imports/ContactEdit/ContactDetailAvatarEditor.qml (+45/-94)
src/imports/ContactEdit/ContactDetailGroupWithTypeEditor.qml (+4/-30)
src/imports/ContactEdit/ContactDetailNameEditor.qml (+1/-0)
src/imports/ContactEdit/ContactDetailSyncTargetEditor.qml (+8/-2)
src/imports/ContactEdit/ContactDetailWithTypeEditor.qml (+27/-27)
src/imports/ContactEdit/ContactEditor.qml (+95/-28)
src/imports/ContactEdit/TextInputDetail.qml (+1/-0)
src/imports/ContactEdit/ValueSelector.qml (+12/-15)
src/imports/ContactList/ContactListPage.qml (+3/-5)
src/imports/ContactList/PageWithBottomEdge.qml (+39/-4)
src/imports/ContactView/ActionButton.qml (+2/-2)
src/imports/ContactView/BasicFieldView.qml (+27/-40)
src/imports/ContactView/CMakeLists.txt (+0/-1)
src/imports/ContactView/ContactDetailAvatarView.qml (+14/-60)
src/imports/ContactView/ContactDetailFavoriteView.qml (+0/-43)
src/imports/ContactView/ContactDetailGroupWithTypeView.qml (+2/-2)
src/imports/ContactView/ContactDetailPhoneNumberView.qml (+15/-37)
src/imports/ContactView/ContactDetailPhoneNumbersView.qml (+0/-5)
src/imports/ContactView/ContactDetailWithTypeView.qml (+0/-2)
src/imports/ContactView/ContactView.qml (+22/-20)
src/imports/Ubuntu/Contacts/ContactAvatar.qml (+13/-3)
src/imports/Ubuntu/Contacts/ContactDelegate.qml (+1/-2)
src/imports/Ubuntu/Contacts/ContactDetailPickerPhoneNumberDelegate.qml (+7/-17)
src/imports/Ubuntu/Contacts/ContactListView.qml (+5/-0)
src/imports/Ubuntu/Contacts/ContactSimpleListView.qml (+3/-1)
src/imports/Ubuntu/Contacts/MultipleSelectionListView.qml (+9/-1)
tests/autopilot/address_book_app/pages/_contact_editor.py (+42/-20)
tests/autopilot/address_book_app/tests/__init__.py (+2/-6)
tests/autopilot/address_book_app/tests/test_add_contact.py (+20/-32)
tests/autopilot/address_book_app/tests/test_edit_contact.py (+27/-14)
To merge this branch: bzr merge lp:~renatofilho/address-book-app/release-2014-06-18
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Ubuntu Phablet Team Pending
Review via email: mp+223639@code.launchpad.net

Commit message

Update contact view page visuals
Update contact edit page visuals
Add a hide animation for the bottom edge title.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
192. By Renato Araujo Oliveira Filho

To not keep track of contact in the ContactDetailPickerPhoneNumberDelegate.

This avoid crashes during the contact removal.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Renato Araujo Oliveira Filho (renatofilho) wrote :

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

Is your branch in sync with latest trunk? YES

Did you perform an exploratory manual test run of your code change and any related functionality on device or emulator? YES

Did you successfully run all tests found in your component's Test Plan on device or emulator? YES

If you changed the UI, was the change specified/approved by design? YES

If you changed the packaging (debian), did you subscribe a core-dev to this MP? No package change

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/app/imagescalethread.cpp'
2--- src/app/imagescalethread.cpp 2013-10-15 14:34:03 +0000
3+++ src/app/imagescalethread.cpp 2014-06-18 21:27:19 +0000
4@@ -57,7 +57,7 @@
5 }
6
7 // Create the temporary file
8- m_tmpFile = new QTemporaryFile();
9+ m_tmpFile = new QTemporaryFile("avatar_XXXXXX.png");
10 if (!m_tmpFile->open()) {
11 return;
12 }
13
14=== modified file 'src/imports/Common/ContactDetailBase.qml'
15--- src/imports/Common/ContactDetailBase.qml 2014-05-08 18:10:28 +0000
16+++ src/imports/Common/ContactDetailBase.qml 2014-06-18 21:27:19 +0000
17@@ -93,6 +93,7 @@
18 }
19
20 highlightWhenPressed: false
21+ showDivider: false
22
23 Rectangle {
24 anchors.fill: parent
25
26=== modified file 'src/imports/Common/ContactDetailItem.qml'
27--- src/imports/Common/ContactDetailItem.qml 2014-05-16 00:06:46 +0000
28+++ src/imports/Common/ContactDetailItem.qml 2014-06-18 21:27:19 +0000
29@@ -23,6 +23,7 @@
30
31 readonly property alias fieldDelegates: fieldsColumn.children
32 property Component fieldDelegate: null
33+ property alias spacing: fieldsColumn.spacing
34
35 implicitHeight: fieldsColumn.height
36 Column {
37
38=== added file 'src/imports/ContactEdit/AddFieldDialog.qml'
39--- src/imports/ContactEdit/AddFieldDialog.qml 1970-01-01 00:00:00 +0000
40+++ src/imports/ContactEdit/AddFieldDialog.qml 2014-06-18 21:27:19 +0000
41@@ -0,0 +1,142 @@
42+/*
43+ * Copyright (C) 2012-2014 Canonical, Ltd.
44+ *
45+ * This program is free software; you can redistribute it and/or modify
46+ * it under the terms of the GNU General Public License as published by
47+ * the Free Software Foundation; version 3.
48+ *
49+ * This program is distributed in the hope that it will be useful,
50+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
51+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
52+ * GNU General Public License for more details.
53+ *
54+ * You should have received a copy of the GNU General Public License
55+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
56+ */
57+
58+import QtQuick 2.2
59+import Ubuntu.Components 0.1
60+import QtContacts 5.0
61+import Ubuntu.Components.Popups 0.1 as Popups
62+
63+
64+Item {
65+ id: root
66+
67+ property QtObject contact: null
68+ property var currentDialog: null
69+ readonly property var validDetails: [ ContactDetail.PhoneNumber,
70+ ContactDetail.Email,
71+ ContactDetail.Address,
72+ ContactDetail.OnlineAccount,
73+ ContactDetail.Organization
74+ // TODO: Not supported yet
75+ // ContactDetail.Birthday,
76+ // ContactDetail.Note,
77+ // ContactDetail.Url
78+ ]
79+ readonly property var singleValueDetails: [ ContactDetail.Organization ]
80+ signal fieldSelected(string fieldName, string qmlTypeName)
81+
82+ function showOptions()
83+ {
84+ if (currentDialog == null) {
85+ // make sure the OSK disappear
86+ root.forceActiveFocus()
87+ currentDialog = PopupUtils.open(addFieldDialog, null)
88+ }
89+ }
90+
91+ function nameFromEnum(value)
92+ {
93+ switch (value)
94+ {
95+ case ContactDetail.PhoneNumber:
96+ return i18n.tr("Phone")
97+ case ContactDetail.Email:
98+ return i18n.tr("Email")
99+ case ContactDetail.Address:
100+ return i18n.tr("Address")
101+ case ContactDetail.OnlineAccount:
102+ return i18n.tr("Social")
103+ case ContactDetail.Organization:
104+ return i18n.tr("Professional Details")
105+ default:
106+ console.error("Invalid contact detail enum value:" + value)
107+ return ""
108+ }
109+ }
110+
111+ function qmlTypeFromEnum(value)
112+ {
113+ switch (value)
114+ {
115+ case ContactDetail.PhoneNumber:
116+ return "PhoneNumber"
117+ case ContactDetail.Email:
118+ return "EmailAddress"
119+ case ContactDetail.Address:
120+ return "Address"
121+ case ContactDetail.OnlineAccount:
122+ return "OnlineAccount"
123+ case ContactDetail.Organization:
124+ return "Organization"
125+ default:
126+ console.error("Invalid contact detail enum value:" + value)
127+ return ""
128+ }
129+ }
130+
131+ // check which details will be allowed to create
132+ // some details we only support one value
133+ function filterSingleDetails(details, contact)
134+ {
135+ var result = []
136+ for(var i=0; i < details.length; i++) {
137+ var det = details[i]
138+ if (singleValueDetails.indexOf(det) != -1) {
139+ if (contact.details(det).length === 0) {
140+ result.push(det)
141+ }
142+ } else {
143+ result.push(det)
144+ }
145+ }
146+ return result
147+ }
148+
149+ visible: false
150+ Component {
151+ id: addFieldDialog
152+
153+ Popups.Dialog {
154+ id: dialogue
155+ objectName: "addFieldDialog"
156+
157+ title: i18n.tr("Select a field")
158+ Repeater {
159+ model: root.filterSingleDetails(validDetails, root.contact)
160+ Button {
161+ objectName: text
162+
163+ text: root.nameFromEnum(modelData)
164+ onClicked: {
165+ root.fieldSelected(text, root.qmlTypeFromEnum(modelData))
166+ PopupUtils.close(root.currentDialog)
167+ root.currentDialog = null
168+ }
169+ }
170+ }
171+ Button {
172+ objectName: "cancel"
173+
174+ text: i18n.tr("Cancel")
175+ gradient: UbuntuColors.greyGradient
176+ onClicked: {
177+ PopupUtils.close(root.currentDialog)
178+ root.currentDialog = null
179+ }
180+ }
181+ }
182+ }
183+}
184
185=== added file 'src/imports/ContactEdit/AvatarImport.qml'
186--- src/imports/ContactEdit/AvatarImport.qml 1970-01-01 00:00:00 +0000
187+++ src/imports/ContactEdit/AvatarImport.qml 2014-06-18 21:27:19 +0000
188@@ -0,0 +1,78 @@
189+/*
190+ * Copyright (C) 2012-2014 Canonical, Ltd.
191+ *
192+ * This program is free software; you can redistribute it and/or modify
193+ * it under the terms of the GNU General Public License as published by
194+ * the Free Software Foundation; version 3.
195+ *
196+ * This program is distributed in the hope that it will be useful,
197+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
198+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
199+ * GNU General Public License for more details.
200+ *
201+ * You should have received a copy of the GNU General Public License
202+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
203+ */
204+
205+import QtQuick 2.2
206+import Ubuntu.Components 0.1
207+import Ubuntu.Components.Popups 0.1 as Popups
208+import Ubuntu.Content 0.1 as ContentHub
209+
210+Item {
211+ id: root
212+
213+ property var activeTransfer: null
214+ property var loadingDialog: null
215+
216+ signal avatarReceived(string avatarUrl)
217+
218+ function requestNewAvatar()
219+ {
220+ if (!root.loadingDialog) {
221+ root.loadingDialog = PopupUtils.open(loadingDialog, null)
222+ root.activeTransfer = defaultSource.request();
223+ }
224+ }
225+
226+ ContentHub.ContentPeer {
227+ id: defaultSource
228+
229+ contentType: ContentHub.ContentType.Pictures
230+ handler: ContentHub.ContentHandler.Source
231+ selectionType: ContentHub.ContentTransfer.Single
232+ }
233+
234+ Connections {
235+ target: root.activeTransfer
236+ onStateChanged: {
237+ var done = ((root.activeTransfer.state === ContentHub.ContentTransfer.Charged) ||
238+ (root.activeTransfer.state === ContentHub.ContentTransfer.Aborted));
239+
240+ if (root.activeTransfer.state === ContentHub.ContentTransfer.Charged) {
241+ if (root.activeTransfer.items.length > 0) {
242+ root.avatarReceived(root.activeTransfer.items[0].url)
243+ }
244+ }
245+
246+ if (done) {
247+ PopupUtils.close(root.loadingDialog)
248+ root.loadingDialog = null
249+ }
250+ }
251+ }
252+
253+ Component {
254+ id: loadingDialog
255+
256+ Popups.Dialog {
257+ id: dialogue
258+
259+ title: i18n.tr("Loading")
260+ ActivityIndicator {
261+ running: true
262+ visible: running
263+ }
264+ }
265+ }
266+}
267
268=== modified file 'src/imports/ContactEdit/CMakeLists.txt'
269--- src/imports/ContactEdit/CMakeLists.txt 2014-06-09 22:45:19 +0000
270+++ src/imports/ContactEdit/CMakeLists.txt 2014-06-18 21:27:19 +0000
271@@ -1,4 +1,6 @@
272 set(CONTACT_EDIT_QMLS
273+ AvatarImport.qml
274+ AddFieldDialog.qml
275 ContactDetailAddressesEditor.qml
276 ContactDetailAvatarEditor.qml
277 ContactDetailEmailsEditor.qml
278
279=== modified file 'src/imports/ContactEdit/ContactDetailAvatarEditor.qml'
280--- src/imports/ContactEdit/ContactDetailAvatarEditor.qml 2014-05-06 13:18:07 +0000
281+++ src/imports/ContactEdit/ContactDetailAvatarEditor.qml 2014-06-18 21:27:19 +0000
282@@ -17,15 +17,13 @@
283 import QtQuick 2.2
284 import Ubuntu.Components 0.1
285 import QtContacts 5.0
286-import Ubuntu.Content 0.1
287-import Ubuntu.Components.Popups 0.1 as Popups
288
289 import "../Common"
290
291 ContactDetailBase {
292 id: root
293
294- readonly property string defaultAvatar: Qt.resolvedUrl("../../artwork/contact-default-profile.png")
295+ readonly property string defaultAvatar: "image://theme/contact"
296
297 function isEmpty() {
298 return false;
299@@ -62,98 +60,51 @@
300 }
301
302 detail: contact ? contact.detail(ContactDetail.Avatar) : null
303- implicitHeight: units.gu(17)
304-
305- Image {
306- id: avatarImage
307-
308- anchors.fill: parent
309- source: root.getAvatar(root.detail)
310- asynchronous: true
311- fillMode: Image.PreserveAspectCrop
312- // When updating the avatar using the content picker the temporary file returned
313- // can contain the same name as the previous one and if the cache is enabled this
314- // will cause the image to not be updated
315- cache: false
316-
317- Component {
318- id: loadingDialog
319-
320- Popups.Dialog {
321- id: dialogue
322-
323- title: i18n.tr("Loading")
324-
325- ActivityIndicator {
326- id: activity
327-
328- anchors.centerIn: parent
329- running: true
330- visible: running
331- }
332- }
333- }
334-
335- Icon {
336- anchors {
337- right: parent.right
338- rightMargin: units.gu(1.5)
339- bottom: parent.bottom
340- bottomMargin: units.gu(2)
341- }
342- width: units.gu(3)
343- height: width
344- name: "import-image"
345- color: "white"
346- }
347-
348- ContentPeer {
349- id: defaultSource
350- contentType: ContentType.Pictures
351- handler: ContentHandler.Source
352- selectionType: ContentTransfer.Single
353- }
354-
355- MouseArea {
356- id: changeButton
357-
358- property var activeTransfer
359- property var loadingDialog: null
360-
361- anchors.fill: parent
362- onClicked: {
363- // make sure the OSK disappear
364- root.forceActiveFocus()
365- if (!changeButton.loadingDialog) {
366- changeButton.loadingDialog = PopupUtils.open(loadingDialog, null)
367- changeButton.activeTransfer = defaultSource.request();
368- }
369- }
370-
371- Connections {
372- target: changeButton.activeTransfer != null ? changeButton.activeTransfer : null
373- onStateChanged: {
374- var done = ((changeButton.activeTransfer.state === ContentTransfer.Charged) ||
375- (changeButton.activeTransfer.state === ContentTransfer.Aborted));
376-
377- if (changeButton.activeTransfer.state === ContentTransfer.Charged) {
378- if (changeButton.activeTransfer.items.length > 0) {
379- // remove the previous image, this is nessary to make sure that the new image
380- // get updated otherwise if the new image has the same name the image will not
381- // be updated
382- avatarImage.source = ""
383- // Update with the new valu
384- avatarImage.source = application.copyImage(root.contact, changeButton.activeTransfer.items[0].url);
385- }
386- }
387-
388- if (done) {
389- PopupUtils.close(changeButton.loadingDialog)
390- changeButton.loadingDialog = null
391- }
392- }
393- }
394+ implicitHeight: units.gu(8)
395+ implicitWidth: units.gu(8)
396+
397+ UbuntuShape {
398+ id: avatar
399+
400+ radius: "medium"
401+ anchors.fill: parent
402+ image: Image {
403+ id: avatarImage
404+
405+ fillMode: Image.PreserveAspectCrop
406+ asynchronous: true
407+ source: root.getAvatar(root.detail)
408+ height: units.gu(8)
409+ width: units.gu(8)
410+
411+ // When updating the avatar using the content picker the temporary file returned
412+ // can contain the same name as the previous one and if the cache is enabled this
413+ // will cause the image to not be updated
414+ cache: false
415+ }
416+ }
417+
418+ AvatarImport {
419+ id: avatarImport
420+
421+ onAvatarReceived: {
422+ // remove the previous image, this is nessary to make sure that the new image
423+ // get updated otherwise if the new image has the same name the image will not
424+ // be updated
425+ avatarImage.source = ""
426+ // Update with the new value
427+ avatarImage.source = application.copyImage(root.contact, avatarUrl);
428+ }
429+ }
430+
431+ MouseArea {
432+ anchors.fill: parent
433+ onClicked: {
434+ // make sure the OSK disappear
435+ root.forceActiveFocus()
436+ avatarImport.requestNewAvatar()
437 }
438 }
439 }
440
441+
442
443=== modified file 'src/imports/ContactEdit/ContactDetailGroupWithTypeEditor.qml'
444--- src/imports/ContactEdit/ContactDetailGroupWithTypeEditor.qml 2014-05-22 06:56:03 +0000
445+++ src/imports/ContactEdit/ContactDetailGroupWithTypeEditor.qml 2014-06-18 21:27:19 +0000
446@@ -95,7 +95,7 @@
447
448 return changed
449 }
450- minimumHeight: units.gu(5)
451+
452 headerDelegate: ListItem.Empty {
453 id: header
454 highlightWhenPressed: false
455@@ -104,6 +104,9 @@
456 height: units.gu(5)
457 // disable listview mouse area
458 __mouseArea.visible: false
459+ divider.anchors.leftMargin: units.gu(2)
460+ divider.anchors.rightMargin: units.gu(2)
461+
462 Label {
463 anchors {
464 verticalCenter: parent.verticalCenter
465@@ -117,35 +120,6 @@
466 // style
467 fontSize: "medium"
468 }
469-
470- Icon {
471- objectName: "newDetailButton"
472-
473- anchors {
474- verticalCenter: parent.verticalCenter
475- right: parent.right
476- rightMargin: units.gu(2)
477- }
478- width: units.gu(2)
479- height: units.gu(2)
480- name: "add"
481- }
482-
483- // Mouse area fill all title area to avoid problems with swipe from the right gesture
484- MouseArea {
485- anchors.fill: parent
486- onClicked: {
487- if (detailQmlTypeName) {
488- var newDetail = Qt.createQmlObject("import QtContacts 5.0; " + detailQmlTypeName + "{}", root)
489- if (newDetail) {
490- var newDetailsCopy = root.newDetails
491- newDetailsCopy.push(newDetail)
492- root.newDetails = newDetailsCopy
493- root.contact.addDetail(newDetail)
494- }
495- }
496- }
497- }
498 }
499
500 detailDelegate: ContactDetailWithTypeEditor {
501
502=== modified file 'src/imports/ContactEdit/ContactDetailNameEditor.qml'
503--- src/imports/ContactEdit/ContactDetailNameEditor.qml 2014-05-09 20:00:09 +0000
504+++ src/imports/ContactEdit/ContactDetailNameEditor.qml 2014-06-18 21:27:19 +0000
505@@ -51,6 +51,7 @@
506 return changed
507 }
508
509+ spacing: units.gu(1)
510 detail: root.contact ? root.contact.name : null
511 fields: [ QtContacts.Name.FirstName, QtContacts.Name.LastName ]
512
513
514=== modified file 'src/imports/ContactEdit/ContactDetailSyncTargetEditor.qml'
515--- src/imports/ContactEdit/ContactDetailSyncTargetEditor.qml 2014-05-28 15:22:38 +0000
516+++ src/imports/ContactEdit/ContactDetailSyncTargetEditor.qml 2014-06-18 21:27:19 +0000
517@@ -84,6 +84,12 @@
518 height: units.gu(4)
519 }
520
521+ ListItem.ThinDivider {
522+ id: divider
523+
524+ anchors.top: label.bottom
525+ }
526+
527 OptionSelector {
528 id: sources
529
530@@ -91,8 +97,8 @@
531 anchors {
532 left: parent.left
533 leftMargin: units.gu(2)
534- top: label.bottom
535- topMargin: units.gu(1)
536+ top: divider.bottom
537+ topMargin: units.gu(2)
538 right: parent.right
539 rightMargin: units.gu(2)
540 bottom: parent.bottom
541
542=== modified file 'src/imports/ContactEdit/ContactDetailWithTypeEditor.qml'
543--- src/imports/ContactEdit/ContactDetailWithTypeEditor.qml 2014-05-16 01:59:36 +0000
544+++ src/imports/ContactEdit/ContactDetailWithTypeEditor.qml 2014-06-18 21:27:19 +0000
545@@ -73,41 +73,19 @@
546 enabled: root.detail ? !root.detail.readOnly : false
547 implicitHeight: detailTypeSelector.height + fieldValues.height + units.gu(2)
548 opacity: enabled ? 1.0 : 0.5
549- ValueSelector {
550- id: detailTypeSelector
551- objectName: detail ? "type_" + detailToString(detail.type, -1) + "_" + index : ""
552-
553- readOnly: root.detail ? root.detail.readOnly : false
554- visible: (currentIndex != -1)
555- active: root.active
556+
557+ Column {
558+ id: fieldValues
559+
560 anchors {
561 left: parent.left
562- leftMargin: units.gu(3)
563+ leftMargin: units.gu(2)
564 right: parent.right
565 rightMargin: units.gu(2)
566 top: parent.top
567 topMargin: units.gu(1)
568 }
569-
570- height: visible ? (root.active ? units.gu(4) : units.gu(3)) : 0
571- onExpandedChanged: {
572- // Make sure that the inputfield get focus when clicking on type selector
573- if (expanded) {
574- root.forceActiveFocus()
575- }
576- }
577- }
578-
579- Column {
580- id: fieldValues
581-
582- anchors {
583- left: detailTypeSelector.left
584- right: detailTypeSelector.right
585- top: detailTypeSelector.bottom
586- }
587 height: childrenRect.height
588-
589 Repeater {
590 id: fieldRepeater
591
592@@ -141,4 +119,26 @@
593 }
594 }
595 }
596+
597+ ValueSelector {
598+ id: detailTypeSelector
599+ objectName: detail ? "type_" + detailToString(detail.type, -1) + "_" + index : ""
600+
601+ anchors {
602+ left: fieldValues.left
603+ right: fieldValues.right
604+ top: fieldValues.bottom
605+ }
606+
607+ readOnly: root.detail ? root.detail.readOnly : false
608+ visible: (currentIndex != -1)
609+ active: root.active
610+ height: visible ? (root.active ? units.gu(4) : units.gu(3)) : 0
611+ onExpandedChanged: {
612+ // Make sure that the inputfield get focus when clicking on type selector
613+ if (expanded) {
614+ root.forceActiveFocus()
615+ }
616+ }
617+ }
618 }
619
620=== modified file 'src/imports/ContactEdit/ContactEditor.qml'
621--- src/imports/ContactEdit/ContactEditor.qml 2014-06-16 12:59:15 +0000
622+++ src/imports/ContactEdit/ContactEditor.qml 2014-06-18 21:27:19 +0000
623@@ -161,7 +161,7 @@
624 fill: parent
625 bottomMargin: keyboard.height
626 }
627- contentHeight: contents.height
628+ contentHeight: contents.height + units.gu(2)
629 contentWidth: parent.width
630
631 //after add a new field we need to wait for the contentHeight to change to scroll to the correct position
632@@ -172,32 +172,48 @@
633
634 anchors {
635 top: parent.top
636+ topMargin: units.gu(2)
637 left: parent.left
638 right: parent.right
639 }
640 height: childrenRect.height
641
642- ContactDetailNameEditor {
643- id: nameEditor
644-
645-
646- anchors {
647- left: parent.left
648- right: parent.right
649- }
650- height: nameEditor.implicitHeight + units.gu(3)
651- contact: contactEditor.contact
652- }
653-
654- ContactDetailAvatarEditor {
655- id: avatarEditor
656-
657- contact: contactEditor.contact
658- anchors {
659- left: parent.left
660- right: parent.right
661- }
662- height: implicitHeight
663+ Row {
664+ function save()
665+ {
666+ var avatarSave = avatarEditor.save()
667+ var nameSave = nameEditor.save();
668+
669+ return (nameSave || avatarSave);
670+ }
671+
672+ function isEmpty()
673+ {
674+ return (avatarEditor.isEmpty() && nameEditor.isEmpty())
675+ }
676+
677+ anchors {
678+ left: parent.left
679+ leftMargin: units.gu(2)
680+ right: parent.right
681+ }
682+ height: Math.max(avatarEditor.height, nameEditor.height) - units.gu(4)
683+
684+ ContactDetailAvatarEditor {
685+ id: avatarEditor
686+
687+ contact: contactEditor.contact
688+ height: implicitHeight
689+ width: implicitWidth
690+ }
691+
692+ ContactDetailNameEditor {
693+ id: nameEditor
694+
695+ width: parent.width - avatarEditor.width
696+ height: nameEditor.implicitHeight + units.gu(3)
697+ contact: contactEditor.contact
698+ }
699 }
700
701 ContactDetailPhoneNumbersEditor {
702@@ -272,13 +288,48 @@
703 height: implicitHeight
704 }
705
706- // We need this extra element to correct align the deleteButton
707+ ListItem.ThinDivider {}
708+
709 Item {
710 anchors {
711 left: parent.left
712 right: parent.right
713 }
714- height: deleteButton.height + units.gu(2)
715+ height: units.gu(2)
716+ }
717+
718+ Row {
719+ anchors {
720+ left: parent.left
721+ right: parent.right
722+ margins: units.gu(2)
723+ }
724+ height: units.gu(6)
725+ spacing: units.gu(2)
726+
727+ // WORKAROUND: SDK uses a old version of qtquick components
728+ activeFocusOnTab: true
729+ onActiveFocusChanged: {
730+ if (activeFocus) {
731+ addNewFieldButton.forceActiveFocus()
732+ }
733+ }
734+
735+ Button {
736+ id: addNewFieldButton
737+ objectName: "addNewFieldButton"
738+
739+ text: i18n.tr("Add Field")
740+ gradient: UbuntuColors.greyGradient
741+ anchors {
742+ top: parent.top
743+ bottom: parent.bottom
744+ bottomMargin: units.gu(2)
745+ }
746+ width: (parent.width / 2) - units.gu(1)
747+
748+ onClicked: addFieldDialog.showOptions()
749+ }
750
751 Button {
752 id: deleteButton
753@@ -286,12 +337,11 @@
754 text: i18n.tr("Delete")
755 visible: !contactEditor.isNewContact
756 anchors {
757- margins: units.gu(2)
758 top: parent.top
759- left: parent.left
760- right: parent.right
761+ bottom: parent.bottom
762+ bottomMargin: units.gu(2)
763 }
764-
765+ width: (parent.width / 2) - units.gu(1)
766 onClicked: {
767 var dialog = Popups.PopupUtils.open(removeContactDialog, null)
768 dialog.contacts = [contactEditor.contact]
769@@ -351,6 +401,23 @@
770 }
771 }
772
773+ AddFieldDialog {
774+ id: addFieldDialog
775+
776+ contact: contactEditor.contact
777+ onFieldSelected: {
778+ if (qmlTypeName) {
779+ var newDetail = Qt.createQmlObject("import QtContacts 5.0; " + qmlTypeName + "{}", addFieldDialog)
780+ if (newDetail) {
781+ var newDetailsCopy = contactEditor.newDetails
782+ newDetailsCopy.push(newDetail)
783+ contactEditor.newDetails = newDetailsCopy
784+ contactEditor.contact.addDetail(newDetail)
785+ }
786+ }
787+ }
788+ }
789+
790 Component {
791 id: removeContactDialog
792
793
794=== modified file 'src/imports/ContactEdit/TextInputDetail.qml'
795--- src/imports/ContactEdit/TextInputDetail.qml 2014-05-28 15:19:53 +0000
796+++ src/imports/ContactEdit/TextInputDetail.qml 2014-06-18 21:27:19 +0000
797@@ -80,6 +80,7 @@
798 overlaySpacing: 0
799 frameSpacing: 0
800 background: Item {}
801+ color: UbuntuColors.lightAubergine
802 }
803 onActiveFocusChanged: {
804 if (activeFocus) {
805
806=== modified file 'src/imports/ContactEdit/ValueSelector.qml'
807--- src/imports/ContactEdit/ValueSelector.qml 2014-06-06 17:52:58 +0000
808+++ src/imports/ContactEdit/ValueSelector.qml 2014-06-18 21:27:19 +0000
809@@ -59,22 +59,12 @@
810
811 // FIXME: workaround to close list after a while.
812 // we cant rely on focus since it hides the keyboard.
813- MouseArea {
814- anchors.fill: parent
815- visible: expanded
816- onPressed: {
817- mouse.accepted = false
818- timer.restart()
819- }
820- propagateComposedEvents: true
821- z: 1
822- }
823-
824 Timer {
825 id: timer
826+
827 interval: 5000
828+ running: false
829 onTriggered: state = ""
830- running: false
831 }
832
833 Item {
834@@ -119,8 +109,12 @@
835
836 MouseArea {
837 anchors.fill: parent
838- propagateComposedEvents: true
839- onClicked: if (!readOnly) root.state = "expanded"
840+ onClicked: {
841+ if (!readOnly) {
842+ root.state = "expanded"
843+ timer.restart()
844+ }
845+ }
846 }
847
848 ListView {
849@@ -160,7 +154,10 @@
850 width: parent.width + units.gu(0.5)
851 height: parent.height + units.gu(0.5)
852 anchors.centerIn: parent
853- onClicked: currentIndex = index
854+ onClicked: {
855+ currentIndex = index
856+ timer.restart()
857+ }
858 }
859 }
860
861
862=== modified file 'src/imports/ContactList/ContactListPage.qml'
863--- src/imports/ContactList/ContactListPage.qml 2014-06-16 12:59:54 +0000
864+++ src/imports/ContactList/ContactListPage.qml 2014-06-18 21:27:19 +0000
865@@ -43,10 +43,7 @@
866 function createEmptyContact(phoneNumber) {
867 var details = [ {detail: "PhoneNumber", field: "number", value: phoneNumber},
868 {detail: "EmailAddress", field: "emailAddress", value: ""},
869- {detail: "OnlineAccount", field: "accountUri", value: ""},
870- {detail: "Address", field: "street", value: ""},
871- {detail: "Name", field: "firstName", value: ""},
872- {detail: "Organization", field: "name", value: ""}
873+ {detail: "Name", field: "firstName", value: ""}
874 ]
875
876 var newContact = Qt.createQmlObject("import QtContacts 5.0; Contact{ }", mainPage)
877@@ -385,7 +382,8 @@
878 {model: contactList.listModel,
879 contact: newContact,
880 active: false,
881- enabled: false})
882+ enabled: false,
883+ initialFocusSection: "name"})
884 }
885
886 Connections {
887
888=== modified file 'src/imports/ContactList/PageWithBottomEdge.qml'
889--- src/imports/ContactList/PageWithBottomEdge.qml 2014-06-16 13:00:42 +0000
890+++ src/imports/ContactList/PageWithBottomEdge.qml 2014-06-18 21:27:19 +0000
891@@ -142,6 +142,15 @@
892 z: 1
893 }
894
895+ Timer {
896+ id: hideIndicator
897+
898+ interval: 3000
899+ running: true
900+ repeat: false
901+ onTriggered: tipContainer.y = -units.gu(1)
902+ }
903+
904 Rectangle {
905 id: bottomEdge
906 objectName: "bottomEdge"
907@@ -184,6 +193,9 @@
908 clip: true
909 y: -bottomEdge.tipHeight
910 anchors.horizontalCenter: parent.horizontalCenter
911+ Behavior on y {
912+ UbuntuNumberAnimation {}
913+ }
914
915 UbuntuShape {
916 id: tip
917@@ -230,10 +242,7 @@
918 }
919 }
920
921- onPressed: {
922- bottomEdge.state = "floating"
923- bottomEdge.y -= bottomEdge.tipHeight
924- }
925+ onPressed: bottomEdge.state = "floating"
926 }
927
928 Behavior on y {
929@@ -252,6 +261,14 @@
930 target: tip
931 opacity: 1.0
932 }
933+ PropertyChanges {
934+ target: tipContainer
935+ y: -bottomEdge.tipHeight
936+ }
937+ PropertyChanges {
938+ target: hideIndicator
939+ running: true
940+ }
941 },
942 State {
943 name: "expanded"
944@@ -263,6 +280,14 @@
945 target: tip
946 opacity: 0.0
947 }
948+ PropertyChanges {
949+ target: tipContainer
950+ y: -bottomEdge.tipHeight
951+ }
952+ PropertyChanges {
953+ target: hideIndicator
954+ running: false
955+ }
956 },
957 State {
958 name: "floating"
959@@ -270,6 +295,14 @@
960 target: shadow
961 opacity: 1.0
962 }
963+ PropertyChanges {
964+ target: hideIndicator
965+ running: false
966+ }
967+ PropertyChanges {
968+ target: tipContainer
969+ y: -bottomEdge.tipHeight
970+ }
971 }
972 ]
973
974@@ -316,6 +349,8 @@
975
976 // load a new bottom page in memory
977 edgeLoader.active = true
978+
979+ hideIndicator.restart()
980 }
981 }
982 }
983
984=== modified file 'src/imports/ContactView/ActionButton.qml'
985--- src/imports/ContactView/ActionButton.qml 2014-06-06 17:52:58 +0000
986+++ src/imports/ContactView/ActionButton.qml 2014-06-18 21:27:19 +0000
987@@ -27,7 +27,7 @@
988 id: icon
989
990 anchors.centerIn: parent
991- height: units.gu(3)
992- width: height
993+ height: units.gu(2.5)
994+ width: units.gu(2.5)
995 }
996 }
997
998=== modified file 'src/imports/ContactView/BasicFieldView.qml'
999--- src/imports/ContactView/BasicFieldView.qml 2014-06-06 17:52:58 +0000
1000+++ src/imports/ContactView/BasicFieldView.qml 2014-06-18 21:27:19 +0000
1001@@ -24,56 +24,23 @@
1002
1003 property alias typeLabel: typeLabel.text
1004 property alias values: valueList.model
1005- property alias iconSource: actionIcon.source
1006- property double lineHeight: units.gu(3)
1007+ property double lineHeight: units.gu(2)
1008 property QtObject detail: null
1009 property variant fields: null
1010 property int parentIndex: -1
1011
1012- implicitHeight: typeLabel.height + (root.lineHeight * valueList.count) + units.gu(2)
1013+ implicitHeight: typeLabel.height + fieldValues.height + units.gu(2)
1014
1015- Image {
1016- id: actionIcon
1017+ Column {
1018+ id: fieldValues
1019
1020 anchors {
1021- verticalCenter: parent.verticalCenter
1022 left: parent.left
1023- }
1024- height: units.gu(2.5)
1025- width: visible ? units.gu(2.5) : 0
1026- visible: iconSource && iconSource != ""
1027- }
1028-
1029- Label {
1030- id: typeLabel
1031- objectName: detail ? "type_" + detailToString(detail.type, -1) + "_" + root.parentIndex : ""
1032-
1033- elide: Text.ElideRight
1034- visible: text != ""
1035- anchors {
1036- left: actionIcon.right
1037- leftMargin: actionIcon.visible ? units.gu(2) : 0
1038 top: parent.top
1039 topMargin: units.gu(1)
1040- right: root.right
1041- }
1042- height: visible ? units.gu(2) : 0
1043- verticalAlignment: Text.AlignVCenter
1044-
1045- // style
1046- fontSize: "small"
1047- opacity: 0.8
1048- }
1049-
1050- Column {
1051- id: fieldValues
1052-
1053- anchors {
1054- left: typeLabel.left
1055- top: typeLabel.bottom
1056 right: parent.right
1057- bottom: parent.bottom
1058 }
1059+ height: (valueList.count * root.lineHeight)
1060
1061 Repeater {
1062 id: valueList
1063@@ -83,8 +50,8 @@
1064 objectName: detail && fields ? "label_" + detailToString(detail.type, fields[index]) + "_" + root.parentIndex + "." + index : ""
1065
1066 anchors {
1067- left: parent ? parent.left : null
1068- right: parent ? parent.right : null
1069+ left: parent ? parent.left : undefined
1070+ right: parent ? parent.right : undefined
1071 }
1072 height: root.lineHeight
1073 verticalAlignment: Text.AlignVCenter
1074@@ -97,4 +64,24 @@
1075 }
1076 }
1077 }
1078+
1079+ Label {
1080+ id: typeLabel
1081+ objectName: detail ? "type_" + detailToString(detail.type, -1) + "_" + root.parentIndex : ""
1082+
1083+ elide: Text.ElideRight
1084+ visible: text != ""
1085+ anchors {
1086+ left: parent.left
1087+ top: fieldValues.bottom
1088+ //topMargin: units.gu(0.0)
1089+ right: parent.right
1090+ }
1091+ height: visible ? units.gu(2) : 0
1092+ verticalAlignment: Text.AlignVCenter
1093+
1094+ // style
1095+ fontSize: "small"
1096+ opacity: 0.8
1097+ }
1098 }
1099
1100=== modified file 'src/imports/ContactView/CMakeLists.txt'
1101--- src/imports/ContactView/CMakeLists.txt 2014-06-09 22:45:19 +0000
1102+++ src/imports/ContactView/CMakeLists.txt 2014-06-18 21:27:19 +0000
1103@@ -4,7 +4,6 @@
1104 ContactDetailAddressesView.qml
1105 ContactDetailAvatarView.qml
1106 ContactDetailEmailsView.qml
1107- ContactDetailFavoriteView.qml
1108 ContactDetailGroupWithTypeView.qml
1109 ContactDetailNameView.qml
1110 ContactDetailOnlineAccountsView.qml
1111
1112=== modified file 'src/imports/ContactView/ContactDetailAvatarView.qml'
1113--- src/imports/ContactView/ContactDetailAvatarView.qml 2014-05-07 18:13:12 +0000
1114+++ src/imports/ContactView/ContactDetailAvatarView.qml 2014-06-18 21:27:19 +0000
1115@@ -17,74 +17,28 @@
1116 import QtQuick 2.2
1117 import QtContacts 5.0
1118 import Ubuntu.Components 0.1
1119+import Ubuntu.Contacts 0.1 as ContactsUI
1120
1121 import "../Common"
1122
1123 ContactDetailBase {
1124 id: root
1125
1126- readonly property string defaultAvatar: Qt.resolvedUrl("../../artwork/contact-default-profile.png")
1127-
1128- function getAvatar(avatarDetail)
1129- {
1130- // use this verbose mode to avoid problems with binding loops
1131- var avatarUrl = defaultAvatar
1132- if (avatarDetail) {
1133- var avatarValue = avatarDetail.value(Avatar.ImageUrl)
1134- if (avatarValue != "") {
1135- avatarUrl = avatarValue
1136- }
1137- }
1138- return avatarUrl
1139- }
1140-
1141- detail: contact ? contact.detail(ContactDetail.Avatar) : null
1142- implicitHeight: units.gu(17)
1143-
1144- // update the contact detail in case of the contact change
1145+ implicitHeight: units.gu(8)
1146+ implicitWidth: units.gu(10)
1147+
1148 Connections {
1149- target: root.contact
1150- onContactChanged: {
1151- if (root.contact) {
1152- root.detail = contact.detail(ContactDetail.Avatar)
1153- } else {
1154- root.detail = null
1155- }
1156- }
1157- }
1158-
1159- onDetailChanged: {
1160- var newAvatar = root.getAvatar(root.detail)
1161- if (newAvatar !== defaultAvatar) {
1162- avatar.source = newAvatar
1163- } else {
1164- updateAvatar.restart()
1165- }
1166- }
1167-
1168- // Wait some milliseconds before update the avatar, in some cases the avatac get update later and this cause the image flick
1169- Timer {
1170- id: updateAvatar
1171-
1172- interval: 100
1173- running: false
1174- repeat: false
1175- onTriggered: {
1176- if (root.detail && contact) {
1177- avatar.source = root.getAvatar(root.detail)
1178- } else {
1179- avatar.source = root.defaultAvatar
1180- }
1181- }
1182- }
1183-
1184- Image {
1185+ target: root.contact.avatar
1186+ onDetailChanged: avatar.reload()
1187+ }
1188+
1189+ ContactsUI.ContactAvatar {
1190 id: avatar
1191
1192- anchors.fill: parent
1193- asynchronous: true
1194- smooth: true
1195- source: root.defaultAvatar
1196- fillMode: Image.PreserveAspectCrop
1197+ contactElement: root.contact
1198+ anchors {
1199+ fill: parent
1200+ leftMargin: units.gu(2)
1201+ }
1202 }
1203 }
1204
1205=== removed file 'src/imports/ContactView/ContactDetailFavoriteView.qml'
1206--- src/imports/ContactView/ContactDetailFavoriteView.qml 2014-05-06 13:18:07 +0000
1207+++ src/imports/ContactView/ContactDetailFavoriteView.qml 1970-01-01 00:00:00 +0000
1208@@ -1,43 +0,0 @@
1209-/*
1210- * Copyright (C) 2012-2013 Canonical, Ltd.
1211- *
1212- * This program is free software; you can redistribute it and/or modify
1213- * it under the terms of the GNU General Public License as published by
1214- * the Free Software Foundation; version 3.
1215- *
1216- * This program is distributed in the hope that it will be useful,
1217- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1218- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1219- * GNU General Public License for more details.
1220- *
1221- * You should have received a copy of the GNU General Public License
1222- * along with this program. If not, see <http://www.gnu.org/licenses/>.
1223- */
1224-
1225-import QtQuick 2.2
1226-import Ubuntu.Components 0.1
1227-import QtContacts 5.0
1228-
1229-import "../Common"
1230-
1231-ContactDetailBase {
1232- id: root
1233-
1234- detail: root.contact ? root.contact.favorite : null
1235- showDivider: false
1236-
1237- Icon {
1238- id: icon
1239-
1240- anchors.fill: parent
1241- name: root.detail && root.detail.favorite ? "favorite-selected" : "favorite-unselected"
1242- color: UbuntuColors.orange
1243- MouseArea {
1244- anchors.fill: parent
1245- onClicked: {
1246- root.detail.favorite = !root.detail.favorite
1247- root.contact.save()
1248- }
1249- }
1250- }
1251-}
1252
1253=== modified file 'src/imports/ContactView/ContactDetailGroupWithTypeView.qml'
1254--- src/imports/ContactView/ContactDetailGroupWithTypeView.qml 2014-06-06 17:52:58 +0000
1255+++ src/imports/ContactView/ContactDetailGroupWithTypeView.qml 2014-06-18 21:27:19 +0000
1256@@ -31,6 +31,8 @@
1257 headerDelegate: ListItem.Empty {
1258 highlightWhenPressed: false
1259
1260+ divider.anchors.leftMargin: units.gu(2)
1261+ divider.anchors.rightMargin: units.gu(2)
1262 width: root.width
1263 height: units.gu(5)
1264 Label {
1265@@ -45,7 +47,6 @@
1266
1267 // style
1268 fontSize: "medium"
1269- opacity: 0.8
1270 }
1271 }
1272
1273@@ -56,7 +57,6 @@
1274 contact: root.contact
1275 fields: root.fields
1276 typeLabel: detailType ? detailType.label : ""
1277- typeIcon: detailType && detailType.icon ? detailType.icon : ""
1278
1279 height: implicitHeight
1280 width: root.width
1281
1282=== modified file 'src/imports/ContactView/ContactDetailPhoneNumberView.qml'
1283--- src/imports/ContactView/ContactDetailPhoneNumberView.qml 2014-05-06 13:18:07 +0000
1284+++ src/imports/ContactView/ContactDetailPhoneNumberView.qml 2014-06-18 21:27:19 +0000
1285@@ -55,62 +55,40 @@
1286 anchors {
1287 left: parent.left
1288 top: parent.top
1289- right: div0.left
1290+ right: messageActions.left
1291 bottom: parent.bottom
1292 leftMargin: units.gu(2)
1293 }
1294- iconSource: root.action ? root.action.iconSource : ""
1295 }
1296
1297- Image {
1298- id: div0
1299+ ActionButton {
1300+ id: messageActions
1301
1302 anchors {
1303- top: parent.top
1304 right: callActions.left
1305- bottom: parent.bottom
1306+ rightMargin: units.gu(1)
1307+ verticalCenter: parent.verticalCenter
1308 }
1309- width: 2
1310- fillMode: Image.TileVertically
1311- source: "artwork:/vertical-div.png"
1312+ width: units.gu(4)
1313+ height: units.gu(4)
1314+ iconName: "messages"
1315+ onClicked: Qt.openUrlExternally("message:///" + encodeURIComponent(view.values[0]))
1316 }
1317
1318+
1319 ActionButton {
1320 id: callActions
1321
1322 anchors {
1323- right: div1.left
1324+ right: parent.right
1325+ rightMargin: units.gu(2)
1326 top: parent.top
1327- bottom: parent.bottom
1328+ verticalCenter: parent.verticalCenter
1329 }
1330- width: height
1331+ width: units.gu(4)
1332+ height: units.gu(4)
1333 iconName: "call-start"
1334 onClicked: Qt.openUrlExternally("tel:///" + encodeURIComponent(view.values[0]))
1335 }
1336
1337- Image {
1338- id: div1
1339-
1340- anchors {
1341- top: parent.top
1342- right: messageActions.left
1343- bottom: parent.bottom
1344- }
1345- width: 2
1346- fillMode: Image.TileVertically
1347- source: "artwork:/vertical-div.png"
1348- }
1349-
1350- ActionButton {
1351- id: messageActions
1352-
1353- anchors {
1354- right: parent.right
1355- top: parent.top
1356- bottom: parent.bottom
1357- }
1358- width: height
1359- iconName: "messages"
1360- onClicked: Qt.openUrlExternally("message:///" + encodeURIComponent(view.values[0]))
1361- }
1362 }
1363
1364=== modified file 'src/imports/ContactView/ContactDetailPhoneNumbersView.qml'
1365--- src/imports/ContactView/ContactDetailPhoneNumbersView.qml 2014-05-06 13:18:07 +0000
1366+++ src/imports/ContactView/ContactDetailPhoneNumbersView.qml 2014-06-18 21:27:19 +0000
1367@@ -29,11 +29,6 @@
1368
1369 title: i18n.tr("Phone")
1370 typeModel: ContactDetailPhoneNumberTypeModel { }
1371- defaultAction: Action {
1372- text: i18n.tr("Favorite")
1373- iconSource: "artwork:/contact-call.png"
1374- }
1375-
1376 detailDelegate: ContactDetailPhoneNumberView {
1377 property variant detailType: detail && root.contact && root.typeModelReady ? root.getType(detail) : null
1378
1379
1380=== modified file 'src/imports/ContactView/ContactDetailWithTypeView.qml'
1381--- src/imports/ContactView/ContactDetailWithTypeView.qml 2014-05-06 13:18:07 +0000
1382+++ src/imports/ContactView/ContactDetailWithTypeView.qml 2014-06-18 21:27:19 +0000
1383@@ -25,7 +25,6 @@
1384 id: root
1385
1386 property alias typeLabel: view.typeLabel
1387- property string typeIcon: null
1388 property alias lineHeight: view.lineHeight
1389 readonly property bool isReady: (fields != null) && (detail != null)
1390
1391@@ -61,6 +60,5 @@
1392 left: parent.left
1393 leftMargin: units.gu(2)
1394 }
1395- iconSource: typeIcon ? typeIcon : (root.action ? root.action.iconSource : "")
1396 }
1397 }
1398
1399=== modified file 'src/imports/ContactView/ContactView.qml'
1400--- src/imports/ContactView/ContactView.qml 2014-06-11 21:25:08 +0000
1401+++ src/imports/ContactView/ContactView.qml 2014-06-18 21:27:19 +0000
1402@@ -67,7 +67,7 @@
1403 anchors.fill: parent
1404 //WORKAROUND: There is a bug on SDK page that causes the page to appear flicked with small contents
1405 // see bug #1223050
1406- contentHeight: Math.max(contents.height, parent.height)
1407+ contentHeight: Math.max(contents.height, parent.height) + units.gu(2)
1408 contentWidth: parent.width
1409 visible: !busyIndicator.visible
1410
1411@@ -77,27 +77,16 @@
1412 height: childrenRect.height
1413 anchors {
1414 top: parent.top
1415+ topMargin: units.gu(2)
1416 left: parent.left
1417 right: parent.right
1418 }
1419
1420 ContactDetailAvatarView {
1421 contact: root.contact
1422- anchors {
1423- left: parent.left
1424- right: parent.right
1425- }
1426+ anchors.left: parent.left
1427 height: implicitHeight
1428- ContactDetailFavoriteView {
1429- contact: root.contact
1430- anchors {
1431- left: parent.left
1432- bottom: parent.bottom
1433- margins: units.gu(2)
1434- }
1435- width: units.gu(4)
1436- height: units.gu(4)
1437- }
1438+ width: implicitWidth
1439 }
1440
1441 ContactDetailPhoneNumbersView {
1442@@ -202,15 +191,28 @@
1443 tools: ToolbarItems {
1444 ToolbarButton {
1445 action: Action {
1446- objectName: "share"
1447- text: i18n.tr("Share")
1448- iconName: "share"
1449+ objectName: "favorite"
1450+ text: i18n.tr("Favorite")
1451+ iconName: root.contact && root.contact.favorite.favorite ? "favorite-selected" : "favorite-unselected"
1452 onTriggered: {
1453- pageStack.push(Qt.resolvedUrl("../ContactShare/ContactSharePage.qml"),
1454- { contactModel: root.model, contact: root.contact})
1455+ root.contact.favorite.favorite = !root.contact.favorite.favorite
1456+ root.contact.save()
1457 }
1458 }
1459 }
1460+ // FIXME: Having more than 3 options in the header causes a bug that make difficult to reach the component behind it.
1461+ // Enable it again when the bug #1329557 get fix
1462+// ToolbarButton {
1463+// action: Action {
1464+// objectName: "share"
1465+// text: i18n.tr("Share")
1466+// iconName: "share"
1467+// onTriggered: {
1468+// pageStack.push(Qt.resolvedUrl("../ContactShare/ContactSharePage.qml"),
1469+// { contactModel: root.model, contact: root.contact})
1470+// }
1471+// }
1472+// }
1473 ToolbarButton {
1474 action: Action {
1475 objectName: "edit"
1476
1477=== modified file 'src/imports/Ubuntu/Contacts/ContactAvatar.qml'
1478--- src/imports/Ubuntu/Contacts/ContactAvatar.qml 2014-06-06 17:52:58 +0000
1479+++ src/imports/Ubuntu/Contacts/ContactAvatar.qml 2014-06-18 21:27:19 +0000
1480@@ -24,7 +24,14 @@
1481
1482 property var contactElement: null
1483 property string displayName: ContactsJS.formatToDisplay(contactElement, ContactDetail.Name, [Name.FirstName, Name.LastName])
1484+ readonly property string defaultAvatar: "image://theme/contact"
1485 readonly property string avatarUrl: ContactsJS.getAvatar(contactElement, "")
1486+ readonly property bool useDefaultAvatar: (displayName === "" || contact.tag.tag === "") && (avatarUrl === "")
1487+
1488+ function reload()
1489+ {
1490+ img.source = ContactsJS.getAvatar(contactElement, "")
1491+ }
1492
1493 radius: "medium"
1494 color: Theme.palette.normal.overlay
1495@@ -34,13 +41,16 @@
1496 text: ContactsJS.getNameItials(displayName)
1497 font.pointSize: 88
1498 color: UbuntuColors.lightAubergine
1499- visible: avatarUrl === ""
1500+ visible: (img.status != Image.Ready)
1501 }
1502
1503 image: Image {
1504+ id: img
1505+
1506 fillMode: Image.PreserveAspectCrop
1507 asynchronous: true
1508- source: avatarUrl
1509- visible: source !== ""
1510+ source: avatar.useDefaultAvatar ? avatar.defaultAvatar : avatar.avatarUrl
1511+ height: avatar.height
1512+ width: avatar.width
1513 }
1514 }
1515
1516=== modified file 'src/imports/Ubuntu/Contacts/ContactDelegate.qml'
1517--- src/imports/Ubuntu/Contacts/ContactDelegate.qml 2014-06-06 22:40:23 +0000
1518+++ src/imports/Ubuntu/Contacts/ContactDelegate.qml 2014-06-18 21:27:19 +0000
1519@@ -154,8 +154,7 @@
1520
1521 onStatusChanged: {
1522 if (status == Loader.Ready) {
1523- pickerLoader.item.contactsModel = listModel
1524- pickerLoader.item.contactId = contact.contactId
1525+ pickerLoader.item.updateDetails(contact)
1526 pickerLoader.item.detailClicked.connect(root._onDetailClicked)
1527 }
1528 }
1529
1530=== modified file 'src/imports/Ubuntu/Contacts/ContactDetailPickerPhoneNumberDelegate.qml'
1531--- src/imports/Ubuntu/Contacts/ContactDetailPickerPhoneNumberDelegate.qml 2014-06-06 20:14:24 +0000
1532+++ src/imports/Ubuntu/Contacts/ContactDetailPickerPhoneNumberDelegate.qml 2014-06-18 21:27:19 +0000
1533@@ -21,9 +21,6 @@
1534 Item {
1535 id: root
1536
1537- property string contactId
1538- property alias contactsModel: contactFetch.model
1539-
1540 signal detailClicked(QtObject detail, string action)
1541
1542 function containsPointer(item, point)
1543@@ -32,9 +29,12 @@
1544 (point.y >= item.y) && (point.y <= item.y + item.height));
1545 }
1546
1547+ function updateDetails(contact)
1548+ {
1549+ phoneNumberEntries.model = contact.phoneNumbers
1550+ }
1551+
1552 height: detailItems.height + units.gu(2)
1553- onContactIdChanged: contactFetch.fetchContact(contactId)
1554-
1555 Column {
1556 id: detailItems
1557
1558@@ -48,15 +48,15 @@
1559
1560 ListItem.Standard {
1561 id: noNumberMessage
1562-
1563 showDivider: false
1564 text: "No phone numbers."
1565- visible: false
1566+ visible: phoneNumberEntries.count == 0
1567 }
1568
1569 Repeater {
1570 id: phoneNumberEntries
1571
1572+ model: contact.phoneNumbers
1573 ListItem.Subtitled {
1574 anchors {
1575 left: parent.left
1576@@ -119,14 +119,4 @@
1577 ContactDetailPhoneNumberTypeModel {
1578 id: phoneTypeModel
1579 }
1580-
1581- ContactFetch {
1582- id: contactFetch
1583-
1584- // do not keep track of contact to avoid problems during the delete destruction
1585- onContactFetched: {
1586- phoneNumberEntries.model = contact.phoneNumbers
1587- noNumberMessage.visible = (contact.phoneNumbers.length === 0)
1588- }
1589- }
1590 }
1591
1592=== modified file 'src/imports/Ubuntu/Contacts/ContactListView.qml'
1593--- src/imports/Ubuntu/Contacts/ContactListView.qml 2014-06-12 12:49:51 +0000
1594+++ src/imports/Ubuntu/Contacts/ContactListView.qml 2014-06-18 21:27:19 +0000
1595@@ -481,6 +481,11 @@
1596 //new contacts as soon as it arrives in the model
1597 if (contactsModel._clearModel && contacts.length === 0) {
1598 contactsModel._clearModel = false
1599+ // do a new update if autoUpdate is false
1600+ if (!contactsModel.autoUpdate) {
1601+ contactsModel.update()
1602+ }
1603+
1604 }
1605 }
1606 }
1607
1608=== modified file 'src/imports/Ubuntu/Contacts/ContactSimpleListView.qml'
1609--- src/imports/Ubuntu/Contacts/ContactSimpleListView.qml 2014-06-09 15:52:06 +0000
1610+++ src/imports/Ubuntu/Contacts/ContactSimpleListView.qml 2014-06-18 21:27:19 +0000
1611@@ -99,7 +99,9 @@
1612 */
1613 property var fetchHint : FetchHint {
1614 detailTypesHint: {
1615- var hints = [ ContactDetail.Tag, contactListView.titleDetail ]
1616+ var hints = [ ContactDetail.Tag, // sections
1617+ ContactDetail.PhoneNumber, // expansion
1618+ contactListView.titleDetail ]
1619
1620 if (contactListView.showAvatar) {
1621 hints.push(ContactDetail.Avatar)
1622
1623=== modified file 'src/imports/Ubuntu/Contacts/MultipleSelectionListView.qml'
1624--- src/imports/Ubuntu/Contacts/MultipleSelectionListView.qml 2014-06-06 17:52:58 +0000
1625+++ src/imports/Ubuntu/Contacts/MultipleSelectionListView.qml 2014-06-18 21:27:19 +0000
1626@@ -55,7 +55,7 @@
1627 \endqml
1628 */
1629
1630-UbuntuListView {
1631+ListView {
1632 id: listView
1633
1634 /*!
1635@@ -188,4 +188,12 @@
1636 MultipleSelectionVisualModel {
1637 id: visualModel
1638 }
1639+
1640+ Component.onCompleted: {
1641+ // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition
1642+ // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration
1643+ var scaleFactor = units.gridUnit / 8;
1644+ maximumFlickVelocity = maximumFlickVelocity * scaleFactor;
1645+ flickDeceleration = flickDeceleration * scaleFactor;
1646+ }
1647 }
1648
1649=== modified file 'tests/autopilot/address_book_app/pages/_contact_editor.py'
1650--- tests/autopilot/address_book_app/pages/_contact_editor.py 2014-05-22 12:50:28 +0000
1651+++ tests/autopilot/address_book_app/pages/_contact_editor.py 2014-06-18 21:27:19 +0000
1652@@ -51,6 +51,36 @@
1653 class ContactEditor(_common.PageWithHeader):
1654 """Custom proxy object for the Contact Editor."""
1655
1656+ _DETAIL_ALIAS = {
1657+ 'phones': 'Phone',
1658+ 'emails': 'Email',
1659+ 'ims': 'Social',
1660+ 'addresses': 'Address',
1661+ 'professionalDetails': 'Professional Details'
1662+ }
1663+
1664+ @autopilot.logging.log_action(logger.info)
1665+ def add_field(self, detail_name):
1666+ """Create a new field into the edit contact form.
1667+
1668+ :param detail_name: The detail field name
1669+
1670+ """
1671+
1672+ add_field_button = self.select_single(
1673+ 'Button', objectName='addNewFieldButton')
1674+ add_field_button.swipe_into_view()
1675+
1676+ self.pointing_device.click_object(add_field_button)
1677+
1678+ add_field_dialog = self.get_root_instance().wait_select_single(
1679+ 'Dialog', objectName='addFieldDialog')
1680+ new_field_button = add_field_dialog.select_single(
1681+ 'Button',
1682+ objectName=self._DETAIL_ALIAS[detail_name])
1683+
1684+ self.pointing_device.click_object(new_field_button)
1685+
1686 @autopilot.logging.log_action(logger.info)
1687 def fill_form(self, contact_information):
1688 """Fill the edit contact form.
1689@@ -90,7 +120,7 @@
1690 def _fill_detail_group(self, object_name, details):
1691 editor = self.select_single(
1692 ContactDetailGroupWithTypeEditor, objectName=object_name)
1693- editor.fill(details)
1694+ editor.fill(self, details)
1695
1696 def _get_form_values(self):
1697 first_name = _get_text_field(self, 'first_name').text
1698@@ -135,12 +165,12 @@
1699 'professionalDetails': 'base_unknown_{}'
1700 }
1701
1702- def fill(self, details):
1703+ def fill(self, editor, details):
1704 """Fill a contact detail group."""
1705- for index, detail in enumerate(details[:-1]):
1706+ for index, detail in enumerate(details):
1707+ if self.detailsCount <= index:
1708+ editor.add_field(self.objectName)
1709 self._fill_detail(index, detail)
1710- self._add_detail()
1711- self._fill_detail(len(details) - 1, details[-1])
1712
1713 def _fill_detail(self, index, detail):
1714 detail_editor = self._get_detail_editor_by_index(index)
1715@@ -175,15 +205,16 @@
1716 """Custom proxy object for the ContactDetailWithTypeEditor widget."""
1717
1718 def fill(self, field, index, detail):
1719+ self._fill_value(field, index, detail)
1720 self._select_type(detail)
1721- self._fill_value(field, index, detail)
1722
1723 def _select_type(self, detail):
1724 type_index = detail.TYPES.index(detail.type)
1725- selected_type_index = self._get_selected_type_index()
1726- if type_index != selected_type_index:
1727- # TODO --elopio - 2014-03-01
1728- raise NotImplementedError('Type selection not yet implemented.')
1729+ value_selector = self.select_single('ValueSelector')
1730+
1731+ while(value_selector.currentIndex != type_index):
1732+ ubuntuuitoolkit.get_keyboard().press_and_release("Shift+Right")
1733+ time.sleep(0.1)
1734
1735 def _get_selected_type_index(self):
1736 value_selector = self.select_single('ValueSelector')
1737@@ -210,16 +241,7 @@
1738 self._make_field_visible_and_write(text_field, value)
1739
1740 def _make_field_visible_and_write(self, text_field, value):
1741- while not text_field.activeFocus:
1742- # XXX We should just swipe the text field into view.
1743- # Update this once bug http://pad.lv/1286479 is implemented.
1744- # --elopio - 2014-03-01
1745- text_field.keyboard.press_and_release('Tab')
1746- time.sleep(0.1)
1747- contact_editor = self.get_root_instance().select_single(
1748- ContactEditor, objectName='contactEditorPage', active=True)
1749- contact_editor.wait_to_stop_moving()
1750-
1751+ text_field.swipe_into_view()
1752 text_field.write(value)
1753
1754 def _fill_address(self, index, address):
1755
1756=== modified file 'tests/autopilot/address_book_app/tests/__init__.py'
1757--- tests/autopilot/address_book_app/tests/__init__.py 2014-06-06 22:40:23 +0000
1758+++ tests/autopilot/address_book_app/tests/__init__.py 2014-06-18 21:27:19 +0000
1759@@ -143,6 +143,7 @@
1760 self.pointing_device.click_object(clear_button)
1761 self.assertThat(field.text, Eventually(Equals("")))
1762
1763+ # FIXME: Remove this function use ContactEditor.add_field
1764 def create_new_detail(self, detailGroup):
1765 detCount = detailGroup.detailsCount
1766 add_button = detailGroup.select_single("Icon",
1767@@ -155,17 +156,12 @@
1768 list_page = self.main_window.get_contact_list_page()
1769 list_page.open_contact(index)
1770
1771- list_page = self.main_window.get_contact_list_page()
1772 self.assertThat(list_page.visible, Eventually(Equals(False)))
1773-
1774 view_page = self.main_window.get_contact_view_page()
1775 self.assertThat(view_page.visible, Eventually(Equals(True)))
1776
1777 # Edit contact
1778- self.main_window.get_header().click_action_button("edit")
1779- self.assertThat(view_page.visible, Eventually(Equals(False)))
1780-
1781- edit_page = self.main_window.get_contact_edit_page()
1782+ edit_page = view_page.go_to_edit_contact()
1783 self.assertThat(edit_page.visible, Eventually(Equals(True)))
1784
1785 return edit_page
1786
1787=== modified file 'tests/autopilot/address_book_app/tests/test_add_contact.py'
1788--- tests/autopilot/address_book_app/tests/test_add_contact.py 2014-06-08 13:51:45 +0000
1789+++ tests/autopilot/address_book_app/tests/test_add_contact.py 2014-06-18 21:27:19 +0000
1790@@ -173,28 +173,23 @@
1791 self.assertThat(list_view.count, Eventually(Equals(1)))
1792
1793 def test_email_label_save(self):
1794- # execute add new contact
1795 contact_editor = self.app.main_window.go_to_add_contact()
1796
1797- # fill name
1798- contact_editor.fill_form(
1799- data.Contact(first_name='Sherlock', last_name='Holmes'))
1800+ my_emails = []
1801+ my_emails.append(data.Email(type_="Home", address="home@email.com"))
1802+ my_emails.append(data.Email(type_="Work", address="work@email.com"))
1803+ my_emails.append(data.Email(type_="Other", address="other@email.com"))
1804
1805- # Home
1806- self.set_email_address(0, "home@email.com", 0)
1807- # Work
1808- self.set_email_address(1, "work@email.com", 1)
1809- # Other
1810- self.set_email_address(2, "other@email.com", 2)
1811+ test_contact = data.Contact(first_name="Sherlock",
1812+ last_name="Holmes",
1813+ emails=my_emails)
1814+ contact_editor.fill_form(test_contact)
1815
1816 # Save contact
1817 self.app.main_window.save()
1818
1819 list_page = self.app.main_window.get_contact_list_page()
1820- list_page.open_contact(0)
1821-
1822- # check if contacts was saved with the correct labels
1823- view_page = self.app.main_window.get_contact_view_page()
1824+ view_page = list_page.open_contact(0)
1825 self.assertThat(view_page.visible, Eventually(Equals(True)))
1826
1827 # check if we have 3 emails"""
1828@@ -223,33 +218,26 @@
1829 self.assertThat(len(emails), Equals(0))
1830
1831 def test_phone_label_save(self):
1832- # execute add new contact
1833 contact_editor = self.app.main_window.go_to_add_contact()
1834
1835- # fill name
1836- contact_editor.fill_form(
1837- data.Contact(first_name='Sherlock', last_name='Holmes'))
1838+ my_phones = []
1839+ my_phones.append(data.Phone(type_="Home", number="(000) 000-0000"))
1840+ my_phones.append(data.Phone(type_="Work", number="(000) 000-0001"))
1841+ my_phones.append(data.Phone(type_="Mobile", number="(000) 000-0002"))
1842+ my_phones.append(data.Phone(type_="Work Mobile", number="(000) 000-0003"))
1843+ my_phones.append(data.Phone(type_="Other", number="(000) 000-0004"))
1844
1845- # Home
1846- self.set_phone_number(0, "(000) 000-0000", 0)
1847- # Work
1848- self.set_phone_number(1, "(000) 000-0001", 1)
1849- # Mobile
1850- self.set_phone_number(2, "(000) 000-0002", 2)
1851- # Work Mobile
1852- self.set_phone_number(3, "(000) 000-0003", 3)
1853- # Other
1854- self.set_phone_number(4, "(000) 000-0004", 4)
1855+ test_contact = data.Contact(first_name="Sherlock",
1856+ last_name="Holmes",
1857+ phones=my_phones)
1858+ contact_editor.fill_form(test_contact)
1859
1860 # Save contact
1861 self.app.main_window.save()
1862
1863 # Open contact view
1864 list_page = self.app.main_window.get_contact_list_page()
1865- list_page.open_contact(0)
1866-
1867- # check if contacts was saved with the correct labels
1868- view_page = self.app.main_window.get_contact_view_page()
1869+ view_page = list_page.open_contact(0)
1870 self.assertThat(view_page.visible, Eventually(Equals(True)))
1871
1872 # check if we have five phones"""
1873
1874=== modified file 'tests/autopilot/address_book_app/tests/test_edit_contact.py'
1875--- tests/autopilot/address_book_app/tests/test_edit_contact.py 2014-05-30 07:11:42 +0000
1876+++ tests/autopilot/address_book_app/tests/test_edit_contact.py 2014-06-18 21:27:19 +0000
1877@@ -37,10 +37,7 @@
1878 edit_page = self.edit_contact(0)
1879
1880 # Add a new phone
1881- phoneGroup = edit_page.select_single(
1882- "ContactDetailGroupWithTypeEditor",
1883- objectName="phones")
1884- self.create_new_detail(phoneGroup)
1885+ edit_page.add_field('phones')
1886
1887 # fill phone number
1888 phone_number_1 = self.app.main_window.select_single(
1889@@ -67,11 +64,18 @@
1890 self.assertThat(phone_label_1.text, Eventually(Equals(self.PHONE_NUMBERS[1])))
1891
1892 def test_remove_phone(self):
1893- self.add_contact("Fulano", "de Tal", self.PHONE_NUMBERS[1:3])
1894- edit_page = self.edit_contact(0)
1895+ contact_editor = self.app.main_window.go_to_add_contact()
1896+ my_phones = []
1897+ for n in self.PHONE_NUMBERS[1:3]:
1898+ my_phones.append(data.Phone(type_='Mobile', number=n))
1899+
1900+ test_contact = data.Contact(first_name="Fulano",
1901+ last_name="de Tal",
1902+ phones=my_phones)
1903+ contact_editor.fill_form(test_contact)
1904
1905 # clear phone 1
1906- phone_number_1 = edit_page.select_single(
1907+ phone_number_1 = contact_editor.wait_select_single(
1908 "TextInputDetail",
1909 objectName="phoneNumber_1")
1910 self.clear_text_on_field(phone_number_1)
1911@@ -79,8 +83,11 @@
1912 # Save contact
1913 self.app.main_window.save()
1914
1915+ # Go to contact view
1916+ list_page = self.main_window.get_contact_list_page()
1917+
1918 # check if we have onlye one phone
1919- view_page = self.app.main_window.get_contact_view_page()
1920+ view_page = list_page.open_contact(0)
1921 phone_group = view_page.select_single(
1922 "ContactDetailGroupWithTypeView",
1923 objectName="phones")
1924@@ -95,11 +102,7 @@
1925 def test_add_email(self):
1926 self.add_contact("Fulano", "")
1927 edit_page = self.edit_contact(0)
1928-
1929- emailGroup = edit_page.select_single(
1930- "ContactDetailGroupWithTypeEditor",
1931- objectName="emails")
1932- self.create_new_detail(emailGroup)
1933+ edit_page.add_field("emails")
1934
1935 # fill email address
1936 email_field = edit_page.select_single(
1937@@ -174,7 +177,17 @@
1938 self.assertThat(view_page.title, Eventually(Equals("Fulano de Tal")))
1939
1940 def test_im_type(self):
1941- self.add_contact("Fulano", "de Tal", im_address=["im@account.com"])
1942+ contact_editor = self.app.main_window.go_to_add_contact()
1943+ alias = data.SocialAlias(type_="Skype", alias="im@account.com")
1944+ test_contact = data.Contact(first_name="Fulano",
1945+ last_name="de Tal",
1946+ social_aliases=[alias])
1947+ contact_editor.fill_form(test_contact)
1948+
1949+ # Save contact
1950+ self.app.main_window.save()
1951+
1952+ # edit again
1953 edit_page = self.edit_contact(0)
1954
1955 # Change Im type

Subscribers

People subscribed via source and target branches