Merge lp:~renatofilho/address-book-app/choose-default-address-book into lp:address-book-app

Proposed by Renato Araujo Oliveira Filho
Status: Merged
Approved by: Bill Filler
Approved revision: 502
Merged at revision: 520
Proposed branch: lp:~renatofilho/address-book-app/choose-default-address-book
Merge into: lp:address-book-app
Diff against target: 529 lines (+330/-46)
8 files modified
src/app/addressbookapp.cpp (+1/-0)
src/app/addressbookapp.h (+1/-0)
src/imports/ABContactEditorPage.qml (+4/-0)
src/imports/Settings/CMakeLists.txt (+1/-0)
src/imports/Settings/SettingsDefaultSyncTarget.qml (+229/-0)
src/imports/Settings/SettingsPage.qml (+10/-0)
src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailSyncTargetEditor.qml (+81/-40)
tests/qml/tst_ContactEditor.qml (+3/-6)
To merge this branch: bzr merge lp:~renatofilho/address-book-app/choose-default-address-book
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
Bill Filler (community) Needs Fixing
Gustavo Pichorim Boiko (community) Approve
Review via email: mp+279135@code.launchpad.net

Commit message

Implemented the ability to choose the default address-book from settings page.

Description of the change

TESTING
========

 * Test change the default address-book:
  * Open address-book app
  * Enter on settings page
  * Select a different "Default address book"
  * Close settings page
  * Do a bottom edge swipe to create a new contact
  * Check if the selected address-book is the one that you choose on settings page.
  * Save the contact
  * Open the recent created contact and check if the address-book is the correct one

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
Gustavo Pichorim Boiko (boiko) wrote :

Just a couple remarks.

review: Needs Fixing
Revision history for this message
Renato Araujo Oliveira Filho (renatofilho) wrote :

All comments fixed on rev.497

Revision history for this message
Gustavo Pichorim Boiko (boiko) wrote :

Looks good now!

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Bill Filler (bfiller) wrote :

If I switch off contact syncing for any account in the list, it should be removed from the list. If the account was the default account, then Personal should be made the new default account.

review: Needs Fixing
Revision history for this message
Bill Filler (bfiller) wrote :

If I switch contact-sync back on for the account then it should be added back to the list, but not made the default

review: Needs Fixing
498. By Renato Araujo Oliveira Filho

Update source list on application active to keep it in sync.

499. By Renato Araujo Oliveira Filho

Make sure the correct source is selected after reload source list.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
500. By Renato Araujo Oliveira Filho

Trunk merged.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
501. By Renato Araujo Oliveira Filho

Created 'saveActionEnabled' property to avoid problems on tests.
Used that new property instead of search for Action.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
502. By Renato Araujo Oliveira Filho

Update source list if sources change while the settings and edit page are visible.

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/app/addressbookapp.cpp'
2--- src/app/addressbookapp.cpp 2015-09-29 19:03:44 +0000
3+++ src/app/addressbookapp.cpp 2015-12-11 16:43:55 +0000
4@@ -430,6 +430,7 @@
5 qWarning() << "Fail to connect with pim service.";
6 }
7 connect(m_server.data(), SIGNAL(safeModeChanged()), SIGNAL(serverSafeModeChanged()));
8+ connect(m_server.data(), SIGNAL(sourcesChanged()), SIGNAL(sourcesChanged()));
9 Q_EMIT serverSafeModeChanged();
10 }
11
12
13=== modified file 'src/app/addressbookapp.h'
14--- src/app/addressbookapp.h 2015-09-16 01:08:07 +0000
15+++ src/app/addressbookapp.h 2015-12-11 16:43:55 +0000
16@@ -51,6 +51,7 @@
17 void isOnlineChanged();
18 void serverSafeModeChanged();
19 void updatingChanged();
20+ void sourcesChanged();
21
22 public Q_SLOTS:
23 void activateWindow();
24
25=== modified file 'src/imports/ABContactEditorPage.qml'
26--- src/imports/ABContactEditorPage.qml 2015-10-26 13:18:11 +0000
27+++ src/imports/ABContactEditorPage.qml 2015-12-11 16:43:55 +0000
28@@ -27,6 +27,9 @@
29
30 property alias backIconName: backAction.iconName
31
32+ // Property used on unit tests
33+ readonly property alias saveActionEnabled: saveAction.enabled
34+
35 head.backAction: Action {
36 id: backAction
37
38@@ -42,6 +45,7 @@
39
40 head.actions: [
41 Action {
42+ id: saveAction
43 objectName: "save"
44 name: "save"
45
46
47=== modified file 'src/imports/Settings/CMakeLists.txt'
48--- src/imports/Settings/CMakeLists.txt 2015-03-17 17:14:09 +0000
49+++ src/imports/Settings/CMakeLists.txt 2015-12-11 16:43:55 +0000
50@@ -1,5 +1,6 @@
51 set(CONTACT_SETTINGS_QMLS
52 MyselfPhoneNumbersModel.qml
53+ SettingsDefaultSyncTarget.qml
54 SettingsPage.qml
55 )
56
57
58=== added file 'src/imports/Settings/SettingsDefaultSyncTarget.qml'
59--- src/imports/Settings/SettingsDefaultSyncTarget.qml 1970-01-01 00:00:00 +0000
60+++ src/imports/Settings/SettingsDefaultSyncTarget.qml 2015-12-11 16:43:55 +0000
61@@ -0,0 +1,229 @@
62+/*
63+* Copyright (C) 2015 Canonical, Ltd.
64+*
65+* This program is free software; you can redistribute it and/or modify
66+* it under the terms of the GNU General Public License as published by
67+* the Free Software Foundation; version 3.
68+*
69+* This program is distributed in the hope that it will be useful,
70+* but WITHOUT ANY WARRANTY; without even the implied warranty of
71+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
72+* GNU General Public License for more details.
73+*
74+* You should have received a copy of the GNU General Public License
75+* along with this program. If not, see <http://www.gnu.org/licenses/>.
76+*/
77+
78+import QtQuick 2.4
79+import QtContacts 5.0
80+
81+import Ubuntu.Components 1.3
82+import Ubuntu.Components.ListItems 1.3
83+
84+import Ubuntu.Contacts 0.1
85+import Ubuntu.AddressBook.Base 0.1
86+
87+Empty {
88+ id: root
89+
90+ signal changed()
91+
92+ function update()
93+ {
94+ sourceModel.update()
95+ }
96+
97+ function save()
98+ {
99+ var changed = false
100+ var selectedSource = getSelectedSource()
101+ if (!selectedSource) {
102+ return
103+ }
104+
105+ var details = selectedSource.details(ContactDetail.ExtendedDetail)
106+ for(var d in details) {
107+ if (details[d].name === "IS-PRIMARY") {
108+ if (!details[d].data) {
109+ details[d].data = true
110+ changed = true
111+ break
112+ }
113+ }
114+ }
115+ if (changed) {
116+ sourceModel.saveContact(selectedSource)
117+ }
118+ }
119+
120+ function getSelectedSource() {
121+ if (sources.model.count <= 0)
122+ return -1
123+
124+ return sources.model.get(sources.selectedIndex).source
125+ }
126+
127+ height: sources.currentlyExpanded ?
128+ sources.containerHeight + units.gu(6) + label.height :
129+ sources.itemHeight + units.gu(6) + label.height
130+
131+ ContactModel {
132+ id: sourceModel
133+
134+ manager: (typeof(QTCONTACTS_MANAGER_OVERRIDE) !== "undefined") && (QTCONTACTS_MANAGER_OVERRIDE != "") ?
135+ QTCONTACTS_MANAGER_OVERRIDE : "galera"
136+ filter: DetailFilter {
137+ detail: ContactDetail.Type
138+ field: Type.TypeField
139+ value: Type.Group
140+ matchFlags: DetailFilter.MatchExactly
141+ }
142+ autoUpdate: false
143+ onContactsChanged: {
144+ if (contacts.length > 0) {
145+ writableSources.reload()
146+ root.changed()
147+ }
148+ }
149+ }
150+
151+ ListModel {
152+ id: writableSources
153+
154+ function getSourceMetaData(contact) {
155+ var metaData = {'read-only' : false,
156+ 'account-provider': '',
157+ 'account-id': 0,
158+ 'is-primary': false}
159+
160+ var details = contact.details(ContactDetail.ExtendedDetail)
161+ for(var d in details) {
162+ if (details[d].name === "READ-ONLY") {
163+ metaData['read-only'] = details[d].data
164+ } else if (details[d].name === "PROVIDER") {
165+ metaData['account-provider'] = details[d].data
166+ } else if (details[d].name === "APPLICATION-ID") {
167+ metaData['account-id'] = details[d].data
168+ } else if (details[d].name === "IS-PRIMARY") {
169+ metaData['is-primary'] = details[d].data
170+ }
171+ }
172+ return metaData
173+ }
174+
175+ function reload() {
176+ sources._notify = false
177+
178+ clear()
179+ // filter out read-only sources
180+ var contacts = sourceModel.contacts
181+ if (contacts.length === 0) {
182+ return
183+ }
184+
185+ var data = []
186+ for(var i in contacts) {
187+ var sourceMetaData = getSourceMetaData(contacts[i])
188+ if (!sourceMetaData['readOnly']) {
189+ data.push({'sourceId': contacts[i].guid.guid,
190+ 'sourceName': contacts[i].displayLabel.label,
191+ 'accountId': sourceMetaData['account-id'],
192+ 'accountProvider': sourceMetaData['account-provider'],
193+ 'readOnly': sourceMetaData['read-only'],
194+ 'isPrimary': sourceMetaData['is-primary'],
195+ 'source': contacts[i]
196+ })
197+ }
198+ }
199+
200+ data.sort(function(a, b) {
201+ var valA = a.accountId
202+ var valB = b.accountId
203+ if (a.accountId == b.accountId) {
204+ valA = a.sourceName
205+ valB = b.sourceName
206+ }
207+
208+ if (valA == valB) {
209+ return 0
210+ } else if (valA < valB) {
211+ return -1
212+ } else {
213+ return 1
214+ }
215+ })
216+
217+ sources.selectedIndex = 0
218+ var primaryIndex = 0
219+ for (var i in data) {
220+ if (data[i].isPrimary) {
221+ primaryIndex = i
222+ }
223+ append(data[i])
224+ }
225+
226+ // select primary account
227+ sources.selectedIndex = primaryIndex
228+ sources._notify = true
229+ }
230+ }
231+
232+ Label {
233+ id: label
234+
235+ text: i18n.tr("Default Addressbook")
236+ anchors {
237+ left: parent.left
238+ top: parent.top
239+ right: parent.right
240+ margins: units.gu(2)
241+ }
242+ height: units.gu(4)
243+ }
244+
245+ OptionSelector {
246+ id: sources
247+ property bool _notify: true
248+
249+ model: writableSources
250+ anchors {
251+ left: parent.left
252+ leftMargin: units.gu(2)
253+ top: label.bottom
254+ right: parent.right
255+ rightMargin: units.gu(2)
256+ bottom: parent.bottom
257+ bottomMargin: units.gu(2)
258+ }
259+
260+ delegate: OptionSelectorDelegate {
261+ text: {
262+ if ((sourceId != "system-address-book") &&
263+ (iconSource == "image://theme/address-book-app-symbolic")) {
264+ return i18n.dtr("address-book-app", "Personal - %1").arg(sourceName)
265+ } else {
266+ return sourceName
267+ }
268+ }
269+ constrainImage: true
270+ iconSource: accountProvider == "" ?
271+ "image://theme/address-book-app-symbolic" :
272+ "image://theme/online-accounts-%1".arg(accountProvider)
273+ height: units.gu(4)
274+ }
275+
276+ containerHeight: sources.model && sources.model.count > 4 ? itemHeight * 4 : sources.model ? itemHeight * sources.model.count : 0
277+ onSelectedIndexChanged: {
278+ if (_notify && selectedIndex >= 0) {
279+ root.changed()
280+ }
281+ }
282+ }
283+
284+ // In case of sources changed we need to update the model
285+ Connections {
286+ target: application
287+ onSourcesChanged: root.update()
288+ }
289+}
290+
291
292=== modified file 'src/imports/Settings/SettingsPage.qml'
293--- src/imports/Settings/SettingsPage.qml 2015-12-02 13:36:03 +0000
294+++ src/imports/Settings/SettingsPage.qml 2015-12-11 16:43:55 +0000
295@@ -74,6 +74,10 @@
296 onClicked: pageStack.addPageToCurrentColumn(root, simCardImportPageComponent)
297 enabled: (simList.sims.length > 0) && (simList.present.length > 0)
298 }
299+ SettingsDefaultSyncTarget {
300+ id: defaultSyncTarget
301+ onChanged: save()
302+ }
303 }
304 }
305 ContactsUI.OnlineAccountsHelper {
306@@ -92,4 +96,10 @@
307 onImportCompleted: pageStack.removePages(root)
308 }
309 }
310+
311+ onActiveChanged: {
312+ if (active) {
313+ defaultSyncTarget.update()
314+ }
315+ }
316 }
317
318=== modified file 'src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailSyncTargetEditor.qml'
319--- src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailSyncTargetEditor.qml 2015-10-26 13:18:11 +0000
320+++ src/imports/Ubuntu/AddressBook/ContactEditor/ContactDetailSyncTargetEditor.qml 2015-12-11 16:43:55 +0000
321@@ -27,6 +27,10 @@
322 id: root
323
324 property alias active: sourceModel.autoUpdate
325+ property bool isNewContact: contact && contact.contactId === "qtcontacts:::"
326+ property real myHeight: label.height + units.gu(6) + (sources.currentlyExpanded ? sources.containerHeight :
327+ sources.itemHeight)
328+
329 signal changed()
330
331 function save() {
332@@ -49,9 +53,9 @@
333 if (sources.model.count <= 0)
334 return -1
335
336- var selectedContact = sources.model.get(sources.selectedIndex).contact
337- if (selectedContact) {
338- return selectedContact.guid.guid
339+ var selectedSourceId = sources.model.get(sources.selectedIndex).sourceId
340+ if (selectedSourceId) {
341+ return selectedSourceId
342 } else {
343 return -1
344 }
345@@ -69,23 +73,6 @@
346 return true
347 }
348
349- function targetIsReadOnly(target) {
350- if (!target)
351- return true
352-
353- var details = target.details(ContactDetail.ExtendedDetail)
354- for(var d in details) {
355- if ((details[d].name === "READ-ONLY") && (details[d].data === true)) {
356- return true
357- }
358- }
359-
360- return false
361- }
362-
363- property bool isNewContact: contact && contact.contactId === "qtcontacts:::"
364- property real myHeight: sources.currentlyExpanded ? sources.containerHeight + units.gu(6) + label.height : sources.itemHeight + units.gu(6) + label.height
365-
366 detail: root.contact ? contact.detail(ContactDetail.SyncTarget) : null
367 implicitHeight: root.isNewContact && sources.model && (sources.model.count > 1) ? myHeight : 0
368
369@@ -111,6 +98,27 @@
370 ListModel {
371 id: writableSources
372
373+ function getSourceMetaData(contact) {
374+ var metaData = {'read-only' : false,
375+ 'account-provider': '',
376+ 'account-id': 0,
377+ 'is-primary': false}
378+
379+ var details = contact.details(ContactDetail.ExtendedDetail)
380+ for(var d in details) {
381+ if (details[d].name === "READ-ONLY") {
382+ metaData['read-only'] = details[d].data
383+ } else if (details[d].name === "PROVIDER") {
384+ metaData['account-provider'] = details[d].data
385+ } else if (details[d].name === "APPLICATION-ID") {
386+ metaData['account-id'] = details[d].data
387+ } else if (details[d].name === "IS-PRIMARY") {
388+ metaData['is-primary'] = details[d].data
389+ }
390+ }
391+ return metaData
392+ }
393+
394 function reload() {
395 clear()
396
397@@ -120,11 +128,47 @@
398 return
399 }
400
401+ var data = []
402 for(var i in contacts) {
403- if (!targetIsReadOnly(contacts[i])) {
404- append({'contact': contacts[i]})
405- }
406- }
407+ var sourceMetaData = getSourceMetaData(contacts[i])
408+ if (!sourceMetaData['readOnly']) {
409+ data.push({'sourceId': contacts[i].guid.guid,
410+ 'sourceName': contacts[i].displayLabel.label,
411+ 'accountId': sourceMetaData['account-id'],
412+ 'accountProvider': sourceMetaData['account-provider'],
413+ 'readOnly': sourceMetaData['read-only'],
414+ 'isPrimary': sourceMetaData['is-primary']
415+ })
416+ }
417+ }
418+
419+ data.sort(function(a, b) {
420+ var valA = a.accountId
421+ var valB = b.accountId
422+ if (a.accountId == b.accountId) {
423+ valA = a.sourceName
424+ valB = b.sourceName
425+ }
426+
427+ if (valA == valB) {
428+ return 0
429+ } else if (valA < valB) {
430+ return -1
431+ } else {
432+ return 1
433+ }
434+ })
435+
436+ var primaryIndex = 0
437+ for (var i in data) {
438+ if (data[i].isPrimary) {
439+ primaryIndex = i
440+ }
441+ append(data[i])
442+ }
443+
444+ // select primary account
445+ sources.selectedIndex = primaryIndex
446 }
447 }
448
449@@ -164,27 +208,18 @@
450
451 delegate: OptionSelectorDelegate {
452 text: {
453- if ((contact.guid.guid != "system-address-book") &&
454+ if ((sourceId != "system-address-book") &&
455 (iconSource == "image://theme/address-book-app-symbolic")) {
456- return i18n.dtr("address-book-app", "Personal - %1").arg(contact.displayLabel.label)
457+ //TRANSLATORS: %1 is the display name of the source (address-book)
458+ return i18n.dtr("address-book-app", "Personal - %1").arg(sourceName)
459 } else {
460- return contact.displayLabel.label
461+ return sourceName
462 }
463 }
464 constrainImage: true
465- iconSource: {
466- var details = contact.details(ContactDetail.ExtendedDetail)
467- for(var i in details) {
468- if (details[i].name === "PROVIDER") {
469- if (details[i].data === "") {
470- return "image://theme/address-book-app-symbolic"
471- } else {
472- return "image://theme/online-accounts-%1".arg(details[i].data)
473- }
474- }
475- }
476- return "image://theme/address-book-app-symbolic"
477- }
478+ iconSource: accountProvider == "" ?
479+ "image://theme/address-book-app-symbolic" :
480+ "image://theme/online-accounts-%1".arg(accountProvider)
481 height: units.gu(4)
482 }
483
484@@ -196,5 +231,11 @@
485 sourceModel.update()
486 }
487 }
488+
489+ // In case of sources changed we need to update the model
490+ Connections {
491+ target: application
492+ onSourcesChanged: sourceModel.update()
493+ }
494 }
495
496
497=== modified file 'tests/qml/tst_ContactEditor.qml'
498--- tests/qml/tst_ContactEditor.qml 2015-12-08 07:47:18 +0000
499+++ tests/qml/tst_ContactEditor.qml 2015-12-11 16:43:55 +0000
500@@ -69,8 +69,7 @@
501
502 function init() {
503 waitForRendering(contactEditor);
504- var saveButton = findChild(root, 'save_button');
505- compare(saveButton.enabled, false);
506+ tryCompare(contactEditor, 'saveActionEnabled', false);
507 }
508
509 function cleanup() {
510@@ -113,8 +112,7 @@
511 function test_fillRequiredFieldsMustEnableSaveButton(data) {
512 var textField = findChild(root, data.objectName);
513 textField.text = 'test'
514- var saveButton = findChild(root, 'save_button');
515- tryCompare(saveButton, 'enabled', true);
516+ tryCompare(contactEditor, 'saveActionEnabled', true);
517 }
518
519 function test_fillOptionalFieldsMustNotEnableSaveButton_data() {
520@@ -125,8 +123,7 @@
521 function test_fillOptionalFieldsMustNotEnableSaveButton(data) {
522 var textField = findChild(root, data.objectName);
523 textField.text = 'test'
524- var saveButton = findChild(root, 'save_button');
525- tryCompare(saveButton, 'enabled', false);
526+ tryCompare(contactEditor, 'saveActionEnabled', false);
527 }
528
529 function test_enterKeyMoveFocusedItem() {

Subscribers

People subscribed via source and target branches