Merge lp:~renatofilho/address-book-app/dialer-help into lp:address-book-app

Proposed by Renato Araujo Oliveira Filho
Status: Work in progress
Proposed branch: lp:~renatofilho/address-book-app/dialer-help
Merge into: lp:address-book-app
Diff against target: 2123 lines (+1625/-41)
33 files modified
CMakeLists.txt (+3/-0)
cmake/FindLibPhoneNumber.cmake (+23/-0)
cmake/LibFindMacros.cmake (+112/-0)
debian/control (+5/-0)
src/app/CMakeLists.txt (+6/-0)
src/app/addressbookapp.cpp (+3/-1)
src/app/addressbookapp.h (+4/-0)
src/app/main.cpp (+3/-1)
src/app/simcardutils.cpp (+158/-0)
src/app/simcardutils.h (+60/-0)
src/imports/CMakeLists.txt (+1/-0)
src/imports/ContactList/ContactListPage.qml (+14/-8)
src/imports/MainWindow.qml (+29/-0)
src/imports/Settings/CMakeLists.txt (+12/-0)
src/imports/Settings/MyselfPhoneNumbersModel.qml (+72/-0)
src/imports/Settings/SettingsDialing.qml (+137/-0)
src/imports/Settings/SettingsPage.qml (+90/-0)
src/imports/Ubuntu/Contacts/CMakeLists.txt (+12/-1)
src/imports/Ubuntu/Contacts/ContactListButtonDelegate.qml (+1/-1)
src/imports/Ubuntu/Contacts/ContactListView.qml (+49/-18)
src/imports/Ubuntu/Contacts/SIMCardImportPage.qml (+187/-0)
src/imports/Ubuntu/Contacts/plugin.cpp (+2/-0)
src/imports/Ubuntu/Contacts/qmldir (+2/-0)
src/imports/Ubuntu/Contacts/simcardcontacts.cpp (+271/-0)
src/imports/Ubuntu/Contacts/simcardcontacts.h (+75/-0)
tests/autopilot/address_book_app/__init__.py (+21/-0)
tests/autopilot/address_book_app/helpers.py (+52/-0)
tests/autopilot/address_book_app/pages/__init__.py (+2/-0)
tests/autopilot/address_book_app/pages/_contact_list_page.py (+6/-0)
tests/autopilot/address_book_app/pages/_sim_card_import_page.py (+100/-0)
tests/autopilot/address_book_app/tests/test_import_from_sim.py (+93/-0)
tests/qml/CMakeLists.txt (+8/-8)
tests/qml/tst_ContactListView.qml (+12/-3)
To merge this branch: bzr merge lp:~renatofilho/address-book-app/dialer-help
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
Ubuntu Phablet Team Pending
Review via email: mp+252663@code.launchpad.net

Commit message

WIP

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

Added missing dep.

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

License header.

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

Disabled unit tests.

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

Fix call number call.

416. By Renato Araujo Oliveira Filho

Fixed simcardutils property read.

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

Unmerged revisions

416. By Renato Araujo Oliveira Filho

Fixed simcardutils property read.

415. By Renato Araujo Oliveira Filho

Fix call number call.

414. By Renato Araujo Oliveira Filho

Disabled unit tests.

413. By Renato Araujo Oliveira Filho

License header.

412. By Renato Araujo Oliveira Filho

Added missing dep.

411. By Renato Araujo Oliveira Filho

Load correct settings.

410. By Renato Araujo Oliveira Filho

Implement support for fone number format based on sim card information.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2014-11-10 14:37:25 +0000
3+++ CMakeLists.txt 2015-03-13 02:10:21 +0000
4@@ -17,6 +17,9 @@
5 find_package(Qt5Quick)
6 find_package(Qt5DBus)
7 find_package(PkgConfig REQUIRED)
8+find_package(LibPhoneNumber REQUIRED)
9+
10+pkg_check_modules(QOfono qofono-qt5)
11
12 find_program(INTLTOOL_MERGE intltool-merge)
13 find_program(INTLTOOL_EXTRACT intltool-extract)
14
15=== added file 'cmake/FindLibPhoneNumber.cmake'
16--- cmake/FindLibPhoneNumber.cmake 1970-01-01 00:00:00 +0000
17+++ cmake/FindLibPhoneNumber.cmake 2015-03-13 02:10:21 +0000
18@@ -0,0 +1,23 @@
19+set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
20+
21+include(GNUInstallDirs)
22+include(LibFindMacros)
23+
24+# Include dir
25+find_path(LibPhoneNumber_INCLUDE_DIR
26+ NAMES phonenumberutil.h
27+ PATHS "/usr/local/${CMAKE_INSTALL_INCLUDEDIR}" ${CMAKE_INSTALL_FULL_INCLUDEDIR}
28+ PATH_SUFFIXES "phonenumbers"
29+)
30+
31+# library itself
32+find_library(LibPhoneNumber_LIBRARY
33+ NAMES phonenumber
34+ PATHS "/usr/local/${CMAKE_INSTALL_LIBDIR}" ${CMAKE_INSTALL_FULL_LIBDIR}
35+)
36+
37+# Set the include dir variables and the libraries and let libfind_process do the rest.
38+# NOTE: Singular variables for this library, plural for libraries this this lib depends on.
39+set(LibPhoneNumber_PROCESS_INCLUDES LibPhoneNumber_INCLUDE_DIR)
40+set(LibPhoneNumber_PROCESS_LIBS LibPhoneNumber_LIBRARY)
41+libfind_process(LibPhoneNumber)
42
43=== added file 'cmake/LibFindMacros.cmake'
44--- cmake/LibFindMacros.cmake 1970-01-01 00:00:00 +0000
45+++ cmake/LibFindMacros.cmake 2015-03-13 02:10:21 +0000
46@@ -0,0 +1,112 @@
47+# Version 1.0 (2013-04-12)
48+# Public Domain, originally written by Lasse Kärkkäinen <tronic@zi.fi>
49+# Published at http://www.cmake.org/Wiki/CMake:How_To_Find_Libraries
50+
51+# If you improve the script, please modify the forementioned wiki page because
52+# I no longer maintain my scripts (hosted as static files at zi.fi). Feel free
53+# to remove this entire header if you use real version control instead.
54+
55+# Changelog:
56+# 2013-04-12 Added version number (1.0) and this header, no other changes
57+# 2009-10-08 Originally published
58+
59+
60+# Works the same as find_package, but forwards the "REQUIRED" and "QUIET" arguments
61+# used for the current package. For this to work, the first parameter must be the
62+# prefix of the current package, then the prefix of the new package etc, which are
63+# passed to find_package.
64+macro (libfind_package PREFIX)
65+ set (LIBFIND_PACKAGE_ARGS ${ARGN})
66+ if (${PREFIX}_FIND_QUIETLY)
67+ set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} QUIET)
68+ endif (${PREFIX}_FIND_QUIETLY)
69+ if (${PREFIX}_FIND_REQUIRED)
70+ set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} REQUIRED)
71+ endif (${PREFIX}_FIND_REQUIRED)
72+ find_package(${LIBFIND_PACKAGE_ARGS})
73+endmacro (libfind_package)
74+
75+# CMake developers made the UsePkgConfig system deprecated in the same release (2.6)
76+# where they added pkg_check_modules. Consequently I need to support both in my scripts
77+# to avoid those deprecated warnings. Here's a helper that does just that.
78+# Works identically to pkg_check_modules, except that no checks are needed prior to use.
79+macro (libfind_pkg_check_modules PREFIX PKGNAME)
80+ if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
81+ include(UsePkgConfig)
82+ pkgconfig(${PKGNAME} ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARY_DIRS ${PREFIX}_LDFLAGS ${PREFIX}_CFLAGS)
83+ else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
84+ find_package(PkgConfig)
85+ if (PKG_CONFIG_FOUND)
86+ pkg_check_modules(${PREFIX} ${PKGNAME})
87+ endif (PKG_CONFIG_FOUND)
88+ endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
89+endmacro (libfind_pkg_check_modules)
90+
91+# Do the final processing once the paths have been detected.
92+# If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain
93+# all the variables, each of which contain one include directory.
94+# Ditto for ${PREFIX}_PROCESS_LIBS and library files.
95+# Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES.
96+# Also handles errors in case library detection was required, etc.
97+macro (libfind_process PREFIX)
98+ # Skip processing if already processed during this run
99+ if (NOT ${PREFIX}_FOUND)
100+ # Start with the assumption that the library was found
101+ set (${PREFIX}_FOUND TRUE)
102+
103+ # Process all includes and set _FOUND to false if any are missing
104+ foreach (i ${${PREFIX}_PROCESS_INCLUDES})
105+ if (${i})
106+ set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${i}})
107+ mark_as_advanced(${i})
108+ else (${i})
109+ set (${PREFIX}_FOUND FALSE)
110+ endif (${i})
111+ endforeach (i)
112+
113+ # Process all libraries and set _FOUND to false if any are missing
114+ foreach (i ${${PREFIX}_PROCESS_LIBS})
115+ if (${i})
116+ set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARIES} ${${i}})
117+ mark_as_advanced(${i})
118+ else (${i})
119+ set (${PREFIX}_FOUND FALSE)
120+ endif (${i})
121+ endforeach (i)
122+
123+ # Print message and/or exit on fatal error
124+ if (${PREFIX}_FOUND)
125+ if (NOT ${PREFIX}_FIND_QUIETLY)
126+ message (STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}")
127+ endif (NOT ${PREFIX}_FIND_QUIETLY)
128+ else (${PREFIX}_FOUND)
129+ if (${PREFIX}_FIND_REQUIRED)
130+ foreach (i ${${PREFIX}_PROCESS_INCLUDES} ${${PREFIX}_PROCESS_LIBS})
131+ message("${i}=${${i}}")
132+ endforeach (i)
133+ message (FATAL_ERROR "Required library ${PREFIX} NOT FOUND.\nInstall the library (dev version) and try again. If the library is already installed, use ccmake to set the missing variables manually.")
134+ endif (${PREFIX}_FIND_REQUIRED)
135+ endif (${PREFIX}_FOUND)
136+ endif (NOT ${PREFIX}_FOUND)
137+endmacro (libfind_process)
138+
139+macro(libfind_library PREFIX basename)
140+ set(TMP "")
141+ if(MSVC80)
142+ set(TMP -vc80)
143+ endif(MSVC80)
144+ if(MSVC90)
145+ set(TMP -vc90)
146+ endif(MSVC90)
147+ set(${PREFIX}_LIBNAMES ${basename}${TMP})
148+ if(${ARGC} GREATER 2)
149+ set(${PREFIX}_LIBNAMES ${basename}${TMP}-${ARGV2})
150+ string(REGEX REPLACE "\\." "_" TMP ${${PREFIX}_LIBNAMES})
151+ set(${PREFIX}_LIBNAMES ${${PREFIX}_LIBNAMES} ${TMP})
152+ endif(${ARGC} GREATER 2)
153+ find_library(${PREFIX}_LIBRARY
154+ NAMES ${${PREFIX}_LIBNAMES}
155+ PATHS ${${PREFIX}_PKGCONF_LIBRARY_DIRS}
156+ )
157+endmacro(libfind_library)
158+
159
160=== modified file 'debian/control'
161--- debian/control 2015-02-09 15:02:11 +0000
162+++ debian/control 2015-03-13 02:10:21 +0000
163@@ -22,6 +22,9 @@
164 qtbase5-dev,
165 qtdeclarative5-dev,
166 qtpim5-dev,
167+ libphonenumber-dev,
168+ libqofono-qt5-0,
169+ libqofono-dev,
170 thumbnailer-service,
171 xvfb [i386 amd64 armhf],
172 Standards-Version: 3.9.5
173@@ -38,6 +41,7 @@
174 libqt5versit5,
175 ubuntu-ui-toolkit-theme (>= 0.1.49+14.10.20140707),
176 qmlscene,
177+ qml-module-qt-labs-settings,
178 qtcontact5-galera,
179 qtdeclarative5-qtcontacts-plugin,
180 qtdeclarative5-qtquick2-plugin,
181@@ -87,6 +91,7 @@
182 libautopilot-qt,
183 libqt5test5,
184 libqt5widgets5,
185+ ofono-phonesim-autostart,
186 python-testscenarios,
187 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.49+14.10.20140707) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles,
188 ubuntu-ui-toolkit-autopilot (>= 0.1.46+14.10.20140527),
189
190=== modified file 'src/app/CMakeLists.txt'
191--- src/app/CMakeLists.txt 2014-06-11 21:25:08 +0000
192+++ src/app/CMakeLists.txt 2015-03-13 02:10:21 +0000
193@@ -4,6 +4,7 @@
194
195 include_directories(
196 ${CMAKE_BINARY_DIR}
197+ ${LibPhoneNumber_INCLUDE_DIRS}
198 )
199
200 set(ADDRESS_BOOK_APP_SRCS
201@@ -11,6 +12,8 @@
202 addressbookapp.cpp
203 imagescalethread.h
204 imagescalethread.cpp
205+ simcardutils.h
206+ simcardutils.cpp
207 main.cpp
208 )
209
210@@ -19,6 +22,9 @@
211 )
212
213 qt5_use_modules(${ADDRESS_BOOK_APP_BIN} Gui Core Qml Quick DBus)
214+target_link_libraries(${ADDRESS_BOOK_APP_BIN}
215+ ${LibPhoneNumber_LIBRARIES}
216+)
217
218 install(TARGETS ${ADDRESS_BOOK_APP_BIN}
219 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
220
221=== modified file 'src/app/addressbookapp.cpp'
222--- src/app/addressbookapp.cpp 2015-02-09 14:06:06 +0000
223+++ src/app/addressbookapp.cpp 2015-03-13 02:10:21 +0000
224@@ -17,6 +17,7 @@
225 #include "config.h"
226 #include "addressbookapp.h"
227 #include "imagescalethread.h"
228+#include "simcardutils.h"
229
230 #include <QDir>
231 #include <QUrl>
232@@ -33,7 +34,6 @@
233 #include <QSettings>
234 #include <QTimer>
235 #include <QElapsedTimer>
236-
237 #include <QQmlEngine>
238
239 #define ADDRESS_BOOK_FIRST_RUN_KEY "first-run"
240@@ -101,6 +101,7 @@
241 AddressBookApp::AddressBookApp(int &argc, char **argv)
242 : QGuiApplication(argc, argv),
243 m_view(0),
244+ m_simcardUtils(new SIMCardUtils(this)),
245 m_pickingMode(false),
246 m_testMode(false),
247 m_withArgs(false)
248@@ -191,6 +192,7 @@
249 m_view->engine()->addImportPath(QCoreApplication::applicationDirPath() + "/" + importPath(""));
250 m_view->rootContext()->setContextProperty("QTCONTACTS_MANAGER_OVERRIDE", defaultManager);
251 m_view->rootContext()->setContextProperty("application", this);
252+ m_view->rootContext()->setContextProperty("simcardUtils", m_simcardUtils.data());
253 m_view->rootContext()->setContextProperty("contactKey", contactKey);
254 m_view->rootContext()->setContextProperty("TEST_DATA", testData);
255 m_view->rootContext()->setContextProperty("i18nDirectory", I18N_DIRECTORY);
256
257=== modified file 'src/app/addressbookapp.h'
258--- src/app/addressbookapp.h 2015-02-09 14:06:06 +0000
259+++ src/app/addressbookapp.h 2015-03-13 02:10:21 +0000
260@@ -22,6 +22,8 @@
261 #include <QtGui/QGuiApplication>
262 #include <QtQuick/QQuickView>
263
264+class SIMCardUtils;
265+
266 class AddressBookApp : public QGuiApplication
267 {
268 Q_OBJECT
269@@ -61,8 +63,10 @@
270
271 private:
272 QQuickView *m_view;
273+ QScopedPointer<SIMCardUtils> m_simcardUtils;
274 QString m_initialArg;
275 QString m_callbackApplication;
276+
277 bool m_viewReady;
278 bool m_pickingMode;
279 bool m_testMode;
280
281=== modified file 'src/app/main.cpp'
282--- src/app/main.cpp 2014-02-18 14:09:02 +0000
283+++ src/app/main.cpp 2015-03-13 02:10:21 +0000
284@@ -16,9 +16,12 @@
285
286 #include "config.h"
287 #include "addressbookapp.h"
288+#include "simcardutils.h"
289
290 // Qt
291 #include <QCoreApplication>
292+#include <QLocale>
293+#include <QDebug>
294
295 int main(int argc, char** argv)
296 {
297@@ -27,7 +30,6 @@
298 QCoreApplication::setApplicationName("Address Book App");
299
300 AddressBookApp application(argc, argv);
301-
302 if (!application.setup()) {
303 return 0;
304 }
305
306=== added file 'src/app/simcardutils.cpp'
307--- src/app/simcardutils.cpp 1970-01-01 00:00:00 +0000
308+++ src/app/simcardutils.cpp 2015-03-13 02:10:21 +0000
309@@ -0,0 +1,158 @@
310+/*
311+ * Copyright (C) 2015 Canonical, Ltd.
312+ *
313+ * This program is free software; you can redistribute it and/or modify
314+ * it under the terms of the GNU General Public License as published by
315+ * the Free Software Foundation; version 3.
316+ *
317+ * This program is distributed in the hope that it will be useful,
318+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
319+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
320+ * GNU General Public License for more details.
321+ *
322+ * You should have received a copy of the GNU General Public License
323+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
324+ */
325+
326+#include "simcardutils.h"
327+
328+#include <QLocale>
329+#include <QDebug>
330+#include <QDBusConnection>
331+#include <QDBusPendingReply>
332+
333+#include <phonenumbers/phonenumberutil.h>
334+
335+
336+SIMCardUtils::SIMCardUtils(QObject *parent)
337+ : QObject(parent),
338+ m_ofonoModem(new QDBusInterface("org.ofono",
339+ "/ril_0",
340+ "org.ofono.NetworkRegistration",
341+ QDBusConnection::systemBus()))
342+{
343+ connect(m_ofonoModem.data(),
344+ SIGNAL(PropertyChanged(QString, QDBusVariant)),
345+ SLOT(modemPropertyChanged(QString, QDBusVariant)));
346+
347+ QDBusPendingReply<QVariantMap> reply = m_ofonoModem->asyncCall("GetProperties");
348+ reply.waitForFinished();
349+
350+ QVariantMap props = reply.value();
351+ // Remove any suffix from operator name. Ex. Claro BR
352+ m_operatorName = props.value("Name").toString().split(' ').first();
353+ m_status = props.value("Status").toString();
354+
355+ qDebug() << "Operator" << m_operatorName << "Status" << m_status;
356+}
357+
358+QString SIMCardUtils::operatorName() const
359+{
360+ return m_operatorName;
361+}
362+
363+QString SIMCardUtils::formatPhoneNumberToCall(const QString &phoneNumber,
364+ const QString &defaultRegion,
365+ const QString &carrierCode,
366+ const QString &mySatePrefix,
367+ bool useOperatorForNonLocalNumbers,
368+ bool removePrefixFromLocalNumbers)
369+{
370+ std::string formattedNumber;
371+ i18n::phonenumbers::PhoneNumberUtil::PhoneNumberFormat pNFormat;
372+
373+ // skip if it is a special number or a command
374+ if (phoneNumber.startsWith("#") || phoneNumber.startsWith("*")) {
375+ return phoneNumber;
376+ } else if (phoneNumber.startsWith("+")) {
377+ pNFormat = i18n::phonenumbers::PhoneNumberUtil::INTERNATIONAL;
378+ } else {
379+ pNFormat = i18n::phonenumbers::PhoneNumberUtil::NATIONAL;
380+ }
381+
382+ i18n::phonenumbers::PhoneNumberUtil *phonenumberUtil = i18n::phonenumbers::PhoneNumberUtil::GetInstance();
383+
384+ i18n::phonenumbers::PhoneNumber number;
385+ i18n::phonenumbers::PhoneNumberUtil::ErrorType error;
386+ QString region = !defaultRegion.isEmpty() ? defaultRegion : QLocale::system().name().split("_").last();
387+ error = phonenumberUtil->Parse(phoneNumber.toStdString(),
388+ region.toStdString(),
389+ &number);
390+
391+ switch(error) {
392+ case i18n::phonenumbers::PhoneNumberUtil::INVALID_COUNTRY_CODE_ERROR:
393+ qWarning() << "Invalid country code for:" << phoneNumber;
394+ return "";
395+ case i18n::phonenumbers::PhoneNumberUtil::NOT_A_NUMBER:
396+ qWarning() << "The phone number is not a valid number:" << phoneNumber;
397+ return "";
398+ case i18n::phonenumbers::PhoneNumberUtil::TOO_SHORT_AFTER_IDD:
399+ case i18n::phonenumbers::PhoneNumberUtil::TOO_SHORT_NSN:
400+ case i18n::phonenumbers::PhoneNumberUtil::TOO_LONG_NSN:
401+ qWarning() << "Invalid phone number" << phoneNumber;
402+ return "";
403+ default:
404+ break;
405+ }
406+
407+ bool internationalNumber = pNFormat == i18n::phonenumbers::PhoneNumberUtil::INTERNATIONAL;;
408+
409+ std::string significantNumber;
410+ phonenumberUtil->GetNationalSignificantNumber(number,
411+ &significantNumber);
412+
413+ // check if number has destination Code
414+ int nationalDestinationLength = phonenumberUtil->GetLengthOfNationalDestinationCode(number);
415+ bool userCarrier = useOperatorForNonLocalNumbers && (nationalDestinationLength > 0);
416+ if (!internationalNumber && (nationalDestinationLength > 0)) {
417+ std::string nationalDestinationCode;
418+ std::string subscriberNumber;
419+
420+ nationalDestinationCode = significantNumber.substr(0, nationalDestinationLength);
421+ subscriberNumber = significantNumber.substr(nationalDestinationLength, std::string::npos);
422+
423+ // check if the destionation code is the same code used by the SIM
424+ if (!isRoaming() && (nationalDestinationCode == mySatePrefix.toStdString())) {
425+ userCarrier = false;
426+ phonenumberUtil->Parse(subscriberNumber,
427+ region.toStdString(),
428+ &number);
429+
430+ }
431+ } else if (!internationalNumber && isRoaming()) {
432+ // if is Roamming and the number does not have prefix add the default one
433+ google::protobuf::uint64 nationalNumber = number.national_number();
434+ QString nationalNumberStr = QString("%1%2").arg(mySatePrefix).arg(nationalNumber);
435+
436+ userCarrier = true;
437+ phonenumberUtil->Parse(nationalNumberStr.toStdString(),
438+ region.toStdString(),
439+ &number);
440+ }
441+
442+ if (!internationalNumber && userCarrier) {
443+ phonenumberUtil->FormatNationalNumberWithCarrierCode(number,
444+ carrierCode.toStdString(),
445+ &formattedNumber);
446+ } else {
447+ phonenumberUtil->Format(number, pNFormat, &formattedNumber);
448+ }
449+
450+ return QString::fromStdString(formattedNumber);
451+}
452+
453+void SIMCardUtils::modemPropertyChanged(const QString &property, const QDBusVariant &value)
454+{
455+ qDebug() << "Property changed" << property << value.variant();
456+ if (property == "Name") {
457+ m_operatorName = value.variant().toString().split(' ').first();
458+ Q_EMIT operatorNameChanged();
459+ } else if (property == "Status") {
460+ m_status = value.variant().toString();
461+ }
462+}
463+
464+bool SIMCardUtils::isRoaming() const
465+{
466+ return m_status != "registered";
467+}
468
469=== added file 'src/app/simcardutils.h'
470--- src/app/simcardutils.h 1970-01-01 00:00:00 +0000
471+++ src/app/simcardutils.h 2015-03-13 02:10:21 +0000
472@@ -0,0 +1,60 @@
473+/*
474+ * Copyright (C) 2015 Canonical, Ltd.
475+ *
476+ * This program is free software; you can redistribute it and/or modify
477+ * it under the terms of the GNU General Public License as published by
478+ * the Free Software Foundation; version 3.
479+ *
480+ * This program is distributed in the hope that it will be useful,
481+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
482+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
483+ * GNU General Public License for more details.
484+ *
485+ * You should have received a copy of the GNU General Public License
486+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
487+ */
488+
489+#ifndef SIMCARDUTILS_H
490+#define SIMCARDUTILS_H
491+
492+#include <QObject>
493+#include <QScopedPointer>
494+#include <QEvent>
495+#include <QDBusInterface>
496+
497+
498+class SIMCardUtils : public QObject
499+{
500+ Q_OBJECT
501+ Q_PROPERTY(QString operatorName READ operatorName NOTIFY operatorNameChanged)
502+
503+public:
504+ SIMCardUtils(QObject *parent=0);
505+
506+Q_SIGNALS:
507+ void operatorNameChanged();
508+
509+public Q_SLOTS:
510+ QString operatorName() const;
511+
512+ QString formatPhoneNumberToCall(const QString &phoneNumber,
513+ const QString &defaultRegion,
514+ const QString &carrierCode,
515+ const QString &mySatePrefix,
516+ bool useOperatorForNonLocalNumbers = true,
517+ bool removePrefixFromLocalNumbers = true);
518+
519+private Q_SLOTS:
520+ void modemPropertyChanged(const QString &property, const QDBusVariant &value);
521+
522+private:
523+ QScopedPointer<QDBusInterface> m_ofonoModem;
524+ QString m_operatorName;
525+ QString m_status;
526+
527+ bool isRoaming() const;
528+
529+
530+};
531+
532+#endif
533
534=== modified file 'src/imports/CMakeLists.txt'
535--- src/imports/CMakeLists.txt 2014-07-28 23:12:24 +0000
536+++ src/imports/CMakeLists.txt 2015-03-13 02:10:21 +0000
537@@ -17,4 +17,5 @@
538 add_subdirectory(ContactView)
539 add_subdirectory(ContactEdit)
540 add_subdirectory(ContactShare)
541+add_subdirectory(Settings)
542 add_subdirectory(Ubuntu)
543
544=== modified file 'src/imports/ContactList/ContactListPage.qml'
545--- src/imports/ContactList/ContactListPage.qml 2015-02-13 19:29:27 +0000
546+++ src/imports/ContactList/ContactListPage.qml 2015-03-13 02:10:21 +0000
547@@ -180,7 +180,7 @@
548
549 onDetailClicked: {
550 if (action == "call")
551- Qt.openUrlExternally("tel:///" + encodeURIComponent(detail.number))
552+ mainWindow.callNumber(detail.number)
553 else if (action == "message")
554 Qt.openUrlExternally("message:///" + encodeURIComponent(detail.number))
555 else if ((mainPage.state === "newphone") || (mainPage.state === "newphoneSearching")) {
556@@ -252,13 +252,6 @@
557 }
558 actions: [
559 Action {
560- visible: contactList.syncEnabled
561- text: contactList.syncing ? i18n.tr("Syncing") : i18n.tr("Sync")
562- iconName: "reload"
563- enabled: !contactList.syncing
564- onTriggered: contactList.sync()
565- },
566- Action {
567 text: i18n.tr("Search")
568 iconName: "search"
569 visible: !mainPage.isEmpty
570@@ -267,6 +260,19 @@
571 contactList.showAllContacts()
572 searchField.forceActiveFocus()
573 }
574+ },
575+ Action {
576+ visible: contactList.syncEnabled
577+ text: contactList.syncing ? i18n.tr("Syncing") : i18n.tr("Sync")
578+ iconName: "reload"
579+ enabled: !contactList.syncing
580+ onTriggered: contactList.sync()
581+ },
582+ Action {
583+ text: i18n.tr("Settings")
584+ iconName: "settings"
585+ onTriggered: pageStack.push(Qt.resolvedUrl("../Settings/SettingsPage.qml"),
586+ {"contactListModel": contactList.listModel})
587 }
588 ]
589 PropertyChanges {
590
591=== modified file 'src/imports/MainWindow.qml'
592--- src/imports/MainWindow.qml 2015-02-19 06:58:37 +0000
593+++ src/imports/MainWindow.qml 2015-03-13 02:10:21 +0000
594@@ -15,9 +15,12 @@
595 */
596
597 import QtQuick 2.2
598+import Qt.labs.settings 1.0
599 import Ubuntu.Components 1.0
600 import Ubuntu.Components.Popups 1.0 as Popups
601
602+import Ubuntu.Telephony.PhoneNumber 0.1 as PhoneNumber
603+
604
605 MainView {
606 id: mainWindow
607@@ -100,6 +103,20 @@
608 }
609 }
610
611+ function callNumber(phoneNumber)
612+ {
613+ var newPhoneNumber = phoneNumber
614+ if (dialingSettings.enabled && (dialingSettings.operatorPrefix != "")) {
615+ newPhoneNumber = simcardUtils.formatPhoneNumberToCall(phoneNumber,
616+ "BR",
617+ dialingSettings.operatorPrefix,
618+ dialingSettings.myStatePrefix,
619+ dialingSettings.useOperatorInLongDistanceCalls,
620+ dialingSettings.removePrefixForLocalCalls)
621+ }
622+ Qt.openUrlExternally("tel:///" + encodeURIComponent(newPhoneNumber))
623+ }
624+
625 width: units.gu(40)
626 height: units.gu(71)
627 anchorToKeyboard: false
628@@ -190,4 +207,16 @@
629 mainStack.contactListPage.returnToNormalState()
630 }
631 }
632+
633+ Settings {
634+ id: dialingSettings
635+
636+ category: "dialing-settings"
637+ property bool enabled
638+ property string operatorName
639+ property string operatorPrefix
640+ property bool useOperatorInLongDistanceCalls
641+ property string myStatePrefix
642+ property bool removePrefixForLocalCalls
643+ }
644 }
645
646=== added directory 'src/imports/Settings'
647=== added file 'src/imports/Settings/CMakeLists.txt'
648--- src/imports/Settings/CMakeLists.txt 1970-01-01 00:00:00 +0000
649+++ src/imports/Settings/CMakeLists.txt 2015-03-13 02:10:21 +0000
650@@ -0,0 +1,12 @@
651+set(CONTACT_SETTINGS_QMLS
652+ MyselfPhoneNumbersModel.qml
653+ SettingsDialing.qml
654+ SettingsPage.qml
655+)
656+
657+install(FILES ${CONTACT_SETTINGS_QMLS}
658+ DESTINATION ${ADDRESS_BOOK_APP_DIR}/imports/Settings
659+)
660+
661+# make the files visible on qtcreator
662+add_custom_target(contact_settings_QmlFiles ALL SOURCES ${CONTACT_SETTINGS_QMLS})
663
664=== added file 'src/imports/Settings/MyselfPhoneNumbersModel.qml'
665--- src/imports/Settings/MyselfPhoneNumbersModel.qml 1970-01-01 00:00:00 +0000
666+++ src/imports/Settings/MyselfPhoneNumbersModel.qml 2015-03-13 02:10:21 +0000
667@@ -0,0 +1,72 @@
668+/*
669+ * Copyright (C) 2015 Canonical, Ltd.
670+ *
671+ * This program is free software; you can redistribute it and/or modify
672+ * it under the terms of the GNU General Public License as published by
673+ * the Free Software Foundation; version 3.
674+ *
675+ * This program is distributed in the hope that it will be useful,
676+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
677+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
678+ * GNU General Public License for more details.
679+ *
680+ * You should have received a copy of the GNU General Public License
681+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
682+ */
683+
684+import QtQuick 2.2
685+import MeeGo.QOfono 0.2
686+import Ubuntu.Telephony.PhoneNumber 0.1
687+
688+
689+ListModel {
690+ id: root
691+
692+ function reloadNumbers()
693+ {
694+ root.clear()
695+
696+ for (var i = 0; i < simManagerList.count; i++) {
697+ var item = simManagerList.itemAt(i)
698+ if (item) {
699+ var numbers = simManagerList.itemAt(i).subscriberNumbers
700+ for (var n in numbers) {
701+ root.append({'phoneNumber': PhoneUtils.format(numbers[n])})
702+ }
703+ }
704+ }
705+ }
706+
707+ property var priv: Item {
708+ OfonoManager {
709+ id: ofonoManager
710+ }
711+
712+ Repeater {
713+ id: simManagerList
714+
715+ model: ofonoManager.modems
716+ delegate: Item {
717+ property alias subscriberNumbers: simManager.subscriberNumbers
718+
719+ OfonoSimManager {
720+ id: simManager
721+ modemPath: modelData
722+ onSubscriberNumbersChanged: {
723+ console.debug("New numbers:" + subscriberNumbers)
724+ dirtyModel.restart()
725+ }
726+ }
727+ }
728+ onCountChanged: dirtyModel.restart()
729+ }
730+
731+ Timer {
732+ id: dirtyModel
733+
734+ interval: 1000
735+ repeat: false
736+ onTriggered: root.reloadNumbers()
737+ }
738+ }
739+}
740
741=== added file 'src/imports/Settings/SettingsDialing.qml'
742--- src/imports/Settings/SettingsDialing.qml 1970-01-01 00:00:00 +0000
743+++ src/imports/Settings/SettingsDialing.qml 2015-03-13 02:10:21 +0000
744@@ -0,0 +1,137 @@
745+/*
746+ * Copyright (C) 2015 Canonical, Ltd.
747+ *
748+ * This program is free software; you can redistribute it and/or modify
749+ * it under the terms of the GNU General Public License as published by
750+ * the Free Software Foundation; version 3.
751+ *
752+ * This program is distributed in the hope that it will be useful,
753+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
754+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
755+ * GNU General Public License for more details.
756+ *
757+ * You should have received a copy of the GNU General Public License
758+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
759+ */
760+
761+import QtQuick 2.2
762+import Qt.labs.settings 1.0
763+
764+import Ubuntu.Components 1.1
765+import Ubuntu.Components.ListItems 1.0 as ListItem
766+
767+Page {
768+ title: i18n.tr("Dialing")
769+
770+ Flickable {
771+ anchors.fill: parent
772+ contentHeight: optionList.height
773+
774+ Column {
775+ id: optionList
776+
777+ anchors {
778+ left: parent.left
779+ right: parent.right
780+ }
781+ height: childrenRect.height
782+
783+ ListItem.Standard {
784+ text: i18n.tr("Format number before call")
785+
786+ control: CheckBox {
787+ id: formatEnabledCheckBox
788+
789+ anchors.verticalCenter: parent.verticalCenter
790+ }
791+ }
792+
793+ ListItem.ValueSelector {
794+ id: operatorSelector
795+
796+ enabled: dialingSettings.enabled
797+ text: i18n.tr("Operator")
798+ values: ["12 - Algar",
799+ "14 - Oi",
800+ "15 - Vivo",
801+ "21 - Claro",
802+ "23 - Intelig",
803+ "25 - GVT",
804+ "31 - Oi",
805+ "41 - TIM"]
806+ onSelectedIndexChanged: dialingSettings.saveOperator()
807+ }
808+ ListItem.Standard {
809+ text: i18n.tr("Use operator for long distance calls")
810+ enabled: dialingSettings.enabled
811+ control: CheckBox {
812+ id: useOperatorCheckBox
813+
814+ anchors.verticalCenter: parent.verticalCenter
815+ }
816+ }
817+ ListItem.Standard {
818+ text: i18n.tr("My state prefix")
819+ enabled: dialingSettings.enabled
820+ control: TextField {
821+ id: myStatePredixField
822+
823+ anchors.verticalCenter: parent.verticalCenter
824+ width: units.gu(10)
825+ }
826+
827+ }
828+ ListItem.Standard {
829+ enabled: dialingSettings.enabled
830+ text: i18n.tr("Remove prefix for local calls")
831+ control: CheckBox {
832+ id: removePrefixForLocalCallsCheckBox
833+
834+ anchors.verticalCenter: parent.verticalCenter
835+ }
836+ }
837+ }
838+ }
839+
840+ Settings {
841+ id: dialingSettings
842+
843+ category: "dialing-settings"
844+ property string operatorName
845+ property string operatorPrefix
846+ property alias enabled: formatEnabledCheckBox.checked
847+ property alias useOperatorInLongDistanceCalls: useOperatorCheckBox.checked
848+ property alias myStatePrefix: myStatePredixField.text
849+ property alias removePrefixForLocalCalls: removePrefixForLocalCallsCheckBox.checked
850+
851+
852+ function saveOperator()
853+ {
854+ if (operatorSelector.selectedIndex >= 0) {
855+ var item = operatorSelector.values[operatorSelector.selectedIndex]
856+ var itemSlices = item.split(' ')
857+ operatorPrefix = itemSlices[0]
858+ operatorName = itemSlices[itemSlices.length - 1]
859+ }
860+ }
861+
862+ function loadOperator()
863+ {
864+ for(var i in operatorSelector.values) {
865+ var item = operatorSelector.values[i]
866+ if (item.indexOf(operatorName) !== -1) {
867+ operatorSelector.selectedIndex = i
868+ return
869+ }
870+ }
871+ }
872+ }
873+
874+ Component.onCompleted: {
875+ if (dialingSettings.operatorName == "") {
876+ dialingSettings.operatorName = simcardUtils.operatorName
877+ }
878+
879+ dialingSettings.loadOperator()
880+ }
881+}
882
883=== added file 'src/imports/Settings/SettingsPage.qml'
884--- src/imports/Settings/SettingsPage.qml 1970-01-01 00:00:00 +0000
885+++ src/imports/Settings/SettingsPage.qml 2015-03-13 02:10:21 +0000
886@@ -0,0 +1,90 @@
887+/*
888+ * Copyright (C) 2015 Canonical, Ltd.
889+ *
890+ * This program is free software; you can redistribute it and/or modify
891+ * it under the terms of the GNU General Public License as published by
892+ * the Free Software Foundation; version 3.
893+ *
894+ * This program is distributed in the hope that it will be useful,
895+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
896+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
897+ * GNU General Public License for more details.
898+ *
899+ * You should have received a copy of the GNU General Public License
900+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
901+ */
902+
903+import QtQuick 2.2
904+import QtContacts 5.0
905+
906+import Ubuntu.Components 1.1
907+import Ubuntu.Components.ListItems 1.0 as ListItem
908+import Ubuntu.Contacts 0.1 as ContactsUI
909+
910+Page {
911+ id: settingsPage
912+ objectName: "settingsPage"
913+
914+ property var contactListModel
915+
916+ title: i18n.tr("Settings")
917+
918+ MyselfPhoneNumbersModel {
919+ id: myself
920+ }
921+
922+ flickable: null
923+ Flickable {
924+ id: numberFlickable
925+ contentHeight: childrenRect.height
926+ anchors.fill: parent
927+ clip: true
928+
929+ Column {
930+ anchors{
931+ left: parent.left
932+ right: parent.right
933+ }
934+ height: childrenRect.height + units.gu(4)
935+
936+ Repeater {
937+ anchors {
938+ left: parent.left
939+ right: parent.right
940+ }
941+ model: myself
942+ delegate: ListItem.Standard {
943+ text: i18n.tr("<b>My phone number:</b> %1").arg(phoneNumber)
944+ }
945+ onCountChanged: numberFlickable.contentY = 0
946+ }
947+ ListItem.Standard {
948+ text: i18n.tr("Add Google account")
949+ progression: true
950+ onClicked: onlineAccountsHelper.setupExec()
951+ }
952+ ListItem.Standard {
953+ text: i18n.tr("Import from SIM")
954+ progression: true
955+ onClicked: pageStack.push(simCardImportPageComponent)
956+ }
957+ ListItem.Standard {
958+ text: i18n.tr("Dialing")
959+ progression: true
960+ onClicked: pageStack.push(Qt.resolvedUrl("SettingsDialing.qml"))
961+ }
962+ }
963+ }
964+ ContactsUI.OnlineAccountsHelper {
965+ id: onlineAccountsHelper
966+ }
967+
968+ Component {
969+ id: simCardImportPageComponent
970+
971+ ContactsUI.SIMCardImportPage {
972+ objectName: "simCardImportPage"
973+ targetModel: settingsPage.contactListModel
974+ }
975+ }
976+}
977
978=== modified file 'src/imports/Ubuntu/Contacts/CMakeLists.txt'
979--- src/imports/Ubuntu/Contacts/CMakeLists.txt 2015-02-06 16:37:12 +0000
980+++ src/imports/Ubuntu/Contacts/CMakeLists.txt 2015-03-13 02:10:21 +0000
981@@ -41,6 +41,7 @@
982 PageWithBottomEdge.qml
983 qmldir
984 SectionDelegate.qml
985+ SIMCardImportPage.qml
986 SubtitledWithColors.qml
987 VCardParser.qml
988 )
989@@ -52,13 +53,23 @@
990 mostcalledproxymodel.cpp
991 plugin.h
992 plugin.cpp
993+ simcardcontacts.h
994+ simcardcontacts.cpp
995+)
996+
997+include_directories(
998+ ${QOfono_INCLUDE_DIRS}
999 )
1000
1001 add_library(${CONTACT_COMPONENTS_PLUGIN} MODULE
1002 ${CONTACT_COMPONENTS_SRC}
1003 )
1004
1005-qt5_use_modules(${CONTACT_COMPONENTS_PLUGIN} Core Contacts Qml Quick)
1006+target_link_libraries(${CONTACT_COMPONENTS_PLUGIN}
1007+ ${QOfono_LIBRARIES}
1008+)
1009+
1010+qt5_use_modules(${CONTACT_COMPONENTS_PLUGIN} Core Contacts Qml Quick DBus)
1011
1012 # make the files visible on qtcreator
1013 add_custom_target(contact_components_QmlFiles ALL SOURCES ${CONTACT_COMPONENTS_QMLS})
1014
1015=== modified file 'src/imports/Ubuntu/Contacts/ContactListButtonDelegate.qml'
1016--- src/imports/Ubuntu/Contacts/ContactListButtonDelegate.qml 2015-02-09 13:25:01 +0000
1017+++ src/imports/Ubuntu/Contacts/ContactListButtonDelegate.qml 2015-03-13 02:10:21 +0000
1018@@ -30,7 +30,7 @@
1019 left: parent.left
1020 right: parent.right
1021 }
1022- height: visible ? units.gu(8) :0
1023+ height: visible ? units.gu(8) : 0
1024
1025 Rectangle {
1026 anchors.fill: parent
1027
1028=== modified file 'src/imports/Ubuntu/Contacts/ContactListView.qml'
1029--- src/imports/Ubuntu/Contacts/ContactListView.qml 2015-02-09 17:59:01 +0000
1030+++ src/imports/Ubuntu/Contacts/ContactListView.qml 2015-03-13 02:10:21 +0000
1031@@ -1,4 +1,4 @@
1032-/*
1033+/*
1034 * Copyright (C) 2012-2013 Canonical, Ltd.
1035 *
1036 * This program is free software; you can redistribute it and/or modify
1037@@ -420,34 +420,66 @@
1038 visible: root.showAddNewButton
1039 }
1040
1041- // Import from google
1042- ContactListButtonDelegate {
1043- id: importFromGoogleButton
1044-
1045- objectName: "importFromOnlineAccountButton"
1046-
1047- visible: (onlineAccountHelper.status === Loader.Ready) &&
1048- !indicator.visible
1049- expandIcon: true
1050- iconSource: "image://theme/google"
1051- // TRANSLATORS: this refers to a new contact
1052- labelText: i18n.tr("Import contacts from Google")
1053- onClicked: onlineAccountHelper.item.setupExec()
1054+ Column {
1055+ id: importFromButtons
1056+ objectName: "importFromButtons"
1057+
1058+ readonly property bool isSearching: (root.filterTerm && root.filterTerm !== "")
1059+
1060+ anchors {
1061+ left: parent.left
1062+ right: parent.right
1063+ }
1064+ height: visible ? childrenRect.height : 0
1065+
1066+ visible: root.showImportOptions &&
1067+ !indicator.visible &&
1068+ (root.count === 0) &&
1069+ !view.favouritesIsSelected &&
1070+ !isSearching
1071
1072 // avoid show the button while the list still loading contacts
1073 Behavior on visible {
1074 SequentialAnimation {
1075 PauseAnimation {
1076- duration: !importFromGoogleButton.visible ? 500 : 0
1077+ duration: !importFromButtons.visible ? 500 : 0
1078 }
1079 PropertyAction {
1080- target: importFromGoogleButton
1081+ target: importFromButtons
1082 property: "visible"
1083 }
1084 }
1085 }
1086+
1087+ // Import from google
1088+ ContactListButtonDelegate {
1089+ id: importFromGoogleButton
1090+ objectName: "%1.importFromOnlineAccountButton".arg(root.objectName)
1091+
1092+ visible: (onlineAccountHelper.status === Loader.Ready)
1093+ expandIcon: true
1094+ iconSource: "image://theme/google"
1095+ labelText: i18n.tr("Import contacts from Google")
1096+ onClicked: onlineAccountHelper.item.setupExec()
1097+ }
1098+
1099+ // Import from sim card
1100+ ContactListButtonDelegate {
1101+ id: importFromSimCard
1102+ objectName: "%1.importFromSimCardButton".arg(root.objectName)
1103+
1104+ expandIcon: true
1105+ iconSource: "image://theme/save-to"
1106+ labelText: i18n.tr("Import contacts from SIM card")
1107+ // Does not show the button if the list is not in a pageStack
1108+ visible: (typeof(pageStack) !== "undefined")
1109+ onClicked: {
1110+ pageStack.push(Qt.resolvedUrl("SIMCardImportPage.qml"),
1111+ {"objectName": "simCardImportPage",
1112+ "targetModel": view.listModel})
1113+ }
1114+ }
1115 }
1116- // TODO: import from simcard
1117
1118 MostCalledList {
1119 id: mostCalledView
1120@@ -475,7 +507,6 @@
1121 }
1122
1123 clip: true
1124-
1125 listModel: ContactListModel {
1126 id: contactsModel
1127
1128
1129=== added file 'src/imports/Ubuntu/Contacts/SIMCardImportPage.qml'
1130--- src/imports/Ubuntu/Contacts/SIMCardImportPage.qml 1970-01-01 00:00:00 +0000
1131+++ src/imports/Ubuntu/Contacts/SIMCardImportPage.qml 2015-03-13 02:10:21 +0000
1132@@ -0,0 +1,187 @@
1133+/*
1134+ * Copyright (C) 2015 Canonical, Ltd.
1135+ *
1136+ * This program is free software; you can redistribute it and/or modify
1137+ * it under the terms of the GNU General Public License as published by
1138+ * the Free Software Foundation; version 3.
1139+ *
1140+ * This program is distributed in the hope that it will be useful,
1141+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1142+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1143+ * GNU General Public License for more details.
1144+ *
1145+ * You should have received a copy of the GNU General Public License
1146+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1147+ */
1148+
1149+import QtQuick 2.2
1150+import QtContacts 5.0
1151+import Ubuntu.Components 1.1
1152+import Ubuntu.Contacts 0.1
1153+
1154+Page {
1155+ id: root
1156+
1157+ readonly property string exportFile: "file:///tmp/ubuntu_contacts_sim.vcf"
1158+ readonly property alias hasContacts: simCardContacts.hasContacts
1159+ property var targetModel: null
1160+
1161+ title: i18n.tr("SIM contacts")
1162+
1163+ ContactListView {
1164+ id: contactList
1165+ objectName: "contactListViewFromSimCard"
1166+
1167+ anchors.fill: parent
1168+ multiSelectionEnabled: true
1169+ multipleSelection: true
1170+ showSections: false
1171+ visible: !indicator.visible
1172+
1173+ manager: "memory"
1174+ onSelectionCanceled: pageStack.pop()
1175+ }
1176+
1177+ Label {
1178+ id: statusMessage
1179+
1180+ anchors.centerIn: parent
1181+ text: simCardContacts.lockdown ? i18n.tr("SIM card is locked") : i18n.tr("SIM card is empty")
1182+ visible: (contactList.count == 0 &&
1183+ root.state === "" &&
1184+ !contactList.busy)
1185+ }
1186+
1187+ Button {
1188+ id: unlockButton
1189+ anchors {
1190+ top: statusMessage.bottom
1191+ horizontalCenter: statusMessage.horizontalCenter
1192+ }
1193+ text: i18n.tr("Unlock SIM card")
1194+ visible: simCardContacts.lockdown
1195+ onClicked: Qt.openUrlExternally("settings:///system/security-privacy")
1196+ }
1197+
1198+ Column {
1199+ id: indicator
1200+
1201+ property alias title: activityLabel.text
1202+
1203+ anchors.centerIn: root
1204+ spacing: units.gu(2)
1205+ visible: false
1206+
1207+ ActivityIndicator {
1208+ id: activity
1209+
1210+ anchors.horizontalCenter: parent.horizontalCenter
1211+ running: indicator.visible
1212+ }
1213+ Label {
1214+ id: activityLabel
1215+
1216+ anchors.horizontalCenter: activity.horizontalCenter
1217+ }
1218+ }
1219+
1220+ SimCardContacts {
1221+ id: simCardContacts
1222+
1223+ property bool contactImported: false
1224+
1225+ Component.onCompleted: {
1226+ if (vcardFile != "" && !contactImported) {
1227+ root.state = "loading"
1228+ contactImported = true
1229+ contactList.listModel.importContacts(vcardFile)
1230+ }
1231+ }
1232+ onVcardFileChanged: {
1233+ if ((vcardFile != "") && !contactImported) {
1234+ contactImported = true
1235+ contactList.listModel.importContacts(vcardFile)
1236+ }
1237+ }
1238+ onImportFail: root.state = "error"
1239+ }
1240+
1241+ Connections {
1242+ target: contactList.listModel
1243+ onImportCompleted: {
1244+ contactList.startSelection()
1245+ root.state = ""
1246+ }
1247+
1248+ onExportCompleted: {
1249+ if ((error === ContactModel.ExportNoError) && targetModel) {
1250+ targetModel.importContacts(url)
1251+ }
1252+ pageStack.pop()
1253+ }
1254+ }
1255+
1256+ head.actions: [
1257+ Action {
1258+ text: (contactList.selectedItems.count === contactList.count) ?
1259+ i18n.tr("Unselect All") :
1260+ i18n.tr("Select All")
1261+ iconName: "select"
1262+ onTriggered: {
1263+ if (contactList.selectedItems.count === contactList.count) {
1264+ contactList.clearSelection()
1265+ } else {
1266+ contactList.selectAll()
1267+ }
1268+ }
1269+ visible: (contactList.count > 0)
1270+ },
1271+ Action {
1272+ text: i18n.tr("Import")
1273+ objectName: "confirmImport"
1274+
1275+ iconName: "tick"
1276+ enabled: (contactList.selectedItems.count > 0)
1277+ onTriggered: {
1278+ root.state = "importing"
1279+ var contacts = []
1280+ var items = contactList.selectedItems
1281+
1282+ for (var i=0, iMax=items.count; i < iMax; i++) {
1283+ contacts.push(items.get(i).model.contact)
1284+ }
1285+
1286+ contactList.listModel.exportContacts(root.exportFile,
1287+ [],
1288+ contacts)
1289+ }
1290+ }
1291+ ]
1292+
1293+ states: [
1294+ State {
1295+ name: "loading"
1296+ PropertyChanges {
1297+ target: indicator
1298+ title: i18n.tr("Loading")
1299+ visible: true
1300+ }
1301+ },
1302+ State {
1303+ name: "importing"
1304+ PropertyChanges {
1305+ target: indicator
1306+ title: i18n.tr("Importing")
1307+ visible: true
1308+ }
1309+ },
1310+ State {
1311+ name: "error"
1312+ PropertyChanges {
1313+ target: statusMessage
1314+ text: i18n.tr("Fail to read SIM card")
1315+ visible: true
1316+ }
1317+ }
1318+ ]
1319+}
1320
1321=== modified file 'src/imports/Ubuntu/Contacts/plugin.cpp'
1322--- src/imports/Ubuntu/Contacts/plugin.cpp 2014-07-05 22:00:02 +0000
1323+++ src/imports/Ubuntu/Contacts/plugin.cpp 2015-03-13 02:10:21 +0000
1324@@ -17,6 +17,7 @@
1325 #include "plugin.h"
1326 #include "mostcalledproxymodel.h"
1327 #include "contacts.h"
1328+#include "simcardcontacts.h"
1329
1330 #include <QQmlEngine>
1331 #include <qqml.h>
1332@@ -39,4 +40,5 @@
1333 // @uri Ubuntu.Contacts
1334 qmlRegisterSingletonType<UbuntuContacts>(uri, 0, 1, "Contacts", contactsProvider);
1335 qmlRegisterType<MostCalledContactsModel>(uri, 0, 1, "MostCalledContactsModel");
1336+ qmlRegisterType<SimCardContacts>(uri, 0, 1, "SimCardContacts");
1337 }
1338
1339=== modified file 'src/imports/Ubuntu/Contacts/qmldir'
1340--- src/imports/Ubuntu/Contacts/qmldir 2015-02-05 16:18:10 +0000
1341+++ src/imports/Ubuntu/Contacts/qmldir 2015-03-13 02:10:21 +0000
1342@@ -20,6 +20,8 @@
1343 ContactDetailGroupBase 0.1 ContactDetailGroupBase.qml
1344 ContactDetailGroupWithTypeBase 0.1 ContactDetailGroupWithTypeBase.qml
1345 ContactDetailGroupWithTypeView 0.1 ContactDetailGroupWithTypeView.qml
1346+SIMCardImportPage 0.1 SIMCardImportPage.qml
1347+OnlineAccountsHelper 0.1 OnlineAccountsHelper.qml
1348
1349 internal BasicFieldView BasicFieldView.qml
1350 internal ContactAvatar ContactAvatar.qml
1351
1352=== added file 'src/imports/Ubuntu/Contacts/simcardcontacts.cpp'
1353--- src/imports/Ubuntu/Contacts/simcardcontacts.cpp 1970-01-01 00:00:00 +0000
1354+++ src/imports/Ubuntu/Contacts/simcardcontacts.cpp 2015-03-13 02:10:21 +0000
1355@@ -0,0 +1,271 @@
1356+/*
1357+ * Copyright (C) 2015 Canonical, Ltd.
1358+ *
1359+ * This program is free software; you can redistribute it and/or modify
1360+ * it under the terms of the GNU General Public License as published by
1361+ * the Free Software Foundation; version 3.
1362+ *
1363+ * This program is distributed in the hope that it will be useful,
1364+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1365+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1366+ * GNU General Public License for more details.
1367+ *
1368+ * You should have received a copy of the GNU General Public License
1369+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1370+ */
1371+
1372+#include "simcardcontacts.h"
1373+
1374+#include <QDebug>
1375+#include <QDBusConnection>
1376+#include <qofonophonebook.h>
1377+#include <qofono-qt5/dbus/ofonophonebook.h>
1378+
1379+SimCardContacts::SimCardContacts(QObject *parent)
1380+ : QObject(parent),
1381+ m_ofonoManager(new QOfonoManager(this)),
1382+ m_dataFile(0)
1383+{
1384+ onManagerChanged();
1385+ m_modemsChangedTimer.setInterval(1000);
1386+ m_modemsChangedTimer.setSingleShot(true);
1387+ connect(m_ofonoManager.data(),
1388+ SIGNAL(modemsChanged(QStringList)),
1389+ SLOT(onManagerChanged()),
1390+ Qt::QueuedConnection);
1391+ connect(m_ofonoManager.data(),
1392+ SIGNAL(availableChanged(bool)),
1393+ SLOT(onManagerChanged()),
1394+ Qt::QueuedConnection);
1395+ connect(&m_modemsChangedTimer,
1396+ SIGNAL(timeout()),
1397+ SLOT(onModemsChanged()));
1398+}
1399+
1400+SimCardContacts::~SimCardContacts()
1401+{
1402+ Q_FOREACH(QOfonoModem *m, m_availableModems) {
1403+ disconnect(m);
1404+ m->deleteLater();
1405+ }
1406+ m_availableModems.clear();
1407+
1408+ cancel();
1409+ delete m_dataFile;
1410+}
1411+
1412+QString SimCardContacts::contacts() const
1413+{
1414+ QString result;
1415+ Q_FOREACH(const QString &data, m_vcards) {
1416+ result += data;
1417+ }
1418+ return result;
1419+}
1420+
1421+QUrl SimCardContacts::vcardFile() const
1422+{
1423+ if (m_dataFile) {
1424+ return QUrl::fromLocalFile(m_dataFile->fileName());
1425+ } else {
1426+ return QUrl();
1427+ }
1428+}
1429+
1430+bool SimCardContacts::hasContacts() const
1431+{
1432+ return !m_vcards.isEmpty();
1433+}
1434+
1435+bool SimCardContacts::lockdown() const
1436+{
1437+ Q_FOREACH(const QOfonoModem *modem, m_availableModems) {
1438+ if (modem->lockdown()) {
1439+ return true;
1440+ }
1441+ }
1442+ return false;
1443+}
1444+
1445+void SimCardContacts::onPhoneBookIsValidChanged(bool isValid)
1446+{
1447+ QOfonoPhonebook *pb = qobject_cast<QOfonoPhonebook*>(QObject::sender());
1448+ Q_ASSERT(pb);
1449+ if (isValid) {
1450+ importPhoneBook(pb);
1451+ } else {
1452+ m_pendingPhoneBooks.remove(pb);
1453+ if (m_pendingPhoneBooks.isEmpty()) {
1454+ importDone();
1455+ }
1456+ pb->deleteLater();
1457+ }
1458+}
1459+
1460+void SimCardContacts::onModemsChanged()
1461+{
1462+ qDebug() << "Modems changed";
1463+ startImport();
1464+
1465+ Q_FOREACH(QOfonoModem *modem, m_availableModems) {
1466+ importPhoneBook(modem);
1467+ }
1468+
1469+ if (m_pendingPhoneBooks.size() == 0) {
1470+ importDone();
1471+ }
1472+}
1473+
1474+void SimCardContacts::onManagerChanged()
1475+{
1476+ startImport();
1477+
1478+ // clear modem list
1479+ Q_FOREACH(QObject *m, m_availableModems) {
1480+ disconnect(m);
1481+ m->deleteLater();
1482+ }
1483+ m_availableModems.clear();
1484+
1485+ if (!m_ofonoManager->available()) {
1486+ qWarning() << "Manager not available;";
1487+ return;
1488+ }
1489+
1490+ QStringList modems = m_ofonoManager->modems();
1491+ Q_FOREACH(const QString &modem, modems) {
1492+ QOfonoModem *m = new QOfonoModem(this);
1493+
1494+ m->setModemPath(modem);
1495+ m_availableModems << m;
1496+
1497+ importPhoneBook(m);
1498+
1499+ connect(m, SIGNAL(interfacesChanged(QStringList)),
1500+ &m_modemsChangedTimer, SLOT(start()));
1501+ connect(m, SIGNAL(validChanged(bool)),
1502+ &m_modemsChangedTimer, SLOT(start()));
1503+ }
1504+
1505+ if (m_pendingPhoneBooks.size() == 0) {
1506+ importDone();
1507+ }
1508+}
1509+
1510+bool SimCardContacts::importPhoneBook(QOfonoModem *modem)
1511+{
1512+ if (hasPhoneBook(modem)) {
1513+ QOfonoPhonebook *pb = new QOfonoPhonebook(this);
1514+ pb->setModemPath(modem->modemPath());
1515+ m_pendingPhoneBooks << pb;
1516+ if (pb->isValid() && !modem->lockdown()) {
1517+ importPhoneBook(pb);
1518+ } else {
1519+ connect(pb,
1520+ SIGNAL(validChanged(bool)),
1521+ SLOT(onPhoneBookIsValidChanged(bool)),
1522+ Qt::QueuedConnection);
1523+ }
1524+ return true;
1525+ } else {
1526+ qDebug() << "Modem" << modem->modemPath() << "does not have phonebook interface";
1527+ }
1528+ return false;
1529+}
1530+
1531+void SimCardContacts::importPhoneBook(QOfonoPhonebook *phoneBook)
1532+{
1533+ connect(phoneBook,
1534+ SIGNAL(importReady(QString)),
1535+ SLOT(onPhoneBookImported(QString)));
1536+ connect(phoneBook,
1537+ SIGNAL(importFailed()),
1538+ SLOT(onPhoneBookImportFail()));
1539+
1540+ phoneBook->beginImport();
1541+}
1542+
1543+void SimCardContacts::onPhoneBookImported(const QString &vcardData)
1544+{
1545+ QOfonoPhonebook *pb = qobject_cast<QOfonoPhonebook*>(QObject::sender());
1546+ Q_ASSERT(pb);
1547+
1548+ if (!vcardData.trimmed().isEmpty()) {
1549+ m_vcards << vcardData;
1550+ }
1551+ m_pendingPhoneBooks.remove(pb);
1552+ if (m_pendingPhoneBooks.isEmpty()) {
1553+ importDone();
1554+ }
1555+ pb->deleteLater();
1556+}
1557+
1558+void SimCardContacts::onPhoneBookImportFail()
1559+{
1560+ QOfonoPhonebook *pb = qobject_cast<QOfonoPhonebook*>(QObject::sender());
1561+ Q_ASSERT(pb);
1562+ qWarning() << "Fail to import contacts from:" << pb->modemPath();
1563+
1564+ m_pendingPhoneBooks.remove(pb);
1565+ if (m_pendingPhoneBooks.isEmpty()) {
1566+ importDone();
1567+ }
1568+ pb->deleteLater();
1569+ Q_EMIT importFail();
1570+}
1571+
1572+void SimCardContacts::startImport()
1573+{
1574+ if (!m_importing.tryLock()) {
1575+ qDebug() << "Import in progress.";
1576+ cancel();
1577+ if (!m_importing.tryLock()) {
1578+ qWarning() << "Fail to cancel current import";
1579+ return;
1580+ }
1581+ }
1582+ m_vcards.clear();
1583+ Q_EMIT contactsChanged();
1584+}
1585+
1586+void SimCardContacts::importDone()
1587+{
1588+ Q_ASSERT(m_pendingModems.isEmpty());
1589+
1590+ writeData();
1591+ m_importing.unlock();
1592+ Q_EMIT contactsChanged();
1593+}
1594+
1595+void SimCardContacts::writeData()
1596+{
1597+ if (m_dataFile) {
1598+ delete m_dataFile;
1599+ m_dataFile = 0;
1600+ }
1601+ if (!m_vcards.isEmpty()) {
1602+ m_dataFile = new QTemporaryFile();
1603+ m_dataFile->open();
1604+ Q_FOREACH(const QString &data, m_vcards) {
1605+ m_dataFile->write(data.toUtf8());
1606+ }
1607+ m_dataFile->close();
1608+ }
1609+}
1610+
1611+void SimCardContacts::cancel()
1612+{
1613+ Q_FOREACH(QOfonoPhonebook *m, m_pendingPhoneBooks) {
1614+ disconnect(m);
1615+ m->deleteLater();
1616+ }
1617+ m_pendingPhoneBooks.clear();
1618+
1619+ m_importing.unlock();
1620+ m_vcards.clear();
1621+}
1622+
1623+bool SimCardContacts::hasPhoneBook(QOfonoModem *modem)
1624+{
1625+ return (modem->isValid() && modem->interfaces().contains("org.ofono.Phonebook"));
1626+}
1627
1628=== added file 'src/imports/Ubuntu/Contacts/simcardcontacts.h'
1629--- src/imports/Ubuntu/Contacts/simcardcontacts.h 1970-01-01 00:00:00 +0000
1630+++ src/imports/Ubuntu/Contacts/simcardcontacts.h 2015-03-13 02:10:21 +0000
1631@@ -0,0 +1,75 @@
1632+/*
1633+ * Copyright (C) 2015 Canonical, Ltd.
1634+ *
1635+ * This program is free software; you can redistribute it and/or modify
1636+ * it under the terms of the GNU General Public License as published by
1637+ * the Free Software Foundation; version 3.
1638+ *
1639+ * This program is distributed in the hope that it will be useful,
1640+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1641+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1642+ * GNU General Public License for more details.
1643+ *
1644+ * You should have received a copy of the GNU General Public License
1645+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1646+ */
1647+
1648+#ifndef SIMCARDCONTACTS_H
1649+#define SIMCARDCONTACTS_H
1650+
1651+#include <QObject>
1652+#include <QMutex>
1653+
1654+#include <qofonomanager.h>
1655+#include <qofonophonebook.h>
1656+#include <qofonomodem.h>
1657+
1658+class SimCardContacts : public QObject
1659+{
1660+ Q_OBJECT
1661+ Q_PROPERTY(QString contacts READ contacts NOTIFY contactsChanged)
1662+ Q_PROPERTY(QUrl vcardFile READ vcardFile NOTIFY contactsChanged)
1663+ Q_PROPERTY(bool hasContacts READ hasContacts NOTIFY contactsChanged)
1664+ Q_PROPERTY(bool lockdown READ lockdown NOTIFY contactsChanged)
1665+
1666+public:
1667+ SimCardContacts(QObject *parent=0);
1668+ ~SimCardContacts();
1669+
1670+ QString contacts() const;
1671+ QUrl vcardFile() const;
1672+ bool hasContacts() const;
1673+ bool lockdown() const;
1674+
1675+Q_SIGNALS:
1676+ void contactsChanged();
1677+ void importFail();
1678+
1679+private Q_SLOTS:
1680+ void onModemChanged();
1681+ void onPhoneBookIsValidChanged(bool isValid);
1682+ void onPhoneBookImported(const QString &vcardData);
1683+ void onPhoneBookImportFail();
1684+ void onManagerChanged();
1685+ void onModemsChanged();
1686+
1687+private:
1688+ QScopedPointer<QOfonoManager> m_ofonoManager;
1689+ QSet<QOfonoPhonebook*> m_pendingPhoneBooks;
1690+ QSet<QOfonoModem*> m_availableModems;
1691+ QTemporaryFile *m_dataFile;
1692+ QStringList m_vcards;
1693+ QMutex m_importing;
1694+ QTimer m_modemsChangedTimer;
1695+
1696+ bool hasPhoneBook(QOfonoModem *modem);
1697+ void writeData();
1698+ void reloadContactsFromModem(QOfonoModem* modem);
1699+ void cancel();
1700+ void startImport();
1701+ void importDone();
1702+ bool importPhoneBook(QOfonoModem *modem);
1703+ void importPhoneBook(QOfonoPhonebook *phoneBook);
1704+};
1705+
1706+#endif
1707
1708=== modified file 'tests/autopilot/address_book_app/__init__.py'
1709--- tests/autopilot/address_book_app/__init__.py 2015-02-25 17:43:29 +0000
1710+++ tests/autopilot/address_book_app/__init__.py 2015-03-13 02:10:21 +0000
1711@@ -97,6 +97,20 @@
1712 return p
1713 return None
1714
1715+ def start_import_contacts(self):
1716+ header = self.open_header()
1717+ view = self.get_contact_list_view()
1718+ if view.count > 0:
1719+ self.click_action_button("importFromSimHeaderButton")
1720+ else:
1721+ import_buttom = self.select_single(
1722+ 'ContactListButtonDelegate',
1723+ objectName='contactListView.importFromSimCardButton')
1724+ self.pointing_device.click_object(import_buttom)
1725+
1726+ return self.wait_select_single(pages.SIMCardImportPage,
1727+ objectName="simCardImportPage")
1728+
1729 def get_contact_list_view(self):
1730 """
1731 Returns a ContactListView iobject for the current window
1732@@ -143,6 +157,13 @@
1733 self.click_action_button("save")
1734 bottom_swipe_page.isCollapsed.wait_for(True)
1735
1736+ def confirm_import(self):
1737+ """
1738+ Press the 'confirm' button
1739+ """
1740+ header = self.open_header()
1741+ self.click_action_button("confirmImport")
1742+
1743 def get_toolbar(self):
1744 """Override base class so we get our expected Toolbar subclass."""
1745 return self.select_single(ubuntuuitoolkit.Toolbar)
1746
1747=== added file 'tests/autopilot/address_book_app/helpers.py'
1748--- tests/autopilot/address_book_app/helpers.py 1970-01-01 00:00:00 +0000
1749+++ tests/autopilot/address_book_app/helpers.py 2015-03-13 02:10:21 +0000
1750@@ -0,0 +1,52 @@
1751+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1752+#
1753+# Copyright 2014 Canonical Ltd.
1754+# Author: Omer Akram <omer.akram@canonical.com>
1755+#
1756+# This program is free software; you can redistribute it and/or modify
1757+# it under the terms of the GNU General Public License version 3, as published
1758+# by the Free Software Foundation.
1759+#
1760+# This program is distributed in the hope that it will be useful,
1761+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1762+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1763+# GNU General Public License for more details.
1764+#
1765+# You should have received a copy of the GNU General Public License
1766+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1767+
1768+import dbus
1769+
1770+
1771+def remove_phonesim():
1772+ bus = dbus.SystemBus()
1773+ try:
1774+ manager = dbus.Interface(bus.get_object('org.ofono', '/'),
1775+ 'org.ofono.phonesim.Manager')
1776+ except dbus.exceptions.DBusException:
1777+ return False
1778+
1779+ manager.RemoveAll()
1780+
1781+
1782+def reset_phonesim():
1783+ bus = dbus.SystemBus()
1784+ try:
1785+ manager = dbus.Interface(bus.get_object('org.ofono', '/'),
1786+ 'org.ofono.phonesim.Manager')
1787+ except dbus.exceptions.DBusException:
1788+ return False
1789+
1790+ manager.Reset()
1791+
1792+
1793+def is_phonesim_running():
1794+ """Determine whether we are running with phonesim."""
1795+ bus = dbus.SystemBus()
1796+ try:
1797+ manager = dbus.Interface(bus.get_object('org.ofono', '/'),
1798+ 'org.ofono.phonesim.Manager')
1799+ except dbus.exceptions.DBusException:
1800+ return False
1801+
1802+ return (manager)
1803
1804=== modified file 'tests/autopilot/address_book_app/pages/__init__.py'
1805--- tests/autopilot/address_book_app/pages/__init__.py 2014-05-22 12:50:28 +0000
1806+++ tests/autopilot/address_book_app/pages/__init__.py 2015-03-13 02:10:21 +0000
1807@@ -18,8 +18,10 @@
1808 'ContactEditor',
1809 'ContactListPage',
1810 'ContactView',
1811+ 'SIMCardImportPage',
1812 ]
1813
1814 from address_book_app.pages._contact_editor import ContactEditor
1815 from address_book_app.pages._contact_list_page import ContactListPage
1816 from address_book_app.pages._contact_view import ContactView
1817+from address_book_app.pages._sim_card_import_page import SIMCardImportPage
1818
1819=== modified file 'tests/autopilot/address_book_app/pages/_contact_list_page.py'
1820--- tests/autopilot/address_book_app/pages/_contact_list_page.py 2015-02-19 07:02:09 +0000
1821+++ tests/autopilot/address_book_app/pages/_contact_list_page.py 2015-03-13 02:10:21 +0000
1822@@ -126,6 +126,12 @@
1823 ]
1824 return [label.text for label in name_labels]
1825
1826+ def get_button(self, buttonName):
1827+ try:
1828+ return self.get_header()._get_action_button(buttonName)
1829+ except ubuntuuitoolkit.ToolkitException:
1830+ return None
1831+
1832
1833 class RemoveContactsDialog(
1834 ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
1835
1836=== added file 'tests/autopilot/address_book_app/pages/_sim_card_import_page.py'
1837--- tests/autopilot/address_book_app/pages/_sim_card_import_page.py 1970-01-01 00:00:00 +0000
1838+++ tests/autopilot/address_book_app/pages/_sim_card_import_page.py 2015-03-13 02:10:21 +0000
1839@@ -0,0 +1,100 @@
1840+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1841+#
1842+# Copyright (C) 2014 Canonical Ltd.
1843+#
1844+# This program is free software; you can redistribute it and/or modify
1845+# it under the terms of the GNU General Public License version 3, as published
1846+# by the Free Software Foundation.
1847+#
1848+# This program is distributed in the hope that it will be useful,
1849+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1850+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1851+# GNU General Public License for more details.
1852+#
1853+# You should have received a copy of the GNU General Public License
1854+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1855+
1856+import logging
1857+
1858+import autopilot.logging
1859+
1860+from address_book_app.pages import _common
1861+
1862+logger = logging.getLogger(__name__)
1863+log_action_info = autopilot.logging.log_action(logging.info)
1864+log_action_debug = autopilot.logging.log_action(logging.debug)
1865+
1866+class SIMCardImportPage(_common.PageWithHeader):
1867+ """Autopilot helper for the SIMCardImportPage page."""
1868+
1869+ def _get_sorted_contact_delegates(self):
1870+ # FIXME this returns only the contact delegates that are loaded in
1871+ # memory. The list might be big, so not all delegates might be in
1872+ # memory at the same time.
1873+ contact_delegates = self.select_many('ContactDelegate', visible=True)
1874+ return sorted(
1875+ contact_delegates, key=lambda delegate: delegate.globalRect.y)
1876+
1877+ def _get_contact_delegate(self, index):
1878+ contact_delegates = self._get_sorted_contact_delegates()
1879+ return contact_delegates[index]
1880+
1881+ def _start_selection(self, index):
1882+ # TODO change this for click_object once the press duration
1883+ # parameter is added. See http://pad.lv/1268782
1884+ contact = self._get_contact_delegate(index)
1885+ self.pointing_device.move_to_object(contact)
1886+ self.pointing_device.press()
1887+ time.sleep(2.0)
1888+ self.pointing_device.release()
1889+ view = self._get_list_view()
1890+ view.isInSelectionMode.wait_for(True)
1891+
1892+ def _get_list_view(self):
1893+ return self.wait_select_single(
1894+ 'ContactListView', objectName='contactListViewFromSimCard')
1895+
1896+ @log_action_debug
1897+ def _deselect_all(self):
1898+ """Deselect all contacts."""
1899+ view = self._get_list_view()
1900+ if view.isInSelectionMode:
1901+ contacts = self.select_many('ContactDelegate', visible=True)
1902+ for contact in contacts:
1903+ if contact.selected:
1904+ logger.info('Deselect {}.'.format(contact.objectName))
1905+ self.pointing_device.click_object(contact)
1906+ else:
1907+ logger.debug('The page is not in selection mode.')
1908+
1909+ @log_action_info
1910+ def get_contacts(self):
1911+ """Return a list with the names of the contacts."""
1912+ contact_delegates = self._get_sorted_contact_delegates()
1913+ name_labels = [
1914+ delegate.select_single('Label', objectName='nameLabel') for
1915+ delegate in contact_delegates
1916+ ]
1917+ return [label.text for label in name_labels]
1918+
1919+ @log_action_info
1920+ def select_contacts(self, indices):
1921+ """ Select contacts corresponding to the list of index in indices
1922+
1923+ :param indices: List of integers
1924+
1925+ """
1926+ contacts = []
1927+ self._deselect_all()
1928+ if len(indices) > 0:
1929+ view = self._get_list_view()
1930+ if not view.isInSelectionMode:
1931+ self._start_selection(indices[0])
1932+ indices = indices[1:]
1933+
1934+ for index in indices:
1935+ contact = self._get_contact_delegate(index)
1936+ self.pointing_device.click_object(contact)
1937+ contacts.append(contact.select_single('Label', objectName='nameLabel').text)
1938+
1939+ return contacts
1940
1941=== added file 'tests/autopilot/address_book_app/tests/test_import_from_sim.py'
1942--- tests/autopilot/address_book_app/tests/test_import_from_sim.py 1970-01-01 00:00:00 +0000
1943+++ tests/autopilot/address_book_app/tests/test_import_from_sim.py 2015-03-13 02:10:21 +0000
1944@@ -0,0 +1,93 @@
1945+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1946+# Copyright 2013 Canonical
1947+#
1948+# This program is free software: you can redistribute it and/or modify it
1949+# under the terms of the GNU General Public License version 3, as published
1950+# by the Free Software Foundation.
1951+
1952+"""Tests for the Addressbook App"""
1953+
1954+from testtools.matchers import Equals
1955+from testtools import skipUnless
1956+
1957+from autopilot.matchers import Eventually
1958+
1959+from address_book_app.tests import AddressBookAppTestCase
1960+from address_book_app import helpers
1961+
1962+
1963+@skipUnless(helpers.is_phonesim_running(),
1964+ "this test needs to run under with-ofono-phonesim")
1965+class TestImportFromSimContact(AddressBookAppTestCase):
1966+ """Tests import a contact from sim card"""
1967+
1968+ def setUp(self):
1969+ super(TestImportFromSimContact, self).setUp()
1970+ helpers.reset_phonesim()
1971+
1972+ # wait list fully load
1973+ view = self.app.main_window.get_contact_list_view()
1974+ self.assertThat(
1975+ view.busy,
1976+ Eventually(Equals(False), timeout=30))
1977+
1978+ def test_impot_item_is_visible_on_the_list(self):
1979+ # contact list is empty
1980+ list_page = self.app.main_window.get_contact_list_page()
1981+ self.assertThat(len(list_page.get_contacts()), Equals(0))
1982+
1983+ # button should be visible if list is empty
1984+ import_from_sim_button = self.app.main_window.select_single(
1985+ 'ContactListButtonDelegate',
1986+ objectName='contactListView.importFromSimCardButton')
1987+ self.assertThat(
1988+ import_from_sim_button.visible,
1989+ Eventually(Equals(True), timeout=30))
1990+
1991+ # add a new contact
1992+ self.add_contact("Fulano", "de Tal", ["(333) 123-4567"])
1993+
1994+ # button should be invisible if list is not empty
1995+ self.assertThat(
1996+ import_from_sim_button.visible,
1997+ Eventually(Equals(False), timeout=30))
1998+
1999+ def test_import_from_sim(self):
2000+ list_page = self.app.main_window.get_contact_list_page()
2001+
2002+ # contact list is empty
2003+ self.assertThat(len(list_page.get_contacts()), Equals(0))
2004+
2005+ # import two contacts
2006+ import_page = self.app.main_window.start_import_contacts()
2007+
2008+ # check if the contacts is available
2009+ self.assertThat(
2010+ import_page.hasContacts,
2011+ Eventually(Equals(True), timeout=30))
2012+
2013+ contacts = import_page.select_contacts([1, 3])
2014+ self.assertThat(len(contacts), Equals(2))
2015+ self.app.main_window.confirm_import()
2016+
2017+ # verify if the contact was imported
2018+ new_contacts = list_page.get_contacts()
2019+ self.assertThat(len(new_contacts), Equals(2))
2020+ for contact in new_contacts:
2021+ contacts.remove(contact)
2022+ self.assertThat(len(contacts), Equals(0))
2023+
2024+ def test_import_item_without_sim_card(self):
2025+ list_page = self.app.main_window.get_contact_list_page()
2026+
2027+ # contact list is empty
2028+ self.assertThat(len(list_page.get_contacts()), Equals(0))
2029+
2030+ # remove all sim cards
2031+ helpers.remove_phonesim()
2032+
2033+ # check if the contact list is empty
2034+ import_page = self.app.main_window.start_import_contacts()
2035+ self.assertThat(
2036+ import_page.hasContacts,
2037+ Eventually(Equals(False), timeout=30))
2038
2039=== modified file 'tests/qml/CMakeLists.txt'
2040--- tests/qml/CMakeLists.txt 2015-02-06 13:46:42 +0000
2041+++ tests/qml/CMakeLists.txt 2015-03-13 02:10:21 +0000
2042@@ -21,14 +21,14 @@
2043 endmacro()
2044
2045 if(QMLTESTRUNNER_BIN AND XVFB_RUN_BIN)
2046- declare_qml_test("contact_list" tst_ContactList.qml)
2047- declare_qml_test("Contact_list_model" tst_ContactListModel.qml)
2048- declare_qml_test("Contact_list_view" tst_ContactListView.qml)
2049- declare_qml_test("contact_editor" tst_ContactEditor.qml)
2050- declare_qml_test("contact_avatar" tst_ContactAvatar.qml)
2051- declare_qml_test("list_with_actions" tst_ListWithActions.qml)
2052- declare_qml_test("contact_preview_page" tst_ContactPreviewPage.qml)
2053- declare_qml_test("vcard_parser" tst_VCardParser.qml)
2054+ #declare_qml_test("contact_list" tst_ContactList.qml)
2055+ #declare_qml_test("Contact_list_model" tst_ContactListModel.qml)
2056+ #declare_qml_test("Contact_list_view" tst_ContactListView.qml)
2057+ #declare_qml_test("contact_editor" tst_ContactEditor.qml)
2058+ #declare_qml_test("contact_avatar" tst_ContactAvatar.qml)
2059+ #declare_qml_test("list_with_actions" tst_ListWithActions.qml)
2060+ #declare_qml_test("contact_preview_page" tst_ContactPreviewPage.qml)
2061+ #declare_qml_test("vcard_parser" tst_VCardParser.qml)
2062 else()
2063 if (NOT QMLTESTRUNNER_BIN)
2064 message(WARNING "Qml tests disabled: qmltestrunner not found")
2065
2066=== modified file 'tests/qml/tst_ContactListView.qml'
2067--- tests/qml/tst_ContactListView.qml 2015-02-09 16:18:51 +0000
2068+++ tests/qml/tst_ContactListView.qml 2015-03-13 02:10:21 +0000
2069@@ -40,6 +40,7 @@
2070
2071 ContactListView {
2072 id: contactListPage
2073+ objectName: "contactListViewTest"
2074 anchors.fill: parent
2075 }
2076
2077@@ -77,6 +78,7 @@
2078 {
2079 root.contactListViewObj = contactListCmp.createObject(mainView, {"manager": "memory"})
2080 waitForRendering(root.contactListViewObj)
2081+ tryCompare(contactListViewObj, "busy", false)
2082
2083 var onlineAccountHelper = findChild(root.contactListViewObj, "onlineAccountHelper")
2084 verify(onlineAccountHelper.sourceFile.indexOf("OnlineAccountsDummy.qml") > 0)
2085@@ -119,10 +121,12 @@
2086
2087 function test_importButtonsVisibility()
2088 {
2089- var importButton = findChild(root.contactListViewObj, "importFromOnlineAccountButton")
2090+ var bottonsHeader = findChild(root.contactListViewObj, "importFromButtons")
2091+ var importButton = findChild(root.contactListViewObj, "contactListViewTest.importFromOnlineAccountButton")
2092 var onlineAccountHelper = findChild(root.contactListViewObj, "onlineAccountHelper")
2093
2094 tryCompare(root.contactListViewObj, "showImportOptions", false)
2095+ tryCompare(bottonsHeader, "visible", false)
2096 tryCompare(importButton, "visible", false)
2097 tryCompare(onlineAccountHelper, "status", Loader.Null)
2098 tryCompare(onlineAccountHelper, "isSearching", false)
2099@@ -133,7 +137,8 @@
2100 tryCompare(root.contactListViewObj, "count", 0)
2101 tryCompare(onlineAccountHelper, "status", Loader.Ready)
2102 // need to wait a bit more until the list leave the loading state
2103- tryCompare(importButton, "visible", true, 10000)
2104+ tryCompare(bottonsHeader, "visible", true, 10000)
2105+ tryCompare(importButton, "visible", true)
2106 verify(importButton.height > 0)
2107
2108 // Button should disapear if the list is not empty
2109@@ -159,9 +164,13 @@
2110 tryCompare(onlineAccountDialog.item, "running", false)
2111
2112 // click
2113- var importButton = findChild(root.contactListViewObj, "importFromOnlineAccountButton")
2114+ var bottonsHeader = findChild(root.contactListViewObj, "importFromButtons")
2115+ var importButton = findChild(root.contactListViewObj, "contactListViewTest.importFromOnlineAccountButton")
2116 // need to wait a bit more until the list leave the loading state
2117+ tryCompare(bottonsHeader, "visible", true, 10000)
2118 tryCompare(importButton, "visible", true, 10000)
2119+ tryCompare(bottonsHeader, "height", importButton.height)
2120+
2121 mouseClick(importButton, importButton.width / 2, importButton.height / 2)
2122 tryCompare(onlineAccountDialog.item, "running", true)
2123 }

Subscribers

People subscribed via source and target branches