[Merge] lp:~renatofilho/address-book-app/sim-card-import into lp:address-book-app
Tiago Salem Herrmann
tiago.herrmann at canonical.com
Wed Mar 18 19:59:42 UTC 2015
Review: Needs Information
Diff comments:
> === modified file 'CMakeLists.txt'
> --- CMakeLists.txt 2014-11-10 14:37:25 +0000
> +++ CMakeLists.txt 2015-03-17 17:39:47 +0000
> @@ -18,6 +18,8 @@
> find_package(Qt5DBus)
> find_package(PkgConfig REQUIRED)
>
> +pkg_check_modules(QOfono qofono-qt5)
> +
> find_program(INTLTOOL_MERGE intltool-merge)
> find_program(INTLTOOL_EXTRACT intltool-extract)
> find_program(DBUS_RUNNER dbus-test-runner)
>
> === modified file 'debian/control'
> --- debian/control 2015-02-09 15:02:11 +0000
> +++ debian/control 2015-03-17 17:39:47 +0000
> @@ -3,6 +3,7 @@
> Priority: optional
> Maintainer: Ubuntu Developers <ubuntu-devel-discuss at lists.ubuntu.com>
> Build-Depends: cmake,
> + dbus,
> debhelper (>= 9),
> dh-translations,
> libgl1-mesa-dev | libgl-dev,
> @@ -11,6 +12,8 @@
> pkg-config,
> qml-module-qttest [i386 amd64 armhf],
> qtdeclarative5-dev-tools [i386 amd64 armhf],
> + qtdeclarative5-gsettings1.0 [i386 amd64 armhf],
> + qtdeclarative5-ofono0.2 [i386 amd64 armhf],
> qtdeclarative5-qtcontacts-plugin [i386 amd64 armhf],
> qtdeclarative5-ubuntu-content1 [i386 amd64 armhf],
> qtdeclarative5-ubuntu-history0.1 [i386 amd64 armhf],
> @@ -22,6 +25,8 @@
> qtbase5-dev,
> qtdeclarative5-dev,
> qtpim5-dev,
> + libqofono-qt5-0,
> + libqofono-dev,
> thumbnailer-service,
> xvfb [i386 amd64 armhf],
> Standards-Version: 3.9.5
> @@ -39,8 +44,10 @@
> ubuntu-ui-toolkit-theme (>= 0.1.49+14.10.20140707),
> qmlscene,
> qtcontact5-galera,
> + qtdeclarative5-gsettings1.0,
> qtdeclarative5-qtcontacts-plugin,
> qtdeclarative5-qtquick2-plugin,
> + qtdeclarative5-ofono0.2,
> qtdeclarative5-ubuntu-contacts0.1 (= ${binary:Version}),
> qtdeclarative5-ubuntu-content1,
> qtdeclarative5-ubuntu-history0.1,
> @@ -87,6 +94,7 @@
> libautopilot-qt,
> libqt5test5,
> libqt5widgets5,
> + ofono-phonesim-autostart,
> python-testscenarios,
> qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.49+14.10.20140707) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles,
> ubuntu-ui-toolkit-autopilot (>= 0.1.46+14.10.20140527),
>
> === modified file 'po/address-book-app.pot'
> --- po/address-book-app.pot 2015-02-23 08:34:21 +0000
> +++ po/address-book-app.pot 2015-03-17 17:39:47 +0000
> @@ -8,7 +8,7 @@
> msgstr ""
> "Project-Id-Version: address-book-app\n"
> "Report-Msgid-Bugs-To: \n"
> -"POT-Creation-Date: 2015-02-23 09:33+0100\n"
> +"POT-Creation-Date: 2015-03-16 17:52-0300\n"
> "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
> "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
> "Language-Team: LANGUAGE <LL at li.org>\n"
> @@ -17,6 +17,11 @@
> "Content-Type: text/plain; charset=CHARSET\n"
> "Content-Transfer-Encoding: 8bit\n"
>
> +#: ../src/imports/Ubuntu/Contacts/SIMCardImportPage.qml:68
> +#, qt-format
> +msgid "%1 is locked"
> +msgstr ""
> +
> #: ../src/imports/ContactList/VCardImportDialog.qml:72
> #, qt-format
> msgid "%1 vCards imported"
> @@ -31,7 +36,11 @@
> msgid "Add Field"
> msgstr ""
>
> -#: ../src/imports/ContactList/ContactListPage.qml:390
> +#: ../src/imports/Settings/SettingsPage.qml:63
> +msgid "Add Google account"
> +msgstr ""
> +
> +#: ../src/imports/ContactList/ContactListPage.qml:396
> msgid "Add contact"
> msgstr ""
>
> @@ -56,7 +65,7 @@
> msgstr ""
>
> #. TRANSLATORS: this refers to all contacts
> -#: ../src/imports/ContactList/ContactListPage.qml:277
> +#: ../src/imports/ContactList/ContactListPage.qml:283
> msgid "All"
> msgstr ""
>
> @@ -69,13 +78,13 @@
> msgstr ""
>
> #: ../src/imports/ContactEdit/ContactEditor.qml:398
> -#: ../src/imports/ContactList/ContactListPage.qml:290
> +#: ../src/imports/ContactList/ContactListPage.qml:296
> #: ../src/imports/ContactView/ContactFetchError.qml:29
> #: ../src/imports/MainWindow.qml:157
> msgid "Cancel"
> msgstr ""
>
> -#: ../src/imports/ContactList/ContactListPage.qml:314
> +#: ../src/imports/ContactList/ContactListPage.qml:320
> msgid "Cancel selection"
> msgstr ""
>
> @@ -104,13 +113,13 @@
> msgid "Country"
> msgstr ""
>
> -#: ../src/imports/ContactList/ContactListPage.qml:475
> +#: ../src/imports/ContactList/ContactListPage.qml:481
> msgid "Create a new contact by swiping up from the bottom of the screen."
> msgstr ""
>
> #: ../src/imports/ContactEdit/ContactEditor.qml:355
> #: ../src/imports/ContactList/ContactListPage.qml:168
> -#: ../src/imports/ContactList/ContactListPage.qml:350
> +#: ../src/imports/ContactList/ContactListPage.qml:356
> msgid "Delete"
> msgstr ""
>
> @@ -147,7 +156,11 @@
> msgid "Exporting contacts..."
> msgstr ""
>
> -#: ../src/imports/ContactList/ContactListPage.qml:277
> +#: ../src/imports/Ubuntu/Contacts/SIMCardImportPage.qml:222
> +msgid "Fail to read SIM card"
> +msgstr ""
> +
> +#: ../src/imports/ContactList/ContactListPage.qml:283
> msgid "Favorites"
> msgstr ""
>
> @@ -173,16 +186,28 @@
> msgid "IM"
> msgstr ""
>
> -#. TRANSLATORS: this refers to a new contact
> -#: ../src/imports/Ubuntu/Contacts/ContactListView.qml:434
> +#: ../src/imports/Ubuntu/Contacts/SIMCardImportPage.qml:179
> +msgid "Import"
> +msgstr ""
> +
> +#: ../src/imports/Ubuntu/Contacts/ContactListView.qml:462
> msgid "Import contacts from Google"
> msgstr ""
>
> +#: ../src/imports/Ubuntu/Contacts/ContactListView.qml:473
> +msgid "Import contacts from SIM card"
> +msgstr ""
> +
> +#: ../src/imports/Settings/SettingsPage.qml:68
> +msgid "Import from SIM"
> +msgstr ""
> +
> #: ../src/imports/ContactList/VCardImportDialog.qml:71
> msgid "Import vCards"
> msgstr ""
>
> #: ../src/imports/ContactList/VCardImportDialog.qml:72
> +#: ../src/imports/Ubuntu/Contacts/SIMCardImportPage.qml:214
> msgid "Importing..."
> msgstr ""
>
> @@ -194,7 +219,8 @@
> msgid "Last name"
> msgstr ""
>
> -#: ../src/imports/Ubuntu/Contacts/ContactListView.qml:505
> +#: ../src/imports/Ubuntu/Contacts/ContactListView.qml:536
> +#: ../src/imports/Ubuntu/Contacts/SIMCardImportPage.qml:206
> msgid "Loading..."
> msgstr ""
>
> @@ -215,6 +241,11 @@
> msgid "Multiple contacts"
> msgstr ""
>
> +#: ../src/imports/Settings/SettingsPage.qml:57
> +#, qt-format
> +msgid "My phone number: %1"
> +msgstr ""
> +
> #: ../src/imports/ContactEdit/ContactEditor.qml:142
> msgid "New contact"
> msgstr ""
> @@ -233,6 +264,10 @@
> msgid "No contact selected."
> msgstr ""
>
> +#: ../src/imports/Ubuntu/Contacts/SIMCardImportPage.qml:100
> +msgid "No contacts found"
> +msgstr ""
> +
> #: ../src/imports/Ubuntu/Contacts/ContactDelegate.qml:113
> #: ../src/imports/Ubuntu/Contacts/ContactPreviewPage.qml:31
> #: ../src/imports/Ubuntu/Contacts/ContactPreviewPage.qml:36
> @@ -277,11 +312,20 @@
> msgid "Role"
> msgstr ""
>
> +#: ../src/imports/Settings/SettingsPage.qml:58
> +#, qt-format
> +msgid "SIM %1"
> +msgstr ""
> +
> +#: ../src/imports/Ubuntu/Contacts/SIMCardImportPage.qml:37
> +msgid "SIM contacts"
> +msgstr ""
> +
> #: ../src/imports/ContactEdit/ContactEditor.qml:411
> msgid "Save"
> msgstr ""
>
> -#: ../src/imports/ContactList/ContactListPage.qml:262
> +#: ../src/imports/ContactList/ContactListPage.qml:255
> msgid "Search"
> msgstr ""
>
> @@ -289,11 +333,17 @@
> msgid "Search..."
> msgstr ""
>
> -#: ../src/imports/ContactList/ContactListPage.qml:320
> +#: ../src/imports/ContactList/ContactListPage.qml:326
> +#: ../src/imports/Ubuntu/Contacts/SIMCardImportPage.qml:167
> msgid "Select All"
> msgstr ""
>
> -#: ../src/imports/ContactList/ContactListPage.qml:333
> +#: ../src/imports/ContactList/ContactListPage.qml:272
> +#: ../src/imports/Settings/SettingsPage.qml:30
> +msgid "Settings"
> +msgstr ""
> +
> +#: ../src/imports/ContactList/ContactListPage.qml:339
> #: ../src/imports/ContactView/ContactView.qml:98
> msgid "Share"
> msgstr ""
> @@ -311,15 +361,15 @@
> msgid "Street"
> msgstr ""
>
> -#: ../src/imports/ContactList/ContactListPage.qml:256
> +#: ../src/imports/ContactList/ContactListPage.qml:266
> msgid "Sync"
> msgstr ""
>
> -#: ../src/imports/ContactList/ContactListPage.qml:256
> +#: ../src/imports/ContactList/ContactListPage.qml:266
> msgid "Syncing"
> msgstr ""
>
> -#: ../src/imports/Ubuntu/Contacts/ContactListView.qml:505
> +#: ../src/imports/Ubuntu/Contacts/ContactListView.qml:536
> msgid "Syncing..."
> msgstr ""
>
> @@ -331,7 +381,12 @@
> msgid "Touch"
> msgstr ""
>
> -#: ../src/imports/ContactList/ContactListPage.qml:320
> +#: ../src/imports/Ubuntu/Contacts/SIMCardImportPage.qml:70
> +msgid "Unlock"
> +msgstr ""
> +
> +#: ../src/imports/ContactList/ContactListPage.qml:326
> +#: ../src/imports/Ubuntu/Contacts/SIMCardImportPage.qml:166
> msgid "Unselect All"
> msgstr ""
>
> @@ -354,6 +409,6 @@
> msgid "Yes"
> msgstr ""
>
> -#: ../src/imports/ContactList/ContactListPage.qml:474
> +#: ../src/imports/ContactList/ContactListPage.qml:480
> msgid "You have no contacts."
> msgstr ""
>
> === modified file 'src/imports/CMakeLists.txt'
> --- src/imports/CMakeLists.txt 2014-07-28 23:12:24 +0000
> +++ src/imports/CMakeLists.txt 2015-03-17 17:39:47 +0000
> @@ -17,4 +17,5 @@
> add_subdirectory(ContactView)
> add_subdirectory(ContactEdit)
> add_subdirectory(ContactShare)
> +add_subdirectory(Settings)
> add_subdirectory(Ubuntu)
>
> === modified file 'src/imports/ContactList/ContactListPage.qml'
> --- src/imports/ContactList/ContactListPage.qml 2015-02-13 19:29:27 +0000
> +++ src/imports/ContactList/ContactListPage.qml 2015-03-17 17:39:47 +0000
> @@ -252,13 +252,6 @@
> }
> actions: [
> Action {
> - visible: contactList.syncEnabled
> - text: contactList.syncing ? i18n.tr("Syncing") : i18n.tr("Sync")
> - iconName: "reload"
> - enabled: !contactList.syncing
> - onTriggered: contactList.sync()
> - },
> - Action {
> text: i18n.tr("Search")
> iconName: "search"
> visible: !mainPage.isEmpty
> @@ -267,6 +260,19 @@
> contactList.showAllContacts()
> searchField.forceActiveFocus()
> }
> + },
> + Action {
> + visible: contactList.syncEnabled
> + text: contactList.syncing ? i18n.tr("Syncing") : i18n.tr("Sync")
> + iconName: "reload"
> + enabled: !contactList.syncing
> + onTriggered: contactList.sync()
> + },
> + Action {
> + text: i18n.tr("Settings")
> + iconName: "settings"
> + onTriggered: pageStack.push(Qt.resolvedUrl("../Settings/SettingsPage.qml"),
> + {"contactListModel": contactList.listModel})
> }
> ]
> PropertyChanges {
>
> === added directory 'src/imports/Settings'
> === added file 'src/imports/Settings/CMakeLists.txt'
> --- src/imports/Settings/CMakeLists.txt 1970-01-01 00:00:00 +0000
> +++ src/imports/Settings/CMakeLists.txt 2015-03-17 17:39:47 +0000
> @@ -0,0 +1,11 @@
> +set(CONTACT_SETTINGS_QMLS
> + MyselfPhoneNumbersModel.qml
> + SettingsPage.qml
> +)
> +
> +install(FILES ${CONTACT_SETTINGS_QMLS}
> + DESTINATION ${ADDRESS_BOOK_APP_DIR}/imports/Settings
> +)
> +
> +# make the files visible on qtcreator
> +add_custom_target(contact_settings_QmlFiles ALL SOURCES ${CONTACT_SETTINGS_QMLS})
>
> === added file 'src/imports/Settings/MyselfPhoneNumbersModel.qml'
> --- src/imports/Settings/MyselfPhoneNumbersModel.qml 1970-01-01 00:00:00 +0000
> +++ src/imports/Settings/MyselfPhoneNumbersModel.qml 2015-03-17 17:39:47 +0000
> @@ -0,0 +1,82 @@
> +/*
> + * Copyright (C) 2015 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.2
> +import MeeGo.QOfono 0.2
> +import Ubuntu.Telephony.PhoneNumber 0.1
> +
> +
> +ListModel {
> + id: root
> +
> + function reloadNumbers()
> + {
> + root.clear()
> +
> + for (var i = 0; i < simManagerList.count; i++) {
> + var item = simManagerList.itemAt(i)
> + if (item) {
> + var numbers = item.subscriberNumbers
> + for (var n in numbers) {
> + root.append({'phoneNumber': PhoneUtils.format(numbers[n]),
> + 'network': item.networkName })
As we discussed before, I believe we should try to use the SIM card name instead of the networkName. In scenarios where there is no network this field will be empty, but you can still import contacts. Also, there is a possibility that both sim cards are registered to the same network. The ideal solution would be using the labels stored by the system-settings. I know we don't have designs for this yet, so I would be ok if this can't be done now, but we just need to keep in mind that we will probably need to change this in the future.
> + }
> + }
> + }
> + }
> +
> + property var priv: Item {
> + OfonoManager {
> + id: ofonoManager
> + }
> +
> + Repeater {
> + id: simManagerList
> +
> + model: ofonoManager.modems
> + delegate: Item {
> + property alias subscriberNumbers: simManager.subscriberNumbers
> + property alias networkName: networkRegistration.name
> +
> + OfonoSimManager {
> + id: simManager
> + modemPath: modelData
> + onSubscriberNumbersChanged: {
> + console.debug("New numbers:" + subscriberNumbers)
> + dirtyModel.restart()
> + }
> + }
> +
> + OfonoNetworkRegistration {
> + id: networkRegistration
> + modemPath: modelData
> + onNameChanged: {
> + dirtyModel.restart()
> + }
> + }
> + }
> + onCountChanged: dirtyModel.restart()
> + }
> +
> + Timer {
> + id: dirtyModel
> +
> + interval: 1000
> + repeat: false
> + onTriggered: root.reloadNumbers()
> + }
> + }
> +}
>
> === added file 'src/imports/Settings/SettingsPage.qml'
> --- src/imports/Settings/SettingsPage.qml 1970-01-01 00:00:00 +0000
> +++ src/imports/Settings/SettingsPage.qml 2015-03-17 17:39:47 +0000
> @@ -0,0 +1,92 @@
> +/*
> + * Copyright (C) 2015 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.2
> +import QtContacts 5.0
> +
> +import Ubuntu.Components 1.1
> +import Ubuntu.Components.ListItems 1.0 as ListItem
> +import Ubuntu.Contacts 0.1 as ContactsUI
> +
> +Page {
> + id: root
> + objectName: "settingsPage"
> +
> + property var contactListModel
> +
> + title: i18n.tr("Settings")
> +
> + ContactsUI.SIMList {
> + id: simList
> + }
> +
> + MyselfPhoneNumbersModel {
> + id: myself
> + }
> +
> + flickable: null
> + Flickable {
> + id: numberFlickable
> + contentHeight: childrenRect.height
> + anchors.fill: parent
> + clip: true
> +
> + Column {
> + anchors{
> + left: parent.left
> + right: parent.right
> + }
> + height: childrenRect.height + units.gu(4)
> +
> + Repeater {
> + anchors {
> + left: parent.left
> + right: parent.right
> + }
> + model: myself
> + delegate: ListItem.Subtitled {
> + text: i18n.tr("My phone number: %1").arg(phoneNumber)
> + subText: network != "" ? network : i18n.tr("SIM %1").arg(index)
> + }
> + onCountChanged: numberFlickable.contentY = 0
> + }
> + ListItem.Standard {
> + text: i18n.tr("Add Google account")
> + progression: true
> + onClicked: onlineAccountsHelper.setupExec()
> + }
> + ListItem.Standard {
> + text: i18n.tr("Import from SIM")
> + progression: true
> + onClicked: pageStack.push(simCardImportPageComponent)
> + enabled: (simList.sims.length > 0) && (simList.present.length > 0)
> + }
> + }
> + }
> + ContactsUI.OnlineAccountsHelper {
> + id: onlineAccountsHelper
> + }
> +
> + Component {
> + id: simCardImportPageComponent
> +
> + ContactsUI.SIMCardImportPage {
> + objectName: "simCardImportPage"
> + targetModel: root.contactListModel
> + sims: simList.sims
> + }
> + }
> +}
>
> === modified file 'src/imports/Ubuntu/Contacts/CMakeLists.txt'
> --- src/imports/Ubuntu/Contacts/CMakeLists.txt 2015-02-06 16:37:12 +0000
> +++ src/imports/Ubuntu/Contacts/CMakeLists.txt 2015-03-17 17:39:47 +0000
> @@ -36,11 +36,14 @@
> MostCalledModel.qml
> MultipleSelectionListView.qml
> MultipleSelectionVisualModel.qml
> + Ofono.qml
> OnlineAccountsDummy.qml
> OnlineAccountsHelper.qml
> PageWithBottomEdge.qml
> qmldir
> SectionDelegate.qml
> + SIMList.qml
> + SIMCardImportPage.qml
> SubtitledWithColors.qml
> VCardParser.qml
> )
> @@ -52,13 +55,23 @@
> mostcalledproxymodel.cpp
> plugin.h
> plugin.cpp
> + simcardcontacts.h
> + simcardcontacts.cpp
> +)
> +
> +include_directories(
> + ${QOfono_INCLUDE_DIRS}
> )
>
> add_library(${CONTACT_COMPONENTS_PLUGIN} MODULE
> ${CONTACT_COMPONENTS_SRC}
> )
>
> -qt5_use_modules(${CONTACT_COMPONENTS_PLUGIN} Core Contacts Qml Quick)
> +target_link_libraries(${CONTACT_COMPONENTS_PLUGIN}
> + ${QOfono_LIBRARIES}
> +)
> +
> +qt5_use_modules(${CONTACT_COMPONENTS_PLUGIN} Core Contacts Qml Quick DBus)
>
> # make the files visible on qtcreator
> add_custom_target(contact_components_QmlFiles ALL SOURCES ${CONTACT_COMPONENTS_QMLS})
>
> === modified file 'src/imports/Ubuntu/Contacts/ContactListButtonDelegate.qml'
> --- src/imports/Ubuntu/Contacts/ContactListButtonDelegate.qml 2015-02-09 13:25:01 +0000
> +++ src/imports/Ubuntu/Contacts/ContactListButtonDelegate.qml 2015-03-17 17:39:47 +0000
> @@ -30,7 +30,7 @@
> left: parent.left
> right: parent.right
> }
> - height: visible ? units.gu(8) :0
> + height: visible ? units.gu(8) : 0
>
> Rectangle {
> anchors.fill: parent
>
> === modified file 'src/imports/Ubuntu/Contacts/ContactListView.qml'
> --- src/imports/Ubuntu/Contacts/ContactListView.qml 2015-02-09 17:59:01 +0000
> +++ src/imports/Ubuntu/Contacts/ContactListView.qml 2015-03-17 17:39:47 +0000
> @@ -1,4 +1,4 @@
> -/*
> +/*
> * Copyright (C) 2012-2013 Canonical, Ltd.
> *
> * This program is free software; you can redistribute it and/or modify
> @@ -222,7 +222,13 @@
>
> This property holds if the list is busy or not
> */
> - property alias busy: indicator.visible
> + property alias busy: indicator.isBusy
> + /*!
> + \qmlproperty bool showBusyIndicator
> +
> + This property holds if the busy indicator should became visible
> + */
> + property bool showBusyIndicator: true
>
> /*!
> This handler is called when the selection mode is finished without be canceled
> @@ -420,34 +426,68 @@
> visible: root.showAddNewButton
> }
>
> - // Import from google
> - ContactListButtonDelegate {
> - id: importFromGoogleButton
> -
> - objectName: "importFromOnlineAccountButton"
> -
> - visible: (onlineAccountHelper.status === Loader.Ready) &&
> - !indicator.visible
> - expandIcon: true
> - iconSource: "image://theme/google"
> - // TRANSLATORS: this refers to a new contact
> - labelText: i18n.tr("Import contacts from Google")
> - onClicked: onlineAccountHelper.item.setupExec()
> + Column {
> + id: importFromButtons
> + objectName: "importFromButtons"
> +
> + readonly property bool isSearching: (root.filterTerm && root.filterTerm !== "")
> +
> + anchors {
> + left: parent.left
> + right: parent.right
> + }
> + height: visible ? childrenRect.height : 0
> +
> + visible: root.showImportOptions &&
> + !indicator.visible &&
> + (root.count === 0) &&
> + !view.favouritesIsSelected &&
> + !isSearching
>
> // avoid show the button while the list still loading contacts
> Behavior on visible {
> SequentialAnimation {
> PauseAnimation {
> - duration: !importFromGoogleButton.visible ? 500 : 0
> + duration: !importFromButtons.visible ? 500 : 0
> }
> PropertyAction {
> - target: importFromGoogleButton
> + target: importFromButtons
> property: "visible"
> }
> }
> }
> +
> + // Import from google
> + ContactListButtonDelegate {
> + id: importFromGoogleButton
> + objectName: "%1.importFromOnlineAccountButton".arg(root.objectName)
> +
> + visible: (onlineAccountHelper.status === Loader.Ready)
> + expandIcon: true
> + iconSource: "image://theme/google"
> + labelText: i18n.tr("Import contacts from Google")
> + onClicked: onlineAccountHelper.item.setupExec()
> + }
> +
> + // Import from sim card
> + ContactListButtonDelegate {
> + id: importFromSimCard
> + objectName: "%1.importFromSimCardButton".arg(root.objectName)
> +
> + expandIcon: true
> + iconSource: "image://theme/save-to"
> + labelText: i18n.tr("Import contacts from SIM card")
> + // Does not show the button if the list is not in a pageStack
> + visible: (typeof(pageStack) !== "undefined") &&
> + ((simList.sims.length > 0) && (simList.present.length > 0))
> + onClicked: {
> + pageStack.push(Qt.resolvedUrl("SIMCardImportPage.qml"),
> + {"objectName": "simCardImportPage",
> + "targetModel": view.listModel,
> + "sims": simList.sims})
> + }
> + }
> }
> - // TODO: import from simcard
>
> MostCalledList {
> id: mostCalledView
> @@ -475,7 +515,6 @@
> }
>
> clip: true
> -
> listModel: ContactListModel {
> id: contactsModel
>
> @@ -488,11 +527,14 @@
> Column {
> id: indicator
>
> + readonly property bool isBusy: ((view.loading && !view.contactsLoaded) ||
> + (root.syncing && (view.count === 0)) ||
> + ((onlineAccountHelper.status == Loader.Ready) &&
> + (onlineAccountHelper.item.running)))
> +
> anchors.centerIn: view
> spacing: units.gu(2)
> - visible: ((view.loading && !view.contactsLoaded) ||
> - (root.syncing && (view.count === 0)) ||
> - ((onlineAccountHelper.status == Loader.Ready) && (onlineAccountHelper.item.running)))
> + visible: root.showBusyIndicator && isBusy
>
> ActivityIndicator {
> id: activity
> @@ -526,6 +568,10 @@
> id: syncMonitor
> }
>
> + SIMList {
> + id: simList
> + }
> +
> Loader {
> id: onlineAccountHelper
> objectName: "onlineAccountHelper"
>
> === modified file 'src/imports/Ubuntu/Contacts/ContactSimpleListView.qml'
> --- src/imports/Ubuntu/Contacts/ContactSimpleListView.qml 2015-02-10 12:40:19 +0000
> +++ src/imports/Ubuntu/Contacts/ContactSimpleListView.qml 2015-03-17 17:39:47 +0000
> @@ -16,7 +16,6 @@
>
> import QtQuick 2.2
> import QtContacts 5.0
> -import Ubuntu.Contacts 0.1
> import Ubuntu.Components 1.1
> import Ubuntu.Components.ListItems 1.0 as ListItem
>
>
> === added file 'src/imports/Ubuntu/Contacts/Ofono.qml'
> --- src/imports/Ubuntu/Contacts/Ofono.qml 1970-01-01 00:00:00 +0000
> +++ src/imports/Ubuntu/Contacts/Ofono.qml 2015-03-17 17:39:47 +0000
> @@ -0,0 +1,36 @@
> +/*
> + * Copyright (C) 2015 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.0
> +import MeeGo.QOfono 0.2
> +
> +Item {
> + readonly property alias simMng: simMng
> + readonly property alias present: simMng.present
> +
> + property string path
> + property string name
> +
> + readonly property string title: {
> + var number = simMng.subscriberNumbers[0] || simMng.subscriberIdentity;
> + return name + (number ? " (" + number + ")" : "");
> + }
> +
> + OfonoSimManager {
> + id: simMng
> + modemPath: path
> + }
> +}
>
> === added file 'src/imports/Ubuntu/Contacts/SIMCardImportPage.qml'
> --- src/imports/Ubuntu/Contacts/SIMCardImportPage.qml 1970-01-01 00:00:00 +0000
> +++ src/imports/Ubuntu/Contacts/SIMCardImportPage.qml 2015-03-17 17:39:47 +0000
> @@ -0,0 +1,248 @@
> +/*
> + * Copyright (C) 2015 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.2
> +import QtContacts 5.0
> +
> +import Ubuntu.Components 1.1
> +import Ubuntu.Contacts 0.1
> +import Ubuntu.Components.ListItems 1.0 as ListItem
> +
> +import MeeGo.QOfono 0.2
> +
> +Page {
> + id: root
> +
> + readonly property string exportFile: "file:///tmp/ubuntu_contacts_sim.vcf"
Some questions:
1) Can't we use QStandardPaths::AppDataLocation for this instead of /tmp?
2) Is this file ever getting deleted after its creation?
> + readonly property alias hasContacts: simCardContacts.hasContacts
> + property var targetModel: null
> + property var sims: []
> +
> + title: i18n.tr("SIM contacts")
> +
> + function lockedSIMCount()
> + {
> + var count = 0
> + for(var i=0; i < sims.length; i++) {
> + if (sims[i].simMng.pinRequired !== OfonoSimManager.NoPin) {
> + count++
> + }
> + }
> + return count
> + }
> +
> + Timer {
> + id: simUnlocking
> +
> + interval: 2000
> + repeat: false
> + running: false
> + }
> +
> + Column {
> + id: lockedSIMList
> + anchors {
> + left: parent.left
> + right: parent.right
> + }
> +
> + Repeater {
> + id: lockedSIMRepeater
> + anchors {
> + left: parent.left
> + right: parent.right
> + }
> + model: sims.length
> + delegate: ListItem.Standard {
> + visible: sims[index].simMng.pinRequired !== OfonoSimManager.NoPin
> + onVisibleChanged: {
> + if (visible)
> + simUnlocking.stop()
> + else
> + simUnlocking.start()
> + }
> + text: i18n.tr("%1 is locked").arg(sims[index].title)
> + control: Button {
> + text: i18n.tr("Unlock")
> + onClicked: simCardContacts.unlockModem(sims[index].path)
> + }
> + }
> + }
> + }
> +
> + ContactListView {
> + id: contactList
> + objectName: "contactListViewFromSimCard"
> +
> + anchors {
> + left: parent.left
> + right: parent.right
> + top: lockedSIMList.bottom
> + bottom: parent.bottom
> + }
> + multiSelectionEnabled: true
> + multipleSelection: true
> + showSections: false
> + visible: !indicator.visible
> + showBusyIndicator: false
> +
> + manager: "memory"
> + onSelectionCanceled: pageStack.pop()
> + }
> +
> + Label {
> + id: statusMessage
> +
> + anchors.centerIn: parent
> + text: i18n.tr("No contacts found")
> + visible: ((contactList.count === 0) &&
> + (root.state === "") &&
> + !contactList.busy &&
> + (sims.length > root.lockedSIMCount()))
> + }
> +
> + Column {
> + id: indicator
> +
> + property alias title: activityLabel.text
> +
> + anchors.centerIn: root
> + spacing: units.gu(2)
> + visible: false
> +
> + ActivityIndicator {
> + id: activity
> +
> + anchors.horizontalCenter: parent.horizontalCenter
> + running: indicator.visible
> + }
> + Label {
> + id: activityLabel
> +
> + anchors.horizontalCenter: activity.horizontalCenter
> + }
> + }
> +
> + SimCardContacts {
> + id: simCardContacts
> +
> + property bool contactImported: false
> +
> + Component.onCompleted: {
> + if (vcardFile != "" && !contactImported) {
> + contactImported = true
> + contactList.listModel.importContacts(vcardFile)
> + }
> + }
> + onVcardFileChanged: {
> + if ((vcardFile != "") && !contactImported) {
> + contactImported = true
> + contactList.listModel.importContacts(vcardFile)
> + }
> + }
> + onImportFail: root.state = "error"
> + }
> +
> + Connections {
> + target: contactList.listModel
> + onImportCompleted: {
> + contactList.startSelection()
> + root.state = ""
> + }
> +
> + onExportCompleted: {
> + if ((error === ContactModel.ExportNoError) && targetModel) {
> + targetModel.importContacts(url)
> + }
> + pageStack.pop()
> + }
> + }
> +
> + head.actions: [
> + Action {
> + text: (contactList.selectedItems.count === contactList.count) ?
> + i18n.tr("Unselect All") :
> + i18n.tr("Select All")
> + iconName: "select"
> + onTriggered: {
> + if (contactList.selectedItems.count === contactList.count) {
> + contactList.clearSelection()
> + } else {
> + contactList.selectAll()
> + }
> + }
> + visible: (contactList.count > 0)
> + },
> + Action {
> + text: i18n.tr("Import")
> + objectName: "confirmImport"
> +
> + iconName: "tick"
> + enabled: (contactList.selectedItems.count > 0)
> + onTriggered: {
> + root.state = "importing"
> + var contacts = []
> + var items = contactList.selectedItems
> +
> + for (var i=0, iMax=items.count; i < iMax; i++) {
> + contacts.push(items.get(i).model.contact)
> + }
> +
> + contactList.listModel.exportContacts(root.exportFile,
> + [],
> + contacts)
> + }
> + }
> + ]
> +
> + states: [
> + State {
> + name: "loading"
> + when: (simCardContacts.busy || contactList.busy) &&
> + (sims.length > root.lockedSIMCount())
> + PropertyChanges {
> + target: indicator
> + title: i18n.tr("Loading...")
> + visible: true
> + }
> + },
> + State {
> + name: "unlocking"
> + when: !simCardContacts.busy && (contactList.count == 0) && (simUnlocking.running)
> + PropertyChanges {
> + target: indicator
> + title: i18n.tr("Unlocking...")
> + visible: true
> + }
> + },
> + State {
> + name: "importing"
> + PropertyChanges {
> + target: indicator
> + title: i18n.tr("Importing...")
> + visible: true
> + }
> + },
> + State {
> + name: "error"
> + PropertyChanges {
> + target: statusMessage
> + text: i18n.tr("Fail to read SIM card")
> + visible: true
> + }
> + }
> + ]
> +}
>
> === added file 'src/imports/Ubuntu/Contacts/SIMList.qml'
> --- src/imports/Ubuntu/Contacts/SIMList.qml 1970-01-01 00:00:00 +0000
> +++ src/imports/Ubuntu/Contacts/SIMList.qml 2015-03-17 17:39:47 +0000
> @@ -0,0 +1,81 @@
> +/*
> + * Copyright (C) 2015 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +import QtQuick 2.2
> +
> +import MeeGo.QOfono 0.2
> +
> +import GSettings 1.0
> +
> +Item {
> + id: root
> +
> + property var sims: []
> + property var present: []
> +
> + function filterPresentSims()
> + {
> + var presentSims = []
> + sims.forEach(function (sim) {
> + if (sim.present) {
> + presentSims.push(sim)
> + }
> + });
> + root.present = presentSims
> + }
> +
> + function createQML (modems)
> + {
> + var component = Qt.createComponent(Qt.resolvedUrl("Ofono.qml"));
> +
> + sims.forEach(function (sim) {
> + sim.destroy();
> + });
> + var newSims = []
> + var presentSims = []
> +
> + modems.forEach(function (path, index) {
> + var sim = component.createObject(root, {
> + path: path,
> + name: phoneSettings.simNames[path] ? phoneSettings.simNames[path] :
Is this "name" property being used somewhere?
> + "SIM " + (index + 1)
> + });
> + if (sim === null) {
> + console.warn('Failed to create Sim qml:', component.errorString());
> + } else {
> + newSims.push(sim)
> + if (sim.present) {
> + presentSims.push(sim)
> + }
> + sim.onPresentChanged.connect(filterPresentSims)
> + }
> + });
> +
> + root.sims = newSims
> + root.present = presentSims
> + }
> +
> +
> + GSettings {
> + id: phoneSettings
> + schema.id: "com.ubuntu.phone"
> + }
> +
> + OfonoManager {
> + id: ofonoManager
> + onModemsChanged: root.createQML(modems.slice(0).sort())
> + }
> +}
>
> === modified file 'src/imports/Ubuntu/Contacts/plugin.cpp'
> --- src/imports/Ubuntu/Contacts/plugin.cpp 2014-07-05 22:00:02 +0000
> +++ src/imports/Ubuntu/Contacts/plugin.cpp 2015-03-17 17:39:47 +0000
> @@ -17,6 +17,7 @@
> #include "plugin.h"
> #include "mostcalledproxymodel.h"
> #include "contacts.h"
> +#include "simcardcontacts.h"
>
> #include <QQmlEngine>
> #include <qqml.h>
> @@ -39,4 +40,5 @@
> // @uri Ubuntu.Contacts
> qmlRegisterSingletonType<UbuntuContacts>(uri, 0, 1, "Contacts", contactsProvider);
> qmlRegisterType<MostCalledContactsModel>(uri, 0, 1, "MostCalledContactsModel");
> + qmlRegisterType<SimCardContacts>(uri, 0, 1, "SimCardContacts");
> }
>
> === modified file 'src/imports/Ubuntu/Contacts/qmldir'
> --- src/imports/Ubuntu/Contacts/qmldir 2015-02-05 16:18:10 +0000
> +++ src/imports/Ubuntu/Contacts/qmldir 2015-03-17 17:39:47 +0000
> @@ -20,6 +20,9 @@
> ContactDetailGroupBase 0.1 ContactDetailGroupBase.qml
> ContactDetailGroupWithTypeBase 0.1 ContactDetailGroupWithTypeBase.qml
> ContactDetailGroupWithTypeView 0.1 ContactDetailGroupWithTypeView.qml
> +SIMCardImportPage 0.1 SIMCardImportPage.qml
> +OnlineAccountsHelper 0.1 OnlineAccountsHelper.qml
> +SIMList 0.1 SIMList.qml
>
> internal BasicFieldView BasicFieldView.qml
> internal ContactAvatar ContactAvatar.qml
> @@ -42,5 +45,6 @@
> internal ListItemWithActionsCheckBox ListItemWithActionsCheckBox.qml
> internal MostCalledList MostCalledList.qml
> internal MostCalledModel MostCalledModel.qml
> +internal Ofono Ofono.qml
> internal SectionDelegate SectionDelegate.qml
> internal SubtitledWithColors SubtitledWithColors.qml
>
> === added file 'src/imports/Ubuntu/Contacts/simcardcontacts.cpp'
> --- src/imports/Ubuntu/Contacts/simcardcontacts.cpp 1970-01-01 00:00:00 +0000
> +++ src/imports/Ubuntu/Contacts/simcardcontacts.cpp 2015-03-17 17:39:47 +0000
> @@ -0,0 +1,300 @@
> +/*
> + * Copyright (C) 2015 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "simcardcontacts.h"
> +
> +#include <QDebug>
> +#include <QDBusConnection>
> +#include <qofonophonebook.h>
> +#include <qofono-qt5/dbus/ofonophonebook.h>
> +
> +SimCardContacts::SimCardContacts(QObject *parent)
> + : QObject(parent),
> + m_ofonoManager(new QOfonoManager(this)),
> + m_dataFile(0)
> +{
> + onManagerChanged();
> + m_modemsChangedTimer.setInterval(1000);
> + m_modemsChangedTimer.setSingleShot(true);
> + connect(m_ofonoManager.data(),
> + SIGNAL(modemsChanged(QStringList)),
> + SLOT(onManagerChanged()),
> + Qt::QueuedConnection);
> + connect(m_ofonoManager.data(),
> + SIGNAL(availableChanged(bool)),
> + SLOT(onManagerChanged()),
> + Qt::QueuedConnection);
> + connect(&m_modemsChangedTimer,
> + SIGNAL(timeout()),
> + SLOT(onModemsChanged()));
> +}
> +
> +SimCardContacts::~SimCardContacts()
> +{
> + Q_FOREACH(QOfonoModem *m, m_availableModems) {
> + disconnect(m);
> + m->deleteLater();
> + }
> + m_availableModems.clear();
> +
> + cancel();
> + delete m_dataFile;
> +}
> +
> +QString SimCardContacts::contacts() const
> +{
> + QString result;
> + Q_FOREACH(const QString &data, m_vcards) {
> + result += data;
> + }
> + return result;
> +}
> +
> +QUrl SimCardContacts::vcardFile() const
> +{
> + if (m_dataFile) {
> + return QUrl::fromLocalFile(m_dataFile->fileName());
> + } else {
> + return QUrl();
> + }
> +}
> +
> +bool SimCardContacts::hasContacts() const
> +{
> + return !m_vcards.isEmpty();
> +}
> +
> +bool SimCardContacts::busy() const
> +{
> + return (m_modemsChangedTimer.isActive() ||
> + m_importingFlag);
> +}
> +
> +void SimCardContacts::onPhoneBookIsValidChanged(bool isValid)
> +{
> + QOfonoPhonebook *pb = qobject_cast<QOfonoPhonebook*>(QObject::sender());
> + Q_ASSERT(pb);
> + if (isValid) {
> + importPhoneBook(pb);
> + } else {
> + m_pendingPhoneBooks.remove(pb);
> + if (m_pendingPhoneBooks.isEmpty()) {
> + importDone();
> + }
> + pb->deleteLater();
> + }
> +}
> +
> +void SimCardContacts::onModemsChanged()
> +{
> + qDebug() << "Modems changed";
> + startImport();
> +
> + Q_FOREACH(QOfonoModem *modem, m_availableModems) {
> + importPhoneBook(modem);
> + }
> +
> + if (m_pendingPhoneBooks.size() == 0) {
> + importDone();
> + }
> +}
> +
> +void SimCardContacts::onManagerChanged()
> +{
> + startImport();
> +
> + // clear modem list
> + Q_FOREACH(QObject *m, m_availableModems) {
> + disconnect(m);
> + m->deleteLater();
> + }
> + m_availableModems.clear();
> +
> + if (!m_ofonoManager->available()) {
> + qWarning() << "Manager not available;";
> + return;
> + }
> +
> + QStringList modems = m_ofonoManager->modems();
> + Q_FOREACH(const QString &modem, modems) {
> + QOfonoModem *m = new QOfonoModem(this);
> +
> + m->setModemPath(modem);
> + m_availableModems << m;
> +
> + importPhoneBook(m);
> +
> + connect(m, SIGNAL(interfacesChanged(QStringList)),
> + SLOT(reload()));
> + connect(m, SIGNAL(validChanged(bool)),
> + SLOT(reload()));
> +
> + }
> +
> + if (m_pendingPhoneBooks.size() == 0) {
> + importDone();
> + }
> +}
> +
> +bool SimCardContacts::importPhoneBook(QOfonoModem *modem)
> +{
> + if (hasPhoneBook(modem)) {
> + QOfonoPhonebook *pb = new QOfonoPhonebook(this);
> + pb->setModemPath(modem->modemPath());
> + m_pendingPhoneBooks << pb;
> + if (pb->isValid()) {
> + importPhoneBook(pb);
> + } else {
> + connect(pb,
> + SIGNAL(validChanged(bool)),
> + SLOT(onPhoneBookIsValidChanged(bool)),
> + Qt::QueuedConnection);
> + }
> + return true;
> + } else {
> + qDebug() << "Modem" << modem->modemPath() << "does not have phonebook interface";
> + }
> + return false;
> +}
> +
> +void SimCardContacts::importPhoneBook(QOfonoPhonebook *phoneBook)
> +{
> + connect(phoneBook,
> + SIGNAL(importReady(QString)),
> + SLOT(onPhoneBookImported(QString)));
> + connect(phoneBook,
> + SIGNAL(importFailed()),
> + SLOT(onPhoneBookImportFail()));
> +
> + phoneBook->beginImport();
> +}
> +
> +void SimCardContacts::onPhoneBookImported(const QString &vcardData)
> +{
> + QOfonoPhonebook *pb = qobject_cast<QOfonoPhonebook*>(QObject::sender());
> + Q_ASSERT(pb);
> +
> + if (!vcardData.trimmed().isEmpty()) {
> + m_vcards << vcardData;
> + }
> + m_pendingPhoneBooks.remove(pb);
> + if (m_pendingPhoneBooks.isEmpty()) {
> + importDone();
> + }
> + pb->deleteLater();
> +}
> +
> +void SimCardContacts::onPhoneBookImportFail()
> +{
> + QOfonoPhonebook *pb = qobject_cast<QOfonoPhonebook*>(QObject::sender());
> + Q_ASSERT(pb);
> + qWarning() << "Fail to import contacts from:" << pb->modemPath();
> +
> + m_pendingPhoneBooks.remove(pb);
> + if (m_pendingPhoneBooks.isEmpty()) {
> + importDone();
> + }
> + pb->deleteLater();
> + Q_EMIT importFail();
> +}
> +
> +void SimCardContacts::startImport()
> +{
> + m_importingFlag = true;
> + Q_EMIT busyChanged();
> + if (!m_importing.tryLock()) {
> + qDebug() << "Import in progress.";
> + cancel();
> + if (!m_importing.tryLock()) {
> + qWarning() << "Fail to cancel current import";
> + return;
> + }
> + }
> + m_vcards.clear();
> + Q_EMIT contactsChanged();
> +}
> +
> +void SimCardContacts::importDone()
> +{
> + Q_ASSERT(m_pendingModems.isEmpty());
> +
> + writeData();
> + m_importing.unlock();
> + Q_EMIT contactsChanged();
> + m_importingFlag = false;
> + Q_EMIT busyChanged();
> +}
> +
> +void SimCardContacts::writeData()
> +{
> + if (m_dataFile) {
> + delete m_dataFile;
> + m_dataFile = 0;
> + }
> + if (!m_vcards.isEmpty()) {
> + m_dataFile = new QTemporaryFile();
> + m_dataFile->open();
> + Q_FOREACH(const QString &data, m_vcards) {
> + m_dataFile->write(data.toUtf8());
> + }
> + m_dataFile->close();
> + }
> +}
> +
> +void SimCardContacts::cancel()
> +{
> + Q_FOREACH(QOfonoPhonebook *m, m_pendingPhoneBooks) {
> + disconnect(m);
> + m->deleteLater();
> + }
> + m_pendingPhoneBooks.clear();
> +
> + m_importing.unlock();
> + m_vcards.clear();
> +
> + m_importingFlag = false;
> + Q_EMIT busyChanged();
> +}
> +
> +bool SimCardContacts::hasPhoneBook(QOfonoModem *modem)
> +{
> + return (modem->isValid() && modem->interfaces().contains("org.ofono.Phonebook"));
> +}
> +
> +void SimCardContacts::reload()
> +{
> + m_modemsChangedTimer.start();
> + Q_EMIT busyChanged();
> +}
> +
> +void SimCardContacts::unlockModem(const QString &modemPath)
> +{
> + static const QString connService("com.ubuntu.connectivity1");
> + static const QString connObject("/com/ubuntu/connectivity1/Private");
> + static const QString connInterface("com.ubuntu.connectivity1.Private");
> + static const QString connUnlockmodemMethod("UnlockModem");
> +
> + QDBusInterface connectivityIface (connService,
> + connObject,
> + connInterface,
> + QDBusConnection::sessionBus(),
> + this);
> +
> + auto reply = connectivityIface.call(connUnlockmodemMethod, modemPath);
> + if (reply.type() == QDBusMessage::ErrorMessage) {
> + qWarning() << "Failed to unlock modem" << modemPath << reply.errorMessage();
> + }
> +}
>
> === added file 'src/imports/Ubuntu/Contacts/simcardcontacts.h'
> --- src/imports/Ubuntu/Contacts/simcardcontacts.h 1970-01-01 00:00:00 +0000
> +++ src/imports/Ubuntu/Contacts/simcardcontacts.h 2015-03-17 17:39:47 +0000
> @@ -0,0 +1,80 @@
> +/*
> + * Copyright (C) 2015 Canonical, Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef SIMCARDCONTACTS_H
> +#define SIMCARDCONTACTS_H
> +
> +#include <QObject>
> +#include <QMutex>
> +
> +#include <qofonomanager.h>
> +#include <qofonophonebook.h>
> +#include <qofonomodem.h>
> +
> +class SimCardContacts : public QObject
> +{
> + Q_OBJECT
> + Q_PROPERTY(QString contacts READ contacts NOTIFY contactsChanged)
> + Q_PROPERTY(QUrl vcardFile READ vcardFile NOTIFY contactsChanged)
> + Q_PROPERTY(bool hasContacts READ hasContacts NOTIFY contactsChanged)
> + Q_PROPERTY(bool busy READ busy NOTIFY busyChanged)
> +
> +public:
> + SimCardContacts(QObject *parent=0);
> + ~SimCardContacts();
> +
> + QString contacts() const;
> + QUrl vcardFile() const;
> + bool hasContacts() const;
> + bool busy() const;
> +
> + Q_INVOKABLE void unlockModem(const QString &modemPath);
> +
> +Q_SIGNALS:
> + void contactsChanged();
> + void importFail();
> + void busyChanged();
> +
> +private Q_SLOTS:
> + void onModemChanged();
> + void onPhoneBookIsValidChanged(bool isValid);
> + void onPhoneBookImported(const QString &vcardData);
> + void onPhoneBookImportFail();
> + void onManagerChanged();
> + void onModemsChanged();
> + void reload();
> +
> +private:
> + QScopedPointer<QOfonoManager> m_ofonoManager;
> + QSet<QOfonoPhonebook*> m_pendingPhoneBooks;
> + QSet<QOfonoModem*> m_availableModems;
> + QTemporaryFile *m_dataFile;
> + QStringList m_vcards;
> + QMutex m_importing;
> + QTimer m_modemsChangedTimer;
> + bool m_importingFlag;
> +
> + bool hasPhoneBook(QOfonoModem *modem);
> + void writeData();
> + void reloadContactsFromModem(QOfonoModem* modem);
> + void cancel();
> + void startImport();
> + void importDone();
> + bool importPhoneBook(QOfonoModem *modem);
> + void importPhoneBook(QOfonoPhonebook *phoneBook);
> +};
> +
> +#endif
>
> === modified file 'tests/autopilot/address_book_app/__init__.py'
> --- tests/autopilot/address_book_app/__init__.py 2015-02-25 17:43:29 +0000
> +++ tests/autopilot/address_book_app/__init__.py 2015-03-17 17:39:47 +0000
> @@ -97,6 +97,20 @@
> return p
> return None
>
> + def start_import_contacts(self):
> + header = self.open_header()
> + view = self.get_contact_list_view()
> + if view.count > 0:
> + self.click_action_button("importFromSimHeaderButton")
> + else:
> + import_buttom = self.select_single(
> + 'ContactListButtonDelegate',
> + objectName='contactListView.importFromSimCardButton')
> + self.pointing_device.click_object(import_buttom)
> +
> + return self.wait_select_single(pages.SIMCardImportPage,
> + objectName="simCardImportPage")
> +
> def get_contact_list_view(self):
> """
> Returns a ContactListView iobject for the current window
> @@ -143,6 +157,13 @@
> self.click_action_button("save")
> bottom_swipe_page.isCollapsed.wait_for(True)
>
> + def confirm_import(self):
> + """
> + Press the 'confirm' button
> + """
> + header = self.open_header()
> + self.click_action_button("confirmImport")
> +
> def get_toolbar(self):
> """Override base class so we get our expected Toolbar subclass."""
> return self.select_single(ubuntuuitoolkit.Toolbar)
>
> === added file 'tests/autopilot/address_book_app/helpers.py'
> --- tests/autopilot/address_book_app/helpers.py 1970-01-01 00:00:00 +0000
> +++ tests/autopilot/address_book_app/helpers.py 2015-03-17 17:39:47 +0000
> @@ -0,0 +1,52 @@
> +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
> +#
> +# Copyright 2014 Canonical Ltd.
> +# Author: Omer Akram <omer.akram at canonical.com>
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License version 3, as published
> +# by the Free Software Foundation.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program. If not, see <http://www.gnu.org/licenses/>.
> +
> +import dbus
> +
> +
> +def remove_phonesim():
> + bus = dbus.SystemBus()
> + try:
> + manager = dbus.Interface(bus.get_object('org.ofono', '/'),
> + 'org.ofono.phonesim.Manager')
> + except dbus.exceptions.DBusException:
> + return False
> +
> + manager.RemoveAll()
> +
> +
> +def reset_phonesim():
> + bus = dbus.SystemBus()
> + try:
> + manager = dbus.Interface(bus.get_object('org.ofono', '/'),
> + 'org.ofono.phonesim.Manager')
> + except dbus.exceptions.DBusException:
> + return False
> +
> + manager.Reset()
> +
> +
> +def is_phonesim_running():
> + """Determine whether we are running with phonesim."""
> + bus = dbus.SystemBus()
> + try:
> + manager = dbus.Interface(bus.get_object('org.ofono', '/'),
> + 'org.ofono.phonesim.Manager')
> + except dbus.exceptions.DBusException:
> + return False
> +
> + return (manager)
>
> === modified file 'tests/autopilot/address_book_app/pages/__init__.py'
> --- tests/autopilot/address_book_app/pages/__init__.py 2014-05-22 12:50:28 +0000
> +++ tests/autopilot/address_book_app/pages/__init__.py 2015-03-17 17:39:47 +0000
> @@ -18,8 +18,10 @@
> 'ContactEditor',
> 'ContactListPage',
> 'ContactView',
> + 'SIMCardImportPage',
> ]
>
> from address_book_app.pages._contact_editor import ContactEditor
> from address_book_app.pages._contact_list_page import ContactListPage
> from address_book_app.pages._contact_view import ContactView
> +from address_book_app.pages._sim_card_import_page import SIMCardImportPage
>
> === modified file 'tests/autopilot/address_book_app/pages/_contact_list_page.py'
> --- tests/autopilot/address_book_app/pages/_contact_list_page.py 2015-02-19 07:02:09 +0000
> +++ tests/autopilot/address_book_app/pages/_contact_list_page.py 2015-03-17 17:39:47 +0000
> @@ -126,6 +126,12 @@
> ]
> return [label.text for label in name_labels]
>
> + def get_button(self, buttonName):
> + try:
> + return self.get_header()._get_action_button(buttonName)
> + except ubuntuuitoolkit.ToolkitException:
> + return None
> +
>
> class RemoveContactsDialog(
> ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
>
> === added file 'tests/autopilot/address_book_app/pages/_sim_card_import_page.py'
> --- tests/autopilot/address_book_app/pages/_sim_card_import_page.py 1970-01-01 00:00:00 +0000
> +++ tests/autopilot/address_book_app/pages/_sim_card_import_page.py 2015-03-17 17:39:47 +0000
> @@ -0,0 +1,100 @@
> +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
> +#
> +# Copyright (C) 2014 Canonical Ltd.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License version 3, as published
> +# by the Free Software Foundation.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program. If not, see <http://www.gnu.org/licenses/>.
> +
> +import logging
> +
> +import autopilot.logging
> +
> +from address_book_app.pages import _common
> +
> +logger = logging.getLogger(__name__)
> +log_action_info = autopilot.logging.log_action(logging.info)
> +log_action_debug = autopilot.logging.log_action(logging.debug)
> +
> +class SIMCardImportPage(_common.PageWithHeader):
> + """Autopilot helper for the SIMCardImportPage page."""
> +
> + def _get_sorted_contact_delegates(self):
> + # FIXME this returns only the contact delegates that are loaded in
> + # memory. The list might be big, so not all delegates might be in
> + # memory at the same time.
> + contact_delegates = self.select_many('ContactDelegate', visible=True)
> + return sorted(
> + contact_delegates, key=lambda delegate: delegate.globalRect.y)
> +
> + def _get_contact_delegate(self, index):
> + contact_delegates = self._get_sorted_contact_delegates()
> + return contact_delegates[index]
> +
> + def _start_selection(self, index):
> + # TODO change this for click_object once the press duration
> + # parameter is added. See http://pad.lv/1268782
> + contact = self._get_contact_delegate(index)
> + self.pointing_device.move_to_object(contact)
> + self.pointing_device.press()
> + time.sleep(2.0)
> + self.pointing_device.release()
> + view = self._get_list_view()
> + view.isInSelectionMode.wait_for(True)
> +
> + def _get_list_view(self):
> + return self.wait_select_single(
> + 'ContactListView', objectName='contactListViewFromSimCard')
> +
> + @log_action_debug
> + def _deselect_all(self):
> + """Deselect all contacts."""
> + view = self._get_list_view()
> + if view.isInSelectionMode:
> + contacts = self.select_many('ContactDelegate', visible=True)
> + for contact in contacts:
> + if contact.selected:
> + logger.info('Deselect {}.'.format(contact.objectName))
> + self.pointing_device.click_object(contact)
> + else:
> + logger.debug('The page is not in selection mode.')
> +
> + @log_action_info
> + def get_contacts(self):
> + """Return a list with the names of the contacts."""
> + contact_delegates = self._get_sorted_contact_delegates()
> + name_labels = [
> + delegate.select_single('Label', objectName='nameLabel') for
> + delegate in contact_delegates
> + ]
> + return [label.text for label in name_labels]
> +
> + @log_action_info
> + def select_contacts(self, indices):
> + """ Select contacts corresponding to the list of index in indices
> +
> + :param indices: List of integers
> +
> + """
> + contacts = []
> + self._deselect_all()
> + if len(indices) > 0:
> + view = self._get_list_view()
> + if not view.isInSelectionMode:
> + self._start_selection(indices[0])
> + indices = indices[1:]
> +
> + for index in indices:
> + contact = self._get_contact_delegate(index)
> + self.pointing_device.click_object(contact)
> + contacts.append(contact.select_single('Label', objectName='nameLabel').text)
> +
> + return contacts
>
> === added file 'tests/autopilot/address_book_app/tests/test_import_from_sim.py'
> --- tests/autopilot/address_book_app/tests/test_import_from_sim.py 1970-01-01 00:00:00 +0000
> +++ tests/autopilot/address_book_app/tests/test_import_from_sim.py 2015-03-17 17:39:47 +0000
> @@ -0,0 +1,93 @@
> +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
> +# Copyright 2013 Canonical
> +#
> +# This program is free software: you can redistribute it and/or modify it
> +# under the terms of the GNU General Public License version 3, as published
> +# by the Free Software Foundation.
> +
> +"""Tests for the Addressbook App"""
> +
> +from testtools.matchers import Equals
> +from testtools import skipUnless
> +
> +from autopilot.matchers import Eventually
> +
> +from address_book_app.tests import AddressBookAppTestCase
> +from address_book_app import helpers
> +
> +
> + at skipUnless(helpers.is_phonesim_running(),
> + "this test needs to run under with-ofono-phonesim")
> +class TestImportFromSimContact(AddressBookAppTestCase):
> + """Tests import a contact from sim card"""
> +
> + def setUp(self):
> + super(TestImportFromSimContact, self).setUp()
> + helpers.reset_phonesim()
> +
> + # wait list fully load
> + view = self.app.main_window.get_contact_list_view()
> + self.assertThat(
> + view.busy,
> + Eventually(Equals(False), timeout=30))
> +
> + def test_impot_item_is_visible_on_the_list(self):
> + # contact list is empty
> + list_page = self.app.main_window.get_contact_list_page()
> + self.assertThat(len(list_page.get_contacts()), Equals(0))
> +
> + # button should be visible if list is empty
> + import_from_sim_button = self.app.main_window.select_single(
> + 'ContactListButtonDelegate',
> + objectName='contactListView.importFromSimCardButton')
> + self.assertThat(
> + import_from_sim_button.visible,
> + Eventually(Equals(True), timeout=30))
> +
> + # add a new contact
> + self.add_contact("Fulano", "de Tal", ["(333) 123-4567"])
> +
> + # button should be invisible if list is not empty
> + self.assertThat(
> + import_from_sim_button.visible,
> + Eventually(Equals(False), timeout=30))
> +
> + def test_import_from_sim(self):
> + list_page = self.app.main_window.get_contact_list_page()
> +
> + # contact list is empty
> + self.assertThat(len(list_page.get_contacts()), Equals(0))
> +
> + # import two contacts
> + import_page = self.app.main_window.start_import_contacts()
> +
> + # check if the contacts is available
> + self.assertThat(
> + import_page.hasContacts,
> + Eventually(Equals(True), timeout=30))
> +
> + contacts = import_page.select_contacts([1, 3])
> + self.assertThat(len(contacts), Equals(2))
> + self.app.main_window.confirm_import()
> +
> + # verify if the contact was imported
> + new_contacts = list_page.get_contacts()
> + self.assertThat(len(new_contacts), Equals(2))
> + for contact in new_contacts:
> + contacts.remove(contact)
> + self.assertThat(len(contacts), Equals(0))
> +
> + def test_import_item_without_sim_card(self):
> + list_page = self.app.main_window.get_contact_list_page()
> +
> + # contact list is empty
> + self.assertThat(len(list_page.get_contacts()), Equals(0))
> +
> + # remove all sim cards
> + helpers.remove_phonesim()
> +
> + # check if the contact list is empty
> + import_page = self.app.main_window.start_import_contacts()
> + self.assertThat(
> + import_page.hasContacts,
> + Eventually(Equals(False), timeout=30))
>
> === modified file 'tests/qml/tst_ContactListView.qml'
> --- tests/qml/tst_ContactListView.qml 2015-02-09 16:18:51 +0000
> +++ tests/qml/tst_ContactListView.qml 2015-03-17 17:39:47 +0000
> @@ -40,6 +40,7 @@
>
> ContactListView {
> id: contactListPage
> + objectName: "contactListViewTest"
> anchors.fill: parent
> }
>
> @@ -77,6 +78,7 @@
> {
> root.contactListViewObj = contactListCmp.createObject(mainView, {"manager": "memory"})
> waitForRendering(root.contactListViewObj)
> + tryCompare(contactListViewObj, "busy", false)
>
> var onlineAccountHelper = findChild(root.contactListViewObj, "onlineAccountHelper")
> verify(onlineAccountHelper.sourceFile.indexOf("OnlineAccountsDummy.qml") > 0)
> @@ -84,7 +86,7 @@
>
> function cleanup()
> {
> - root.contactListViewObj.destroy()
> + root.contactListViewObj = null
> }
>
> function test_managerProperty()
> @@ -119,10 +121,12 @@
>
> function test_importButtonsVisibility()
> {
> - var importButton = findChild(root.contactListViewObj, "importFromOnlineAccountButton")
> + var bottonsHeader = findChild(root.contactListViewObj, "importFromButtons")
> + var importButton = findChild(root.contactListViewObj, "contactListViewTest.importFromOnlineAccountButton")
> var onlineAccountHelper = findChild(root.contactListViewObj, "onlineAccountHelper")
>
> tryCompare(root.contactListViewObj, "showImportOptions", false)
> + tryCompare(bottonsHeader, "visible", false)
> tryCompare(importButton, "visible", false)
> tryCompare(onlineAccountHelper, "status", Loader.Null)
> tryCompare(onlineAccountHelper, "isSearching", false)
> @@ -133,7 +137,8 @@
> tryCompare(root.contactListViewObj, "count", 0)
> tryCompare(onlineAccountHelper, "status", Loader.Ready)
> // need to wait a bit more until the list leave the loading state
> - tryCompare(importButton, "visible", true, 10000)
> + tryCompare(bottonsHeader, "visible", true, 10000)
> + tryCompare(importButton, "visible", true)
> verify(importButton.height > 0)
>
> // Button should disapear if the list is not empty
> @@ -159,9 +164,13 @@
> tryCompare(onlineAccountDialog.item, "running", false)
>
> // click
> - var importButton = findChild(root.contactListViewObj, "importFromOnlineAccountButton")
> + var bottonsHeader = findChild(root.contactListViewObj, "importFromButtons")
> + var importButton = findChild(root.contactListViewObj, "contactListViewTest.importFromOnlineAccountButton")
> // need to wait a bit more until the list leave the loading state
> + tryCompare(bottonsHeader, "visible", true, 10000)
> tryCompare(importButton, "visible", true, 10000)
> + tryCompare(bottonsHeader, "height", importButton.height)
> +
> mouseClick(importButton, importButton.width / 2, importButton.height / 2)
> tryCompare(onlineAccountDialog.item, "running", true)
> }
>
--
https://code.launchpad.net/~renatofilho/address-book-app/sim-card-import/+merge/248562
Your team Ubuntu Phablet Team is subscribed to branch lp:address-book-app.
More information about the Ubuntu-reviews
mailing list