├── images ├── plus.png ├── chevron.png ├── green-plus.png ├── red-minus.png ├── avatar-round.png ├── back-chevron.png ├── blue-bubble.png ├── blue-handle.png ├── text-edit-x.png ├── black-statusbar.png ├── blue-checkmark.png ├── magnifying-glass.png ├── share-contact-menu.png └── alphabet-scroller-labels.png ├── settings.js ├── screenshots ├── iOS7-CuteContacts-main-screen.png └── iOS7-contacts-real-main-screen.png ├── components ├── ShareContactMenu.qml ├── IosIconButton.qml ├── AddPhoneLine.qml ├── DimmableTextButton.qml ├── DimmableIconButton.qml ├── AddToFavoritesMenu.qml ├── PhonesBlock.qml ├── IOSTextField.qml ├── TitleBar.qml ├── IOSPage.qml ├── PhoneNumberEditor.qml └── SampleContacts.qml ├── objectiveutils.h ├── .gitignore ├── cutecontacts.pro ├── main.cpp ├── objectiveutils.mm ├── deployment.pri ├── pages ├── NotYetImplemented.qml ├── ListSelectorPage.qml ├── NewContactPage.qml ├── ContactViewPage.qml └── MainPage.qml ├── README.md ├── qml.qrc ├── main.qml └── LICENSE /images/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amarchen/CuteContacts-iOS/HEAD/images/plus.png -------------------------------------------------------------------------------- /images/chevron.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amarchen/CuteContacts-iOS/HEAD/images/chevron.png -------------------------------------------------------------------------------- /settings.js: -------------------------------------------------------------------------------- 1 | .pragma library 2 | 3 | var pne_height = 42; 4 | var colorActiveBlue = "#0079ff" 5 | -------------------------------------------------------------------------------- /images/green-plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amarchen/CuteContacts-iOS/HEAD/images/green-plus.png -------------------------------------------------------------------------------- /images/red-minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amarchen/CuteContacts-iOS/HEAD/images/red-minus.png -------------------------------------------------------------------------------- /images/avatar-round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amarchen/CuteContacts-iOS/HEAD/images/avatar-round.png -------------------------------------------------------------------------------- /images/back-chevron.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amarchen/CuteContacts-iOS/HEAD/images/back-chevron.png -------------------------------------------------------------------------------- /images/blue-bubble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amarchen/CuteContacts-iOS/HEAD/images/blue-bubble.png -------------------------------------------------------------------------------- /images/blue-handle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amarchen/CuteContacts-iOS/HEAD/images/blue-handle.png -------------------------------------------------------------------------------- /images/text-edit-x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amarchen/CuteContacts-iOS/HEAD/images/text-edit-x.png -------------------------------------------------------------------------------- /images/black-statusbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amarchen/CuteContacts-iOS/HEAD/images/black-statusbar.png -------------------------------------------------------------------------------- /images/blue-checkmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amarchen/CuteContacts-iOS/HEAD/images/blue-checkmark.png -------------------------------------------------------------------------------- /images/magnifying-glass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amarchen/CuteContacts-iOS/HEAD/images/magnifying-glass.png -------------------------------------------------------------------------------- /images/share-contact-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amarchen/CuteContacts-iOS/HEAD/images/share-contact-menu.png -------------------------------------------------------------------------------- /images/alphabet-scroller-labels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amarchen/CuteContacts-iOS/HEAD/images/alphabet-scroller-labels.png -------------------------------------------------------------------------------- /screenshots/iOS7-CuteContacts-main-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amarchen/CuteContacts-iOS/HEAD/screenshots/iOS7-CuteContacts-main-screen.png -------------------------------------------------------------------------------- /screenshots/iOS7-contacts-real-main-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amarchen/CuteContacts-iOS/HEAD/screenshots/iOS7-contacts-real-main-screen.png -------------------------------------------------------------------------------- /components/ShareContactMenu.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | Image { 4 | source: "../images/share-contact-menu.png" 5 | height: sourceSize.height / 2 6 | } 7 | -------------------------------------------------------------------------------- /objectiveutils.h: -------------------------------------------------------------------------------- 1 | #ifndef OBJECTIVEUTILS_H 2 | #define OBJECTIVEUTILS_H 3 | 4 | #include 5 | 6 | class ObjectiveUtils : public QObject 7 | { 8 | Q_OBJECT 9 | 10 | public: 11 | static void setGoodStatusBarStyle(); 12 | }; 13 | 14 | #endif // OBJECTIVEUTILS_H 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # C++ objects and libs 2 | 3 | *.slo 4 | *.lo 5 | *.o 6 | *.a 7 | *.la 8 | *.lai 9 | *.so 10 | *.dll 11 | *.dylib 12 | 13 | # Qt-es 14 | 15 | /.qmake.cache 16 | /.qmake.stash 17 | *.pro.user 18 | *.pro.user.* 19 | *.moc 20 | moc_*.cpp 21 | qrc_*.cpp 22 | ui_*.h 23 | Makefile* 24 | *-build-* 25 | 26 | # QtCreator 27 | 28 | *.autosave 29 | -------------------------------------------------------------------------------- /cutecontacts.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | 3 | QT += qml quick widgets 4 | 5 | SOURCES += main.cpp 6 | 7 | RESOURCES += qml.qrc 8 | 9 | # Additional import path used to resolve QML modules in Qt Creator's code model 10 | QML_IMPORT_PATH = 11 | 12 | # Default rules for deployment. 13 | include(deployment.pri) 14 | 15 | 16 | OBJECTIVE_SOURCES += \ 17 | objectiveutils.mm 18 | 19 | HEADERS += \ 20 | objectiveutils.h 21 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "objectiveutils.h" 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | ObjectiveUtils::setGoodStatusBarStyle(); 11 | QApplication app(argc, argv); 12 | QQuickView view; 13 | view.setResizeMode(QQuickView::SizeRootObjectToView); 14 | view.setSource(QUrl(QStringLiteral("qrc:///main.qml"))); 15 | view.showFullScreen(); 16 | 17 | return app.exec(); 18 | } 19 | -------------------------------------------------------------------------------- /objectiveutils.mm: -------------------------------------------------------------------------------- 1 | #include "objectiveutils.h" 2 | #include 3 | 4 | // This method is supposed to paint system status on top of the app's rectangle, but somehow it doesn't 5 | // Oh well, for a prototype just painting a picture instead of a real statusbar is okay 6 | void ObjectiveUtils::setGoodStatusBarStyle() 7 | { 8 | [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; // white text 9 | [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; // default black text 10 | } 11 | -------------------------------------------------------------------------------- /components/IosIconButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | Image { 4 | id: wholeImage 5 | property alias iconSource: wholeImage.source 6 | signal clicked 7 | sourceSize.width: width 8 | sourceSize.height: height 9 | Rectangle { 10 | id: softener 11 | anchors.fill: parent 12 | color: "white" 13 | opacity: 0.5 14 | visible: wholeImageArea.pressed 15 | } 16 | 17 | MouseArea { 18 | id: wholeImageArea 19 | anchors.fill: parent 20 | onClicked: wholeImage.clicked() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /deployment.pri: -------------------------------------------------------------------------------- 1 | android-no-sdk { 2 | target.path = /data/user/qt 3 | export(target.path) 4 | INSTALLS += target 5 | } else:android { 6 | x86 { 7 | target.path = /libs/x86 8 | } else: armeabi-v7a { 9 | target.path = /libs/armeabi-v7a 10 | } else { 11 | target.path = /libs/armeabi 12 | } 13 | export(target.path) 14 | INSTALLS += target 15 | } else:unix { 16 | isEmpty(target.path) { 17 | qnx { 18 | target.path = /tmp/$${TARGET}/bin 19 | } else { 20 | target.path = /opt/$${TARGET}/bin 21 | } 22 | export(target.path) 23 | } 24 | INSTALLS += target 25 | } 26 | 27 | export(INSTALLS) 28 | -------------------------------------------------------------------------------- /pages/NotYetImplemented.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.2 2 | import QtQuick.Controls 1.1 3 | import QtQuick.Controls.Styles 1.1 4 | 5 | import "../components" 6 | 7 | Item { 8 | id: wholePage 9 | 10 | TitleBar { 11 | id: titleBar 12 | anchors.left: parent.left 13 | anchors.right: parent.right 14 | anchors.top: parent.top 15 | color: "#f6f5f1" 16 | 17 | title: "Not yet" 18 | leftButtonText: "Back" 19 | rightButtonText: "" 20 | 21 | onLeftButtonClicked: pageStack.pop() 22 | } 23 | 24 | Text { 25 | anchors.centerIn: parent 26 | width: parent.width 27 | horizontalAlignment: Text.AlignHCenter 28 | lineHeight: 1.5 29 | wrapMode: Text.WrapAtWordBoundaryOrAnywhere 30 | text: 'This feature is not yet implemented. See more about the project \ 31 | at CodingSubmarine or \ 32 | at github project page' 33 | onLinkActivated: { 34 | Qt.openUrlExternally(link) 35 | } 36 | } 37 | 38 | onFocusChanged: { 39 | if(focus) { 40 | Qt.inputMethod.hide() 41 | } 42 | } 43 | 44 | Component.onCompleted: { 45 | wholePage.focus = true 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CuteContacts-iOS 2 | ================ 3 | 4 | Qt/QML-based clone of iOS7 contacts application UI. Not a full app, but a proof of concept of that making a native (native enough?) looking iOS app UI is possible with Qt/QML 5 | 6 | For graphics so far we use the [great iOS7 UI template by teehan+lax](http://www.teehanlax.com/tools/iphone/) 7 | 8 | Screenshots 9 | ----------- 10 | 11 | Real contacts app in the emulator 12 | ![Real contacts app in the emulator](screenshots/iOS7-CuteContacts-main-screen.png?raw=true) 13 | 14 | CuteContacts-iOS7 app in the emulator 15 | ![Real contacts app in the emulator](screenshots/iOS7-contacts-real-main-screen.png?raw=true) 16 | 17 | License 18 | ----------- 19 | 20 |

21 | 23 | CC0 24 | 25 |
26 | To the extent possible under law, 27 | 29 | Artem Marchenko 30 | has waived all copyright and related or neighboring rights to 31 | CuteContacts for iOS. 32 | This work is published from: 33 | 35 | Finland. 36 |

-------------------------------------------------------------------------------- /components/AddPhoneLine.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import "../settings.js" as Settings 4 | 5 | Rectangle { 6 | id: wholeAddPhoneLine 7 | height: Settings.pne_height 8 | signal clicked 9 | 10 | Item { 11 | id: addPhoneRow 12 | anchors.left: parent.left 13 | anchors.right: parent.right 14 | anchors.top: parent.top 15 | height: parent.height 16 | 17 | Image { 18 | id: phonePlus 19 | anchors.left: parent.left 20 | anchors.leftMargin: 10 21 | anchors.verticalCenter: parent.verticalCenter 22 | source: "../images/green-plus.png" 23 | width: 20 24 | height: 20 25 | } 26 | 27 | Text { 28 | id: addPhoneButton 29 | anchors.left: phonePlus.right 30 | anchors.verticalCenter: phonePlus.verticalCenter 31 | anchors.leftMargin: 10 32 | anchors.right: parent.right 33 | text: "add phone" 34 | color: Settings.colorActiveBlue 35 | } 36 | 37 | Rectangle { 38 | id: underline 39 | anchors.top: parent.bottom 40 | anchors.left: addPhoneButton.left 41 | anchors.right: addPhoneButton.right 42 | 43 | anchors.topMargin: -1 44 | height: 1 45 | color: "#cccccc" 46 | } 47 | 48 | MouseArea { 49 | id: addPhoneArea 50 | anchors.fill: parent 51 | onClicked: { 52 | wholeAddPhoneLine.clicked() 53 | } 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | pages/MainPage.qml 5 | pages/NewContactPage.qml 6 | images/plus.png 7 | components/IosIconButton.qml 8 | images/magnifying-glass.png 9 | images/text-edit-x.png 10 | components/DimmableIconButton.qml 11 | components/DimmableTextButton.qml 12 | components/TitleBar.qml 13 | components/IOSTextField.qml 14 | images/red-minus.png 15 | images/green-plus.png 16 | images/chevron.png 17 | components/PhonesBlock.qml 18 | components/PhoneNumberEditor.qml 19 | pages/ListSelectorPage.qml 20 | images/blue-checkmark.png 21 | components/AddPhoneLine.qml 22 | settings.js 23 | pages/NotYetImplemented.qml 24 | components/SampleContacts.qml 25 | images/alphabet-scroller-labels.png 26 | pages/ContactViewPage.qml 27 | images/blue-handle.png 28 | images/blue-bubble.png 29 | images/back-chevron.png 30 | images/avatar-round.png 31 | components/IOSPage.qml 32 | components/AddToFavoritesMenu.qml 33 | components/ShareContactMenu.qml 34 | images/share-contact-menu.png 35 | images/black-statusbar.png 36 | 37 | 38 | -------------------------------------------------------------------------------- /components/DimmableTextButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQuick.Controls 1.1 3 | 4 | /** 5 | * A clickable text button that knows how to dim itself (by overlaying rectangle of supposedly bacground color on top) 6 | * during a mouse press. Dimming happens with a short delay. 7 | * 8 | * It is supposed to simulate the iOS text buttonslocated in the title bar. Simulation looks okay except for one detail: 9 | * in iOS of you drag finger out of the button while having it pressed at some distance discance goes away (and returns if you 10 | * drag finge closer to the button again) 11 | * 12 | * @param dimmingColor Set to the background color below the button 13 | * @signal clicked When clicked 14 | * 15 | * @TODO: Mention missing dimming goes away on distant dragging in the docs 16 | * @TODO: Extract dimming logic from here and DimminIconButton into a separate component? 17 | */ 18 | Label { 19 | signal clicked 20 | property alias dimmingColor: dimmer.color 21 | 22 | MouseArea { 23 | id: buttonArea 24 | anchors.fill: parent 25 | onClicked: { 26 | parent.clicked() 27 | } 28 | } 29 | Rectangle { 30 | id: dimmer 31 | anchors.fill: parent 32 | color: "#f6f5f1" 33 | opacity: 0.0 34 | } 35 | 36 | states: [ 37 | State { 38 | name: "pressed" 39 | when: buttonArea.pressed 40 | 41 | PropertyChanges { 42 | target: dimmer 43 | visible: true 44 | opacity: 0.8 45 | } 46 | } 47 | ] 48 | transitions: [ 49 | Transition { 50 | NumberAnimation { 51 | target: dimmer 52 | properties: "opacity"; 53 | easing.type: Easing.InOutQuad 54 | duration: 100 55 | } 56 | } 57 | 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /components/DimmableIconButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | /** 4 | * A clickable icon button that knows how to dim itself (by overlaying rectangle of supposedly bacground color on top) 5 | * during a mouse press. Dimming happens with a short delay. 6 | * 7 | * It is supposed to simulate the iOS icon buttons located in the title bar. Simulation looks okay except for one detail: 8 | * in iOS of you drag finger out of the button while having it pressed at some distance discance goes away (and returns if you 9 | * drag finge closer to the button again) 10 | * 11 | * @param source icon source to be used 12 | * @param dimmingColor Set to the background color below the button 13 | * @signal clicked When clicked 14 | * 15 | * @TODO: Mention missing dimming goes away on distant dragging in the docs 16 | */ 17 | Item { 18 | signal clicked 19 | property alias dimmingColor: dimmer.color 20 | property alias source: buttonImage.source 21 | property alias imageWidth: buttonImage.width 22 | property alias imageHeight: buttonImage.height 23 | 24 | // color: "lightblue" 25 | 26 | Image { 27 | id: buttonImage 28 | anchors.centerIn: parent 29 | } 30 | 31 | Rectangle { 32 | id: dimmer 33 | anchors.fill: parent 34 | color: "#f6f5f1" 35 | opacity: 0.0 36 | } 37 | 38 | MouseArea { 39 | id: buttonArea 40 | anchors.fill: parent 41 | onClicked: { 42 | parent.clicked() 43 | } 44 | } 45 | 46 | states: [ 47 | State { 48 | name: "pressed" 49 | when: buttonArea.pressed 50 | 51 | PropertyChanges { 52 | target: dimmer 53 | visible: true 54 | opacity: 0.8 55 | } 56 | } 57 | ] 58 | transitions: [ 59 | Transition { 60 | NumberAnimation { 61 | target: dimmer 62 | properties: "opacity"; 63 | easing.type: Easing.InOutQuad 64 | duration: 100 65 | } 66 | } 67 | 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /components/AddToFavoritesMenu.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import "../settings.js" as Settings 3 | 4 | Item { 5 | id: menu 6 | height: cancelButton.height + topMenuPart.height + topMenuPart.anchors.bottomMargin 7 | anchors.leftMargin: 8 8 | anchors.rightMargin: anchors.leftMargin 9 | anchors.bottomMargin: anchors.leftMargin 10 | 11 | Rectangle { 12 | id: cancelButton 13 | anchors.left: parent.left 14 | anchors.right: parent.right 15 | anchors.bottom: parent.bottom 16 | height: 44 17 | radius: 4 18 | 19 | color: "white" 20 | 21 | Text { 22 | anchors.centerIn: parent 23 | font.bold: true 24 | font.pixelSize: 19 25 | color: Settings.colorActiveBlue 26 | text: "Cancel" 27 | } 28 | } 29 | 30 | Rectangle { 31 | id: topMenuPart 32 | anchors.left: parent.left 33 | anchors.right: parent.right 34 | anchors.bottom: cancelButton.top 35 | anchors.bottomMargin: 8 36 | height: menuTitle.height + divider.height + mobileNumberItem.height 37 | radius: 4 38 | 39 | Item { 40 | id: mobileNumberItem 41 | anchors.left: parent.left 42 | anchors.right: parent.right 43 | anchors.bottom: parent.bottom 44 | height: 44 45 | Item { 46 | id: mobileNumberWrapper 47 | anchors.centerIn: parent 48 | width: mobileNumberTypeText.width + numberText.width 49 | 50 | Text { 51 | id: mobileNumberTypeText 52 | anchors.verticalCenter: parent.verticalCenter 53 | anchors.left: parent.left 54 | width: implicitWidth 55 | font.pixelSize: 20 56 | text: "mobile " 57 | } 58 | Text { 59 | id: numberText 60 | anchors.left: mobileNumberTypeText.right 61 | anchors.verticalCenter: parent.verticalCenter 62 | font.pixelSize: 20 63 | color: Settings.colorActiveBlue 64 | text: mobileNumber 65 | } 66 | } 67 | 68 | 69 | } 70 | 71 | Rectangle { 72 | id: divider 73 | anchors.left: parent.left 74 | anchors.right: parent.right 75 | anchors.top: mobileNumberItem.top 76 | height: 1 77 | color: "#cccccc" 78 | } 79 | 80 | 81 | Item { 82 | id: menuTitle 83 | anchors.left: parent.left 84 | anchors.right: parent.right 85 | anchors.bottom: divider.top 86 | height: 48 87 | Text { 88 | anchors.centerIn: parent 89 | font.pixelSize: 13 90 | color: "#888888" 91 | text: "Add to Favorites" 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /components/PhonesBlock.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQuick.Controls 1.1 3 | 4 | import "../settings.js" as Settings 5 | 6 | Item { 7 | height: childrenRect.height 8 | 9 | property var _possibleTypes: ["home", "work", "mobile", "company main", "work fax", 10 | "home fax", "assistant", "pager", "car", "radio"] 11 | 12 | // Are signal when exclusive editing requested/released for a particular control 13 | // I.e. when clicks elsewhere should only cancel editing, but be ignored otherwise 14 | signal startedControlEditing(var control) 15 | signal finishedControlEditing(var control) 16 | 17 | ListModel { 18 | id: phonesModel 19 | } 20 | 21 | ListView { 22 | id: listView 23 | width: parent.width 24 | height: (count )*Settings.pne_height - (addTransition.running ? heightOfElementShown( 25 | addTransition.ViewTransition.targetIndexes[0], 26 | addTransition.ViewTransition.targetItems[0]) : 0) 27 | clip: true 28 | 29 | model: phonesModel 30 | 31 | interactive: false 32 | 33 | // @return pixels 34 | function heightOfElementShown(index, item) { 35 | return - (item.y - index*Settings.pne_height) 36 | } 37 | 38 | delegate: PhoneNumberEditor { 39 | id: editorDelegate 40 | width: parent.width 41 | z: -index 42 | type: text 43 | 44 | Component.onCompleted: { 45 | editorDelegate.focus = true 46 | } 47 | 48 | onStateChanged: { 49 | if(state == "deletionQuery") { 50 | startedControlEditing(editorDelegate) 51 | } else { 52 | finishedControlEditing(editorDelegate) 53 | } 54 | } 55 | 56 | onSelfDeletionWanted: { 57 | // okay, in a real app we'd need to make it be called from onStateChanged 58 | // (not state change happens after control is destroyed already) 59 | finishedControlEditing(editorDelegate) 60 | phonesModel.remove(index) 61 | } 62 | } 63 | 64 | add: Transition { 65 | id: addTransition 66 | NumberAnimation { 67 | properties: "y"; 68 | from: (addTransition.ViewTransition.targetIndexes[0] -1 )*Settings.pne_height 69 | to: addTransition.ViewTransition.targetIndexes[0]*Settings.pne_height 70 | duration: 200 71 | easing.type: Easing.InOutQuad 72 | } 73 | } 74 | 75 | 76 | remove: Transition { 77 | id: removeTransition 78 | NumberAnimation { 79 | properties: "y"; 80 | from: (removeTransition.ViewTransition.targetIndexes[0] )*Settings.pne_height 81 | to: (removeTransition.ViewTransition.targetIndexes[0] -1)*Settings.pne_height 82 | duration: 200 83 | easing.type: Easing.InOutQuad 84 | } 85 | } 86 | 87 | } 88 | 89 | AddPhoneLine { 90 | anchors.top: listView.bottom 91 | width: parent.width 92 | visible: listView.count !== _possibleTypes.length 93 | onClicked: { 94 | if(listView.count < _possibleTypes.length) { 95 | phonesModel.append({text: _possibleTypes[listView.count]}) 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.2 2 | import QtQuick.Controls 1.1 3 | 4 | import "pages" 5 | 6 | Item { 7 | visible: true 8 | width: 320 9 | height: 568 10 | 11 | Rectangle { 12 | id: statusBarWrapper 13 | anchors.left: parent.left 14 | anchors.right: parent.right 15 | anchors.top: parent.top 16 | height: childrenRect.height 17 | color: "#f6f5f1" 18 | 19 | Image { 20 | id: statusBar 21 | anchors.leftMargin: 8 22 | anchors.rightMargin: 8 23 | fillMode: Image.PreserveAspectFit 24 | source: "../images/black-statusbar.png" 25 | height: sourceSize.height / 2 26 | } 27 | } 28 | 29 | StackView { 30 | id: pageStack 31 | anchors.left: parent.left 32 | anchors.right: parent.right 33 | anchors.top: parent.top 34 | anchors.bottom: parent.bottom 35 | 36 | onYChanged: { 37 | console.log("pageStack y ch to " + y) 38 | } 39 | 40 | delegate: StackViewDelegate { 41 | 42 | function getTransition(properties) 43 | { 44 | var usedPushTransition = properties.enterItem.pushTransition ? properties.enterItem.pushTransition : pageStack.transitionPopFromBottom 45 | var usedPopTransition = properties.exitItem.popTransition ? properties.exitItem.popTransition : pageStack.transitionSlideToBottom 46 | 47 | if(properties.name == "pushTransition") { 48 | return usedPushTransition 49 | } else if(properties.name == "popTransition") { 50 | return usedPopTransition 51 | } else { 52 | console.error("Requested unexpected transition type " + properties.name) 53 | } 54 | } 55 | 56 | } 57 | 58 | property Component transitionSlideFromRight: Component { 59 | StackViewTransition { 60 | PropertyAnimation { 61 | target: enterItem 62 | property: "x" 63 | from: 320 64 | to: 0 65 | easing.type: Easing.InOutQuad 66 | duration: 200 67 | } 68 | } 69 | } 70 | 71 | property Component transitionSlideToRight: Component { 72 | StackViewTransition { 73 | PropertyAnimation { 74 | target: exitItem 75 | property: "x" 76 | from: 0 77 | to: 320 78 | easing.type: Easing.InOutQuad 79 | duration: 200 80 | } 81 | } 82 | } 83 | 84 | property Component transitionSlideToBottom: Component { 85 | StackViewTransition { 86 | PropertyAnimation { 87 | target: exitItem 88 | property: "y" 89 | from: 0 90 | to: 568 91 | easing.type: Easing.InOutQuad 92 | duration: 200 93 | } 94 | } 95 | } 96 | 97 | 98 | property Component transitionPopFromBottom: Component { 99 | StackViewTransition { 100 | PropertyAnimation { 101 | target: enterItem 102 | property: "y" 103 | from: 568 104 | to: 0 105 | easing.type: Easing.InOutQuad 106 | duration: 200 107 | } 108 | } 109 | } 110 | 111 | 112 | initialItem: MainPage { 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /components/IOSTextField.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.2 2 | import QtQuick.Controls 1.1 3 | import QtQuick.Controls.Styles 1.1 4 | 5 | /** 6 | * @TODO Cannot position text inisde the rectangle using style 7 | */ 8 | Rectangle { 9 | id: textFieldWrapper 10 | // color: "green" 11 | // opacity: 0.4 12 | 13 | property alias placeholderText: fakePlaceholderTextComponent.text 14 | property alias showUnderline: underline.visible 15 | property alias inputMethodHints: innerTextField.inputMethodHints 16 | 17 | // easiest way to notify about it that I found.. 18 | signal lostActiveFocus 19 | 20 | onFocusChanged: { 21 | if(focus) { 22 | innerTextField.focus = true 23 | innerTextField.forceActiveFocus() 24 | } else { 25 | innerTextField.focus = false 26 | } 27 | } 28 | 29 | MouseArea { 30 | anchors.fill: parent 31 | onClicked: { 32 | console.log("ITF: clicked") 33 | } 34 | } 35 | 36 | width: 208 37 | height: 44 38 | color: "white" 39 | 40 | // Rectangle { 41 | // anchors.fill: parent 42 | // color: "lightyellow" 43 | // opacity: 0.2 44 | // } 45 | 46 | TextField { 47 | 48 | // Rectangle { 49 | // anchors.fill: parent 50 | // color: "blue" 51 | // opacity: 0.4 52 | // } 53 | 54 | id: innerTextField 55 | anchors.left: parent.left 56 | anchors.verticalCenter: parent.verticalCenter 57 | anchors.right: parent.right 58 | width: 204 59 | style: TextFieldStyle { 60 | font.family: "Helvetica Neue" 61 | font.pixelSize: 17 62 | 63 | background: Rectangle { 64 | color: "transparent" 65 | } 66 | } 67 | 68 | // workaround for the bug that sometimes positions cursor vertically a little wrong until first char input 69 | onFocusChanged: { 70 | if(focus) { 71 | var origText = text 72 | text = origText + " " 73 | text = origText 74 | } 75 | } 76 | onActiveFocusChanged: { 77 | if(!activeFocus) { 78 | lostActiveFocus() 79 | } 80 | } 81 | 82 | // To fight against disappearing placeholder 83 | Text { 84 | 85 | // Rectangle { 86 | // anchors.fill: parent 87 | // color: "lightblue" 88 | // opacity: 0.2 89 | // } 90 | 91 | id: fakePlaceholderTextComponent 92 | anchors.fill: parent 93 | anchors.leftMargin: 7 94 | anchors.bottomMargin:6 95 | font: parent.font 96 | horizontalAlignment: parent.horizontalAlignment 97 | verticalAlignment: parent.verticalAlignment 98 | opacity: !parent.text.length ? 1 : 0 99 | color: "darkgray" 100 | clip: contentWidth > width; 101 | elide: Text.ElideRight 102 | renderType: Text.NativeRendering 103 | Behavior on opacity { NumberAnimation { duration: 90 } } 104 | } 105 | 106 | Image { 107 | id: xbutton 108 | anchors.right: parent.right 109 | anchors.rightMargin: 8 110 | anchors.verticalCenter: parent.verticalCenter 111 | width: sourceSize.width / 2 112 | height: sourceSize.height / 2 113 | source: "qrc:///images/text-edit-x.png" 114 | visible: parent.text.length > 0 && parent.activeFocus === true 115 | 116 | MouseArea { 117 | anchors.fill: parent 118 | onClicked: { 119 | parent.parent.text = "" 120 | } 121 | } 122 | } 123 | } 124 | 125 | Rectangle { 126 | id: underline 127 | anchors.left: parent.left 128 | anchors.right: parent.right 129 | anchors.bottom: parent.bottom 130 | height: 1 131 | color: "#c9c9ce" 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /pages/ListSelectorPage.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.2 2 | import QtQuick.Controls 1.1 3 | import QtQuick.Controls.Styles 1.1 4 | 5 | import "../components" 6 | 7 | 8 | Item { 9 | property alias title: titleBar.title 10 | property alias leftButtonText: titleBar.leftButtonText 11 | property alias rightButtonText: titleBar.rightButtonText 12 | 13 | // List of strings to display 14 | property var items: [] 15 | 16 | // when shown list positions selection to this text (if present in the model) 17 | // whenever a different item is selected, this text is updated 18 | property string selectedText 19 | 20 | signal selectionCompleted(string selecteText) 21 | 22 | property bool _initialized: false 23 | 24 | TitleBar { 25 | id: titleBar 26 | anchors.left: parent.left 27 | anchors.right: parent.right 28 | anchors.top: parent.top 29 | color: "#f6f5f1" 30 | 31 | title: "New Contact" 32 | 33 | leftButtonText: "Cancel" 34 | onLeftButtonClicked: pageStack.pop() 35 | 36 | rightButtonText: "Done" 37 | onRightButtonClicked: completeSelection() 38 | } 39 | 40 | ListModel { 41 | id: itemsModel 42 | ListElement { 43 | text: "home" 44 | } 45 | ListElement { 46 | text: "home2" 47 | } 48 | ListElement { 49 | text: "home3" 50 | } 51 | } 52 | 53 | ListView { 54 | id: listView 55 | anchors.top: titleBar.bottom 56 | anchors.left: parent.left 57 | anchors.right: parent.right 58 | anchors.bottom: parent.bottom 59 | model: itemsModel 60 | header: Item { 61 | width: parent.width 62 | height: 36 63 | // color: "#efeff4" 64 | } 65 | footer: Item { 66 | width: parent.width 67 | height: 36 68 | // color: "#efeff4" 69 | } 70 | 71 | delegate: Rectangle { 72 | width: parent.width 73 | height: 43 74 | 75 | Text { 76 | id: itemText 77 | anchors.left: parent.left 78 | anchors.verticalCenter: parent.verticalCenter 79 | anchors.leftMargin: 10 80 | font.pixelSize: 16 81 | text: model.text 82 | } 83 | Rectangle { 84 | id: underline 85 | visible: (index+1 !== parent.ListView.view.count) 86 | anchors.left: itemText.left 87 | anchors.right: parent.right 88 | anchors.top: parent.bottom 89 | anchors.topMargin: -1 90 | height: 1 91 | color: "#cccccc" 92 | } 93 | 94 | Image { 95 | anchors.right: parent.right 96 | anchors.verticalCenter: parent.verticalCenter 97 | anchors.rightMargin: 10 98 | width: sourceSize.width / 2 99 | height: sourceSize.height / 2 100 | visible: parent.ListView.isCurrentItem 101 | 102 | source: "../images/blue-checkmark.png" 103 | } 104 | 105 | MouseArea { 106 | anchors.fill: parent 107 | onClicked: { 108 | parent.ListView.view.currentIndex = index 109 | } 110 | } 111 | } 112 | 113 | onCurrentIndexChanged: { 114 | if(_initialized) { 115 | selectedText = itemsModel.get(currentIndex).text 116 | completeSelection() 117 | } 118 | } 119 | } 120 | 121 | // pops the view first signaling about the current selection to whoever is interested 122 | function completeSelection() { 123 | selectionCompleted(selectedText) 124 | pageStack.pop() 125 | } 126 | 127 | onSelectedTextChanged: { 128 | console.log("LSP: selectedText ch to " + selectedText) 129 | } 130 | 131 | Component.onCompleted: { 132 | _initialized = false 133 | itemsModel.clear() 134 | for(var i=0; i < items.length; i++) { 135 | itemsModel.append({text: items[i]}) 136 | if(selectedText == items[i]) { 137 | listView.currentIndex = i 138 | } 139 | } 140 | _initialized = true 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /components/TitleBar.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQuick.Controls 1.1 3 | 4 | import "../settings.js" as Settings 5 | 6 | /** 7 | * buttonIconSoure and buttonText properties are mutually exclusive. Ideally setting one should clear another one. 8 | * Right now just don't set both 9 | * @param color Background color 10 | * 11 | * @TODO: setting one of the variants for right button, should clear or make invisible the other one. Same for the left button 12 | */ 13 | Rectangle { 14 | id: titleBar 15 | 16 | property alias title: mainLabel.text 17 | property alias rightButtonIconSource: rightIconButton.source 18 | property alias rightButtonText: rightTextButton.text 19 | property alias leftButtonIconSource: leftIconButton.source 20 | property alias leftButtonText: leftTextButton.text 21 | 22 | property bool backChevron: false 23 | 24 | signal leftButtonClicked 25 | signal rightButtonClicked 26 | 27 | 28 | signal leftControlClicked 29 | signal rightControlClicked 30 | 31 | height: 50 32 | color: "#f6f5f1" 33 | 34 | Label { 35 | id: mainLabel 36 | anchors.centerIn: parent 37 | font.family: "Helvetica Neue" 38 | font.bold: true 39 | font.pointSize: 17 40 | } 41 | 42 | // @TODO: dim on the whole action level, not on the label/icon level. At the moment chevron isn't dimmed at all 43 | Item { 44 | id: leftActionBlock 45 | anchors.left: parent.left 46 | anchors.top: parent.top 47 | anchors.bottom: parent.bottom 48 | width: childrenRect.width 49 | 50 | // Capturing not handled by individual areas 51 | MouseArea { 52 | anchors.fill: parent 53 | onClicked: titleBar.leftButtonClicked() 54 | } 55 | 56 | Image { 57 | id: backChevronIcon 58 | anchors.left: parent.left 59 | anchors.verticalCenter: parent.verticalCenter 60 | anchors.leftMargin: 4 61 | visible: backChevron 62 | width: sourceSize.width / 2 63 | height: sourceSize.height / 2 64 | 65 | source: "../images/back-chevron.png" 66 | } 67 | 68 | DimmableIconButton { 69 | id: leftIconButton 70 | visible: source.toString().length > 0 71 | 72 | anchors.left: backChevronIcon.right 73 | anchors.leftMargin: 16 74 | anchors.verticalCenter: parent.verticalCenter 75 | 76 | width: height 77 | height: parent.height 78 | imageWidth: 18 79 | imageHeight: 18 80 | onClicked: titleBar.leftButtonClicked() 81 | } 82 | 83 | DimmableTextButton { 84 | id: leftTextButton 85 | visible: text.length > 0 86 | 87 | anchors.left: backChevron ? backChevronIcon.right : parent.left 88 | anchors.leftMargin: 6 89 | anchors.top: parent.top 90 | anchors.bottom: parent.bottom 91 | verticalAlignment: Text.AlignVCenter 92 | 93 | clip: true 94 | font.family: "Helvetica Neue" 95 | font.pointSize: 18 96 | color: Settings.colorActiveBlue 97 | onClicked: titleBar.leftButtonClicked() 98 | } 99 | 100 | } 101 | 102 | 103 | 104 | 105 | DimmableIconButton { 106 | id: rightIconButton 107 | visible: source.toString().length > 0 108 | 109 | anchors.right: parent.right 110 | anchors.verticalCenter: parent.verticalCenter 111 | 112 | width: height 113 | height: parent.height 114 | imageWidth: 18 115 | imageHeight: 18 116 | onClicked: titleBar.rightButtonClicked() 117 | } 118 | 119 | // @TODO: make areas close to title bar area right edge clickable too (under margin right now) 120 | 121 | 122 | DimmableTextButton { 123 | id: rightTextButton 124 | visible: text.length > 0 125 | 126 | anchors.right: parent.right 127 | anchors.rightMargin: 8 128 | anchors.top: parent.top 129 | anchors.bottom: parent.bottom 130 | verticalAlignment: Text.AlignVCenter 131 | 132 | clip: true 133 | font.family: "Helvetica Neue" 134 | font.pointSize: 18 135 | color: Settings.colorActiveBlue 136 | onClicked: titleBar.rightButtonClicked() 137 | } 138 | 139 | Rectangle { 140 | id: underline 141 | anchors.left: parent.left 142 | anchors.right: parent.right 143 | anchors.bottom: parent.bottom 144 | height: 1 145 | color: "#a3a3a6" 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /components/IOSPage.qml: -------------------------------------------------------------------------------- 1 | /** 2 | * Page overlayeble with menu 3 | */ 4 | import QtQuick 2.0 5 | import QtGraphicalEffects 1.0 6 | import "../settings.js" as Settings 7 | 8 | Item { 9 | id: wholePage 10 | default property alias contents: pageContent.children 11 | property alias menu: backgroundOverlay.menu 12 | property string mobileNumber: "" 13 | property bool _menuIsActive: false 14 | 15 | property alias backgroundColor: pageContent.color 16 | 17 | // if null, the default ones are used 18 | property var pushTransition: null 19 | property var popTransition: null 20 | 21 | function showMenu() { 22 | _menuIsActive = true 23 | } 24 | 25 | Rectangle { 26 | id: pageContent 27 | anchors.fill: parent 28 | anchors.topMargin: statusBar.height 29 | color: "#f6f5f1" 30 | } 31 | 32 | // Ugly hack for status bar area dimming. Doesn't have desaturation and just shows gray rectangle on top 33 | Item { 34 | id: statusBarOverlay 35 | anchors.left: parent.left 36 | anchors.right: parent.right 37 | anchors.top: parent.top 38 | anchors.topMargin: -5 39 | visible: backgroundOverlay.visible 40 | opacity: backgroundOverlay.opacity 41 | z: 500 42 | height: 20 43 | 44 | Rectangle { 45 | anchors.fill: parent 46 | color: "black" 47 | opacity: 0.4 48 | } 49 | } 50 | 51 | Item { 52 | id: backgroundOverlay 53 | anchors.fill: parent 54 | anchors.topMargin: statusBar.height 55 | visible: false 56 | opacity: 0 57 | 58 | Desaturate { 59 | anchors.fill: parent 60 | source: pageContent 61 | desaturation: 0.8 62 | 63 | Rectangle { 64 | anchors.fill: parent 65 | color: "black" 66 | opacity: 0.4 67 | MouseArea { 68 | anchors.fill: parent 69 | onClicked: { 70 | _menuIsActive = false 71 | } 72 | } 73 | } 74 | } 75 | 76 | property Item menu: Item {} 77 | 78 | onMenuChanged: { 79 | if(menu) { 80 | menu.parent = backgroundOverlay 81 | menu.anchors.left = backgroundOverlay.left 82 | menu.anchors.right = backgroundOverlay.right 83 | menu.anchors.top = backgroundOverlay.bottom 84 | } 85 | } 86 | } 87 | 88 | states: [ 89 | State { 90 | name: "menuIsActive" 91 | when: _menuIsActive 92 | PropertyChanges { 93 | target: backgroundOverlay 94 | opacity: 1 95 | } 96 | PropertyChanges { 97 | target: backgroundOverlay 98 | visible: true 99 | } 100 | AnchorChanges { 101 | target: menu 102 | anchors.top: undefined 103 | anchors.bottom: backgroundOverlay.bottom 104 | } 105 | } 106 | ] 107 | 108 | // First make things visible (even with opacity 0), then increase opacity 109 | transitions: [ 110 | Transition { 111 | from: "*" 112 | to: "menuIsActive" 113 | SequentialAnimation { 114 | PropertyAnimation { 115 | properties: "visible" 116 | } 117 | ParallelAnimation { 118 | AnchorAnimation { 119 | easing.type: Easing.InOutQuad 120 | duration: 80 121 | } 122 | PropertyAnimation { 123 | properties: "opacity" 124 | duration: 50 125 | easing.type: Easing.InOutQuad 126 | } 127 | } 128 | } 129 | }, 130 | Transition { 131 | from: "menuIsActive" 132 | to: "*" 133 | SequentialAnimation { 134 | ParallelAnimation { 135 | AnchorAnimation { 136 | easing.type: Easing.InOutQuad 137 | duration: 80 138 | } 139 | PropertyAnimation { 140 | properties: "opacity" 141 | duration: 80 142 | easing.type: Easing.InOutQuad 143 | } 144 | } 145 | PropertyAnimation { 146 | properties: "visible" 147 | } 148 | } 149 | } 150 | 151 | ] 152 | 153 | } 154 | -------------------------------------------------------------------------------- /components/PhoneNumberEditor.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | import "../settings.js" as Settings 4 | 5 | Rectangle { 6 | id: wholeEditor 7 | property alias type: phoneTypeButton.text 8 | 9 | // user clicked delete button 10 | signal selfDeletionWanted 11 | 12 | height: Settings.pne_height 13 | 14 | onFocusChanged: { 15 | phoneNumberField.focus = focus 16 | } 17 | 18 | Rectangle { 19 | id: deleteButtonBlock 20 | anchors.top: parent.top 21 | anchors.bottom: parent.bottom 22 | anchors.right: parent.right 23 | width: 90 24 | color: "red" 25 | 26 | Text { 27 | anchors.verticalCenter: parent.verticalCenter 28 | anchors.left: parent.left 29 | anchors.leftMargin: 24 30 | 31 | font.pixelSize: 18 32 | color: "white" 33 | text: "Delete" 34 | } 35 | 36 | MouseArea { 37 | anchors.fill: parent 38 | onClicked: { 39 | selfDeletionWanted() 40 | } 41 | } 42 | } 43 | Rectangle { 44 | id: swipablePart 45 | anchors.top: parent.top 46 | anchors.bottom: parent.bottom 47 | anchors.left: parent.left 48 | width: parent.width 49 | 50 | Behavior on anchors.leftMargin { 51 | NumberAnimation { 52 | duration: 300 53 | easing.type: Easing.InOutQuad 54 | } 55 | } 56 | 57 | Image { 58 | id: deleteIcon 59 | anchors.verticalCenter: parent.verticalCenter 60 | anchors.left: parent.left 61 | anchors.leftMargin: 10 62 | width: 24 63 | height: 26 64 | 65 | source: "../images/red-minus.png" 66 | 67 | MouseArea { 68 | id: deletionIconArea 69 | anchors.fill: parent 70 | onClicked: wholeEditor.state = "deletionQuery" 71 | } 72 | } 73 | 74 | Rectangle { 75 | // color: "lightgreen" 76 | // opacity: 0.8 77 | id: typeAndNumberBlock 78 | anchors.top: parent.top 79 | anchors.left: deleteIcon.right 80 | // anchors.right: parent.right 81 | anchors.leftMargin: 10 82 | anchors.bottom: parent.bottom 83 | width: parent.width - deleteIcon.width - 10 84 | 85 | Rectangle { 86 | id: buttonAndChevronBlock 87 | anchors.left: parent.left 88 | anchors.top: parent.top 89 | anchors.bottom: parent.bottom 90 | width: childrenRect.width 91 | 92 | Text { 93 | id: phoneTypeButton 94 | text: "home" 95 | anchors.left: parent.left 96 | anchors.verticalCenter: parent.verticalCenter 97 | width: 75 98 | clip: true 99 | elide: Text.ElideRight 100 | color: Settings.colorActiveBlue 101 | } 102 | 103 | Image { 104 | id: chevronIcon 105 | anchors.left: phoneTypeButton.right 106 | anchors.verticalCenter: phoneTypeButton.verticalCenter 107 | anchors.leftMargin: 5 108 | width: 8 109 | height: 12 110 | 111 | source: "../images/chevron.png" 112 | 113 | } 114 | 115 | MouseArea { 116 | anchors.fill: parent 117 | onClicked: { 118 | function selectionCompletionHandler(selectedText) { 119 | phoneTypeButton.text = selectedText 120 | pageStack.currentItem.selectionCompleted.disconnect(selectionCompletionHandler) 121 | } 122 | 123 | pageStack.push("qrc:///pages/ListSelectorPage.qml", 124 | { 125 | leftButtonText: "Cancel", 126 | title: "Label", 127 | items: ["home", "work", "mobile", "company main", "work fax", 128 | "home fax", "assistant", "pager", "car", "radio"], 129 | selectedText: phoneTypeButton.text, 130 | }) 131 | pageStack.currentItem.selectionCompleted.connect(selectionCompletionHandler) 132 | 133 | } 134 | } 135 | } 136 | 137 | // @TODO add some proper gradient 138 | Rectangle { 139 | id: verticalSeparator 140 | anchors.left: buttonAndChevronBlock.right 141 | anchors.top: parent.top 142 | anchors.bottom: parent.bottom 143 | anchors.topMargin: 4 144 | anchors.leftMargin: 4 145 | 146 | width: 1 147 | color: "#cccccc" 148 | } 149 | 150 | IOSTextField { 151 | id: phoneNumberField 152 | anchors.left: verticalSeparator.right 153 | anchors.top: parent.top 154 | anchors.right: parent.right 155 | anchors.bottom: parent.bottom 156 | anchors.rightMargin: 24 157 | 158 | placeholderText: "Phone" 159 | showUnderline: false 160 | inputMethodHints: Qt.ImhDialableCharactersOnly 161 | onLostActiveFocus: { 162 | wholeEditor.state = "" 163 | } 164 | } 165 | 166 | Rectangle { 167 | id: underline 168 | anchors.top: parent.bottom 169 | anchors.left: parent.left 170 | anchors.right: parent.right 171 | anchors.topMargin: -1 172 | 173 | height: 1 174 | color: "#cccccc" 175 | } 176 | 177 | } 178 | } 179 | 180 | states: [ 181 | State { 182 | name: "deletionQuery" 183 | PropertyChanges { 184 | target: swipablePart 185 | anchors.leftMargin: -deleteButtonBlock.width 186 | } 187 | } 188 | ] 189 | 190 | 191 | } 192 | -------------------------------------------------------------------------------- /pages/NewContactPage.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.2 2 | import QtQuick.Controls 1.1 3 | import QtQuick.Controls.Styles 1.1 4 | 5 | import "../components" 6 | import "../settings.js" as Settings 7 | 8 | /** 9 | * @TODO report that it'd be nice to set iOS style even when running on desktop 10 | * @TODO scrollbars on desktop eat space from ScrollView. 11 | * @TODO Default scrollbar style on iOS looks not like iOS standard bars (too long, too thick) and you can't customize it easily 12 | * @TODO Position of cursor bar is to be controlled 13 | */ 14 | 15 | IOSPage { 16 | id: wholePage 17 | focus: true 18 | 19 | // when not empty, clicks are blocked for everything except the controls inside 20 | property var exclusiveControls: [] 21 | 22 | TitleBar { 23 | id: titleBar 24 | anchors.left: parent.left 25 | anchors.right: parent.right 26 | anchors.top: parent.top 27 | color: "#f6f5f1" 28 | 29 | title: "New Contact" 30 | 31 | leftButtonText: "Cancel" 32 | onLeftButtonClicked: pageStack.pop() 33 | 34 | rightButtonText: "Done" 35 | onRightButtonClicked: console.log("NCP: Done button clicked") 36 | 37 | } 38 | ScrollView { 39 | id: mainScrollView 40 | anchors.left: parent.left 41 | anchors.right: parent.right 42 | anchors.top: titleBar.bottom 43 | anchors.bottom: parent.bottom 44 | 45 | Rectangle { 46 | id: flickableBackground 47 | width: mainScrollView.width 48 | height: childrenRect.height 49 | color: "white" 50 | 51 | /** Eats mouse events for the **/ 52 | Rectangle { 53 | id: excluder 54 | anchors.fill: parent 55 | color: "transparent" 56 | z: 1000 57 | visible: exclusiveControls && exclusiveControls.length > 0 58 | MouseArea { 59 | id: excluderArea 60 | anchors.fill: parent 61 | onPressed: { 62 | if(withinTheControls(excluderArea, mouse.x, mouse.y, exclusiveControls)) { 63 | mouse.accepted = false 64 | } 65 | } 66 | 67 | onClicked: { 68 | // okay, not so good binding. Just go over all controls willing to be exclusive and 69 | // cancel this wish of theirs 70 | if(!exclusiveControls) return; 71 | while(exclusiveControls.length) { 72 | exclusiveControls.pop().state = "" 73 | } 74 | } 75 | 76 | /** 77 | * @return true if x,y are within one of the controls. False otherwise 78 | * @param x,y coords in the coord system of eventOriginControl 79 | * @param controls List if Items to check if coordinates re within them or not 80 | */ 81 | function withinTheControls(eventOriginControl, x, y, controls) { 82 | for(var i=0; i < controls.length; i++) { 83 | var translatedCoords = eventOriginControl.mapToItem(controls[i], x, y) 84 | if(controls[i].contains(Qt.point(translatedCoords.x, translatedCoords.y))) { 85 | return true 86 | } 87 | } 88 | return false; 89 | } 90 | 91 | } 92 | } 93 | 94 | MouseArea { 95 | anchors.fill: parent 96 | onClicked: { 97 | console.log("NCP: background clicked") 98 | parent.focus = true 99 | Qt.inputMethod.hide() 100 | } 101 | } 102 | 103 | Rectangle { 104 | id: addPhotoCircle 105 | width: 60 106 | height: width 107 | anchors.top: parent.top 108 | anchors.topMargin: 8 109 | anchors.left: parent.left 110 | anchors.leftMargin: 35 111 | radius: width / 2 112 | color: "transparent" 113 | border.width: 1 114 | border.color: "#c9c9ce" 115 | 116 | Label { 117 | anchors.fill: parent 118 | horizontalAlignment: Text.AlignHCenter 119 | verticalAlignment: Text.AlignVCenter 120 | text: "add\nphoto" 121 | color: Settings.colorActiveBlue 122 | font.pixelSize: 12 123 | } 124 | 125 | MouseArea { 126 | anchors.fill: parent 127 | onClicked: pageStack.push("qrc:///pages/NotYetImplemented.qml") 128 | } 129 | } 130 | 131 | /** 132 | * @TODO Cannot position text inside the rectangle using style 133 | */ 134 | IOSTextField { 135 | id: firstNameField 136 | anchors.top: parent.top 137 | anchors.right: parent.right 138 | placeholderText: "First" 139 | } 140 | IOSTextField { 141 | id: lastNameField 142 | anchors.top: firstNameField.bottom 143 | anchors.right: parent.right 144 | placeholderText: "Last" 145 | } 146 | 147 | IOSTextField { 148 | id: companyField 149 | anchors.top: lastNameField.bottom 150 | anchors.right: parent.right 151 | placeholderText: "Company" 152 | } 153 | 154 | PhonesBlock { 155 | id: phonesBlock 156 | anchors.top: companyField.bottom 157 | anchors.left: parent.left 158 | anchors.right: parent.right 159 | anchors.rightMargin: 6 160 | anchors.topMargin: 56 161 | 162 | onStartedControlEditing: { 163 | // trick to emit property signal 164 | var tmpArray = exclusiveControls 165 | if(!tmpArray) tmpArray = [] 166 | exclusiveControls = null 167 | 168 | tmpArray.push(control) 169 | exclusiveControls = tmpArray 170 | } 171 | 172 | onFinishedControlEditing: { 173 | // trick to emit property signal 174 | var tmpArray = exclusiveControls 175 | if(!tmpArray) tmpArray = [] 176 | exclusiveControls = null 177 | 178 | for(var i=0; i 0 81 | 82 | MouseArea { 83 | anchors.fill: parent 84 | onClicked: { 85 | searchField.text = "" 86 | } 87 | } 88 | } 89 | 90 | } 91 | 92 | Item { 93 | id: searchTextPlaceholder 94 | anchors.verticalCenter: parent.verticalCenter 95 | anchors.horizontalCenter: parent.horizontalCenter 96 | anchors.leftMargin: 8 97 | anchors.topMargin: 4 98 | 99 | width: childrenRect.width 100 | height: glassIcon.height 101 | 102 | Image { 103 | id: glassIcon 104 | width: sourceSize.width / 2 105 | height: sourceSize.height / 2 106 | source: "qrc:///images/magnifying-glass.png" 107 | } 108 | 109 | Label { 110 | anchors.left: glassIcon.right 111 | anchors.leftMargin: 8 112 | anchors.verticalCenter: glassIcon.verticalCenter 113 | text: "Search" 114 | color: "#8e8e93" 115 | font.pixelSize: 14 116 | visible: searchField.text.trim().length === 0 117 | } 118 | } 119 | 120 | } 121 | 122 | onTextChanged: { 123 | updateFilteredModel(text) 124 | } 125 | 126 | DimmableTextButton { 127 | id: cancelLabel 128 | 129 | anchors.left: parent.right 130 | anchors.right: parent.right 131 | anchors.rightMargin: 8 132 | anchors.verticalCenter: parent.verticalCenter 133 | clip: true 134 | font.family: "Helvetica Neue" 135 | font.pointSize: 18 136 | color: Settings.colorActiveBlue 137 | dimmingColor: "#c9c9ce" 138 | text: "Cancel" 139 | onClicked: { 140 | // better than changing state as clicked will care about everything related to focus change 141 | pageArea.clicked(null) 142 | } 143 | } 144 | 145 | Rectangle { 146 | id: underline 147 | anchors.left: parent.left 148 | anchors.right: parent.right 149 | anchors.bottom: parent.bottom 150 | height: 1 151 | color: "#bfbfc1" 152 | } 153 | 154 | 155 | } 156 | 157 | SampleContacts { 158 | id: contactsModel 159 | } 160 | 161 | ListModel { 162 | id: filteredContactsModel 163 | } 164 | 165 | ListView { 166 | id: contactList 167 | model: filteredContactsModel 168 | anchors.left: parent.left 169 | anchors.right: parent.right 170 | anchors.top: searchBar.bottom 171 | anchors.bottom: parent.bottom 172 | 173 | section.property: "firstName" 174 | section.criteria: ViewSection.FirstCharacter 175 | section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart 176 | clip: true 177 | 178 | delegate: Rectangle { 179 | width: parent.width 180 | height: 40 181 | Text { 182 | id: contactNameLabel 183 | anchors.fill: parent 184 | anchors.leftMargin: 10 185 | anchors.rightMargin: 5 186 | verticalAlignment: Text.AlignVCenter 187 | font.pixelSize: 14 188 | text: "" + firstName + " " + lastName 189 | } 190 | Rectangle { 191 | id: underliner 192 | 193 | // next section is undefined for the very first element and.. somehow for the very first delegate 194 | // but for the prototyping purposes we just make sure there's more than one element in first section 195 | anchors.left: isLastElementInSection(index, parent.ListView.section, parent.ListView.nextSection) ? 196 | parent.left : contactNameLabel.left 197 | 198 | // anchors.left: contactNameLabel.left 199 | anchors.right: contactNameLabel.right 200 | anchors.top: contactNameLabel.bottom 201 | anchors.topMargin: -1 202 | height: 1 203 | 204 | // visible: (!parent.ListView.nextSection) || parent.ListView.section == parent.ListView.nextSection 205 | 206 | color: "#cccccc" 207 | 208 | function isLastElementInSection(index, currSection, nextSection) { 209 | // next section is undefined for the very last element and.. somehow for the very first delegate 210 | // but for the prototyping purposes we just make sure there's more than one element in first section 211 | if(index === 0) return false 212 | if(typeof(nextSection) == "undefined") return true 213 | 214 | return currSection != nextSection 215 | } 216 | } 217 | MouseArea { 218 | anchors.fill: parent 219 | onClicked: pageStack.push("qrc:///pages/ContactViewPage.qml", 220 | {firstName: firstName, lastName: lastName, mobileNumber: mobileNumber, 221 | pushTransition: pageStack.transitionSlideFromRight, 222 | popTransition: pageStack.transitionSlideToRight}) 223 | } 224 | } 225 | 226 | section.delegate: Rectangle { 227 | id: sectionDelegate 228 | width: parent.width 229 | height: 18 230 | color: "#eeeeee" 231 | Text { 232 | id: sectionLabel 233 | anchors.fill: parent 234 | anchors.leftMargin: 10 235 | anchors.rightMargin: 5 236 | anchors.topMargin: 2 237 | font.bold: true 238 | font.pixelSize: 14 239 | verticalAlignment: Text.AlignVCenter 240 | text: section 241 | } 242 | } 243 | 244 | footer: Rectangle { 245 | anchors.left: parent.left 246 | anchors.right: parent.right 247 | height:40 248 | 249 | Text { 250 | anchors.centerIn: parent 251 | text: contactsModel.count + " Contacts" 252 | color: "#aaaaaa" 253 | } 254 | 255 | } 256 | 257 | // or ignore if such section is not found 258 | // real app would position to the closest present section probably 259 | function scrollToSection(section) { 260 | for(var i=0; i < contactsModel.count; i++) { 261 | if(contactsModel.get(i).firstName.substr(0, 1).toUpperCase() == section) { 262 | positionViewAtIndex(i, ListView.Beginning) 263 | break 264 | } 265 | } 266 | } 267 | } 268 | 269 | // Touch area is wider than image 270 | Item { 271 | id: listScrollerHolder 272 | anchors.top: contactList.top 273 | anchors.bottom: contactList.bottom 274 | anchors.right: contactList.right 275 | width: 24 276 | 277 | Image { 278 | id: listScroller 279 | anchors.top: parent.top 280 | anchors.bottom: parent.bottom 281 | anchors.right: parent.right 282 | fillMode: Image.PreserveAspectFit 283 | 284 | source: "../images/alphabet-scroller-labels.png" 285 | } 286 | MouseArea { 287 | anchors.fill: parent 288 | onPositionChanged: { 289 | contactList.scrollToSection(sectionForMouseY(mouse.y)) 290 | } 291 | 292 | onPressed: { 293 | contactList.scrollToSection(sectionForMouseY(mouse.y)) 294 | } 295 | 296 | function sectionForMouseY(mouseY) { 297 | // empty areas at top and bottom should be treated as first/last letters 298 | var alphabet = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", 299 | "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"] 300 | var topBlancHeight = 10 301 | var bottomBlancHeight = 20 302 | 303 | if(mouseY < topBlancHeight) return alphabet[0] 304 | if(mouseY > height - bottomBlancHeight) return alphabet[alphabet.length-1] 305 | 306 | var letterIdx = Math.floor((mouseY-topBlancHeight) / (height / 26)) 307 | return alphabet[letterIdx] 308 | } 309 | } 310 | } 311 | 312 | states: [ 313 | State { 314 | name: "searchBarActive" 315 | when: searchField.activeFocus 316 | AnchorChanges { 317 | target: titleBar 318 | anchors.top: pageArea.top 319 | anchors.bottom: pageArea.top 320 | } 321 | AnchorChanges { 322 | target: searchTextPlaceholder 323 | anchors.horizontalCenter: undefined 324 | anchors.left: searchFieldWrapper.left 325 | } 326 | AnchorChanges { 327 | target: cancelLabel 328 | anchors.left: undefined 329 | } 330 | PropertyChanges { 331 | target: cancelLabel 332 | width: cancelLabel.implicitWidth 333 | } 334 | }, 335 | State { 336 | name: "" 337 | StateChangeScript { 338 | script: { 339 | Qt.inputMethod.hide() 340 | } 341 | } 342 | PropertyChanges { 343 | target: searchField 344 | text: "" 345 | } 346 | } 347 | 348 | ] 349 | 350 | transitions: [ 351 | Transition { 352 | from: "*" 353 | to: "*" 354 | AnchorAnimation { duration: 200; easing.type: Easing.InOutQuad } 355 | } 356 | ] 357 | 358 | } 359 | 360 | // @param filterText can be null or undefined as well 361 | function updateFilteredModel(filterText) { 362 | if(!filterText) filterText = "" 363 | filterText = filterText.toLowerCase() 364 | 365 | filteredContactsModel.clear() 366 | for(var i=0; i < contactsModel.count; i++ ) { 367 | var curr = contactsModel.get(i) 368 | if(curr.firstName.toLowerCase().indexOf(filterText) !== -1 || 369 | curr.lastName.toLowerCase().indexOf(filterText) !== -1) { 370 | filteredContactsModel.append(curr) 371 | } 372 | } 373 | } 374 | 375 | Component.onCompleted: { 376 | console.log("MainPage completed. Platform OS is " + Qt.platform.os) 377 | updateFilteredModel(null) 378 | } 379 | 380 | } 381 | -------------------------------------------------------------------------------- /components/SampleContacts.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | 3 | ListModel { 4 | ListElement { firstName: "Abel"; lastName: "Maclead"; mobileNumber: "631-335-3414" } 5 | ListElement { firstName: "Adelina"; lastName: "Nabours"; mobileNumber: "216-230-4892" } 6 | ListElement { firstName: "Adell"; lastName: "Lipkin"; mobileNumber: "973-654-1561" } 7 | ListElement { firstName: "Ahmed"; lastName: "Angalich"; mobileNumber: "717-528-8996" } 8 | ListElement { firstName: "Aja"; lastName: "Gehrett"; mobileNumber: "973-544-2677" } 9 | ListElement { firstName: "Alaine"; lastName: "Bergesen"; mobileNumber: "914-300-9193" } 10 | ListElement { firstName: "Albina"; lastName: "Glick"; mobileNumber: "732-924-7882" } 11 | ListElement { firstName: "Alease"; lastName: "Buemi"; mobileNumber: "303-301-4946" } 12 | ListElement { firstName: "Alecia"; lastName: "Bubash"; mobileNumber: "940-276-7922" } 13 | ListElement { firstName: "Alesia"; lastName: "Hixenbaugh"; mobileNumber: "202-646-7516" } 14 | ListElement { firstName: "Alex"; lastName: "Loader"; mobileNumber: "253-660-7821" } 15 | ListElement { firstName: "Alisha"; lastName: "Slusarski"; mobileNumber: "732-658-3154" } 16 | ListElement { firstName: "Alishia"; lastName: "Sergi"; mobileNumber: "212-860-1579" } 17 | ListElement { firstName: "Aliza"; lastName: "Baltimore"; mobileNumber: "408-504-3552" } 18 | ListElement { firstName: "Allene"; lastName: "Iturbide"; mobileNumber: "715-662-6764" } 19 | ListElement { firstName: "Alline"; lastName: "Jeanty"; mobileNumber: "574-656-2800" } 20 | ListElement { firstName: "Alpha"; lastName: "Palaia"; mobileNumber: "856-312-2629" } 21 | ListElement { firstName: "Alyce"; lastName: "Arias"; mobileNumber: "209-317-1801" } 22 | ListElement { firstName: "Amber"; lastName: "Monarrez"; mobileNumber: "215-934-8655" } 23 | ListElement { firstName: "Amie"; lastName: "Perigo"; mobileNumber: "972-419-7946" } 24 | ListElement { firstName: "Ammie"; lastName: "Corrio"; mobileNumber: "614-801-9788" } 25 | ListElement { firstName: "An"; lastName: "Fritz"; mobileNumber: "609-228-5265" } 26 | ListElement { firstName: "Andra"; lastName: "Scheyer"; mobileNumber: "503-516-2189" } 27 | ListElement { firstName: "Angella"; lastName: "Cetta"; mobileNumber: "808-892-7943" } 28 | ListElement { firstName: "Annabelle"; lastName: "Boord"; mobileNumber: "978-697-6263" } 29 | ListElement { firstName: "Annelle"; lastName: "Tagala"; mobileNumber: "410-757-1035" } 30 | ListElement { firstName: "Annmarie"; lastName: "Castros"; mobileNumber: "936-751-7961" } 31 | ListElement { firstName: "Antione"; lastName: "Onofrio"; mobileNumber: "909-430-7765" } 32 | ListElement { firstName: "Arlene"; lastName: "Klusman"; mobileNumber: "504-710-5840" } 33 | ListElement { firstName: "Arlette"; lastName: "Honeywell"; mobileNumber: "904-775-4480" } 34 | ListElement { firstName: "Arminda"; lastName: "Parvis"; mobileNumber: "602-906-9419" } 35 | ListElement { firstName: "Art"; lastName: "Venere"; mobileNumber: "856-636-8749" } 36 | ListElement { firstName: "Arthur"; lastName: "Farrow"; mobileNumber: "201-238-5688" } 37 | ListElement { firstName: "Ashlyn"; lastName: "Pinilla"; mobileNumber: "305-670-9628" } 38 | ListElement { firstName: "Audra"; lastName: "Kohnert"; mobileNumber: "615-406-7854" } 39 | ListElement { firstName: "Audry"; lastName: "Yaw"; mobileNumber: "813-797-4816" } 40 | ListElement { firstName: "Avery"; lastName: "Steier"; mobileNumber: "407-808-9439" } 41 | ListElement { firstName: "Barbra"; lastName: "Adkin"; mobileNumber: "718-201-3751" } 42 | ListElement { firstName: "Barrett"; lastName: "Toyama"; mobileNumber: "817-765-5781" } 43 | ListElement { firstName: "Beatriz"; lastName: "Corrington"; mobileNumber: "508-584-4279" } 44 | ListElement { firstName: "Beckie"; lastName: "Silvestrini"; mobileNumber: "313-533-4884" } 45 | ListElement { firstName: "Becky"; lastName: "Mirafuentes"; mobileNumber: "908-877-8409" } 46 | ListElement { firstName: "Belen"; lastName: "Strassner"; mobileNumber: "770-507-8791" } 47 | ListElement { firstName: "Benedict"; lastName: "Sama"; mobileNumber: "314-787-1588" } 48 | ListElement { firstName: "Benton"; lastName: "Skursky"; mobileNumber: "310-579-2907" } 49 | ListElement { firstName: "Bernardine"; lastName: "Rodefer"; mobileNumber: "901-901-4726" } 50 | ListElement { firstName: "Bernardo"; lastName: "Figeroa"; mobileNumber: "936-336-3951" } 51 | ListElement { firstName: "Bette"; lastName: "Nicka"; mobileNumber: "610-545-3615" } 52 | ListElement { firstName: "Billye"; lastName: "Miro"; mobileNumber: "601-567-5386" } 53 | ListElement { firstName: "Blair"; lastName: "Malet"; mobileNumber: "215-907-9111" } 54 | ListElement { firstName: "Blondell"; lastName: "Pugh"; mobileNumber: "401-960-8259" } 55 | ListElement { firstName: "Bobbye"; lastName: "Rhym"; mobileNumber: "650-528-5783" } 56 | ListElement { firstName: "Bok"; lastName: "Isaacs"; mobileNumber: "718-809-3762" } 57 | ListElement { firstName: "Brandon"; lastName: "Callaro"; mobileNumber: "808-215-6832" } 58 | ListElement { firstName: "Brett"; lastName: "Mccullan"; mobileNumber: "619-461-9984" } 59 | ListElement { firstName: "Britt"; lastName: "Galam"; mobileNumber: "215-888-3304" } 60 | ListElement { firstName: "Brittni"; lastName: "Gillaspie"; mobileNumber: "208-709-1235" } 61 | ListElement { firstName: "Brock"; lastName: "Bolognia"; mobileNumber: "212-402-9216" } 62 | ListElement { firstName: "Buddy"; lastName: "Cloney"; mobileNumber: "440-989-5826" } 63 | ListElement { firstName: "Bulah"; lastName: "Padilla"; mobileNumber: "254-463-4368" } 64 | ListElement { firstName: "Caitlin"; lastName: "Julia"; mobileNumber: "401-948-4982" } 65 | ListElement { firstName: "Cammy"; lastName: "Albares"; mobileNumber: "956-537-6195" } 66 | ListElement { firstName: "Candida"; lastName: "Corbley"; mobileNumber: "908-275-8357" } 67 | ListElement { firstName: "Caprice"; lastName: "Suell"; mobileNumber: "615-246-1824" } 68 | ListElement { firstName: "Carey"; lastName: "Dopico"; mobileNumber: "317-578-2453" } 69 | ListElement { firstName: "Carin"; lastName: "Deleo"; mobileNumber: "501-308-1040" } 70 | ListElement { firstName: "Carissa"; lastName: "Batman"; mobileNumber: "541-326-4074" } 71 | ListElement { firstName: "Carlee"; lastName: "Boulter"; mobileNumber: "785-347-1805" } 72 | ListElement { firstName: "Carma"; lastName: "Vanheusen"; mobileNumber: "510-503-7169" } 73 | ListElement { firstName: "Carmela"; lastName: "Cookey"; mobileNumber: "773-494-4195" } 74 | ListElement { firstName: "Carmelina"; lastName: "Lindall"; mobileNumber: "303-724-7371" } 75 | ListElement { firstName: "Carmen"; lastName: "Sweigard"; mobileNumber: "732-941-2621" } 76 | ListElement { firstName: "Casie"; lastName: "Good"; mobileNumber: "615-390-2251" } 77 | ListElement { firstName: "Cassi"; lastName: "Wildfong"; mobileNumber: "847-633-3216" } 78 | ListElement { firstName: "Catalina"; lastName: "Tillotson"; mobileNumber: "609-373-3332" } 79 | ListElement { firstName: "Catarina"; lastName: "Gleich"; mobileNumber: "973-210-3994" } 80 | ListElement { firstName: "Cathrine"; lastName: "Pontoriero"; mobileNumber: "806-703-1435" } 81 | ListElement { firstName: "Cecil"; lastName: "Lapage"; mobileNumber: "201-693-3967" } 82 | ListElement { firstName: "Cecilia"; lastName: "Colaizzo"; mobileNumber: "608-382-4541" } 83 | ListElement { firstName: "Cecily"; lastName: "Hollack"; mobileNumber: "512-486-3817" } 84 | ListElement { firstName: "Celeste"; lastName: "Korando"; mobileNumber: "516-509-2347" } 85 | ListElement { firstName: "Ceola"; lastName: "Setter"; mobileNumber: "207-627-7565" } 86 | ListElement { firstName: "Chanel"; lastName: "Caudy"; mobileNumber: "913-388-2079" } 87 | ListElement { firstName: "Chantell"; lastName: "Maynerich"; mobileNumber: "651-591-2583" } 88 | ListElement { firstName: "Charlene"; lastName: "Hamilton"; mobileNumber: "707-300-1771" } 89 | ListElement { firstName: "Chau"; lastName: "Kitzman"; mobileNumber: "310-560-8022" } 90 | ListElement { firstName: "Chauncey"; lastName: "Motley"; mobileNumber: "407-413-4842" } 91 | ListElement { firstName: "Chaya"; lastName: "Malvin"; mobileNumber: "734-928-5182" } 92 | ListElement { firstName: "Cherry"; lastName: "Lietz"; mobileNumber: "248-980-6904" } 93 | ListElement { firstName: "Cheryl"; lastName: "Haroldson"; mobileNumber: "609-518-7697" } 94 | ListElement { firstName: "Christiane"; lastName: "Eschberger"; mobileNumber: "602-390-4944" } 95 | ListElement { firstName: "Ciara"; lastName: "Ventura"; mobileNumber: "845-823-8877" } 96 | ListElement { firstName: "Claribel"; lastName: "Varriano"; mobileNumber: "419-544-4900" } 97 | ListElement { firstName: "Clay"; lastName: "Hoa"; mobileNumber: "775-501-8109" } 98 | ListElement { firstName: "Clorinda"; lastName: "Heimann"; mobileNumber: "760-291-5497" } 99 | ListElement { firstName: "Colette"; lastName: "Kardas"; mobileNumber: "402-896-5943" } 100 | ListElement { firstName: "Cordelia"; lastName: "Storment"; mobileNumber: "337-566-6001" } 101 | ListElement { firstName: "Corinne"; lastName: "Loder"; mobileNumber: "508-942-4186" } 102 | ListElement { firstName: "Cory"; lastName: "Gibes"; mobileNumber: "626-572-1096" } 103 | ListElement { firstName: "Cristal"; lastName: "Samara"; mobileNumber: "213-975-8026" } 104 | ListElement { firstName: "Cristy"; lastName: "Lother"; mobileNumber: "760-971-4322" } 105 | ListElement { firstName: "Cyndy"; lastName: "Goldammer"; mobileNumber: "952-334-9408" } 106 | ListElement { firstName: "Cyril"; lastName: "Daufeldt"; mobileNumber: "212-745-8484" } 107 | ListElement { firstName: "Dalene"; lastName: "Riden"; mobileNumber: "603-315-6839" } 108 | ListElement { firstName: "Dalene"; lastName: "Schoeneck"; mobileNumber: "215-268-1275" } 109 | ListElement { firstName: "Danica"; lastName: "Bruschke"; mobileNumber: "254-782-8569" } 110 | ListElement { firstName: "Daniel"; lastName: "Perruzza"; mobileNumber: "714-771-3880" } 111 | ListElement { firstName: "Daniela"; lastName: "Comnick"; mobileNumber: "609-200-8577" } 112 | ListElement { firstName: "Daren"; lastName: "Weirather"; mobileNumber: "414-959-2540" } 113 | ListElement { firstName: "Daron"; lastName: "Dinos"; mobileNumber: "847-233-3075" } 114 | ListElement { firstName: "Dean"; lastName: "Ketelsen"; mobileNumber: "516-847-4418" } 115 | ListElement { firstName: "Deandrea"; lastName: "Hughey"; mobileNumber: "336-822-7652" } 116 | ListElement { firstName: "Deeanna"; lastName: "Juhas"; mobileNumber: "215-211-9589" } 117 | ListElement { firstName: "Delisa"; lastName: "Crupi"; mobileNumber: "973-354-2040" } 118 | ListElement { firstName: "Delmy"; lastName: "Ahle"; mobileNumber: "401-458-2547" } 119 | ListElement { firstName: "Denise"; lastName: "Patak"; mobileNumber: "407-446-4358" } 120 | ListElement { firstName: "Deonna"; lastName: "Kippley"; mobileNumber: "248-913-4677" } 121 | ListElement { firstName: "Derick"; lastName: "Dhamer"; mobileNumber: "212-304-4515" } 122 | ListElement { firstName: "Detra"; lastName: "Coyier"; mobileNumber: "410-739-9277" } 123 | ListElement { firstName: "Devora"; lastName: "Perez"; mobileNumber: "510-955-3016" } 124 | ListElement { firstName: "Devorah"; lastName: "Chickering"; mobileNumber: "505-975-8559" } 125 | ListElement { firstName: "Diane"; lastName: "Devreese"; mobileNumber: "816-557-9673" } 126 | ListElement { firstName: "Dick"; lastName: "Wenzinger"; mobileNumber: "310-510-9713" } 127 | ListElement { firstName: "Dierdre"; lastName: "Yum"; mobileNumber: "215-325-3042" } 128 | ListElement { firstName: "Dominque"; lastName: "Dickerson"; mobileNumber: "510-993-3758" } 129 | ListElement { firstName: "Donette"; lastName: "Foller"; mobileNumber: "513-570-1893" } 130 | ListElement { firstName: "Donte"; lastName: "Kines"; mobileNumber: "508-429-8576" } 131 | ListElement { firstName: "Dorothy"; lastName: "Chesterfield"; mobileNumber: "858-617-7834" } 132 | ListElement { firstName: "Dorthy"; lastName: "Hidvegi"; mobileNumber: "208-649-2373" } 133 | ListElement { firstName: "Dottie"; lastName: "Hellickson"; mobileNumber: "206-540-6076" } 134 | ListElement { firstName: "Dulce"; lastName: "Labreche"; mobileNumber: "248-357-8718" } 135 | ListElement { firstName: "Dyan"; lastName: "Oldroyd"; mobileNumber: "913-413-4604" } 136 | ListElement { firstName: "Earleen"; lastName: "Mai"; mobileNumber: "214-289-1973" } 137 | ListElement { firstName: "Eden"; lastName: "Jayson"; mobileNumber: "410-890-7866" } 138 | ListElement { firstName: "Edna"; lastName: "Miceli"; mobileNumber: "814-460-2655" } 139 | ListElement { firstName: "Eladia"; lastName: "Saulter"; mobileNumber: "201-474-4924" } 140 | ListElement { firstName: "Elke"; lastName: "Sengbusch"; mobileNumber: "602-896-2993" } 141 | ListElement { firstName: "Elli"; lastName: "Mclaird"; mobileNumber: "315-818-2638" } 142 | ListElement { firstName: "Elly"; lastName: "Morocco"; mobileNumber: "814-393-5571" } 143 | ListElement { firstName: "Elouise"; lastName: "Gwalthney"; mobileNumber: "301-841-5012" } 144 | ListElement { firstName: "Elvera"; lastName: "Benimadho"; mobileNumber: "408-703-8505" } 145 | ListElement { firstName: "Elza"; lastName: "Lipke"; mobileNumber: "973-927-3447" } 146 | ListElement { firstName: "Emerson"; lastName: "Bowley"; mobileNumber: "608-336-7444" } 147 | ListElement { firstName: "Erick"; lastName: "Ferencz"; mobileNumber: "907-741-1044" } 148 | ListElement { firstName: "Erick"; lastName: "Nievas"; mobileNumber: "773-704-9903" } 149 | ListElement { firstName: "Erinn"; lastName: "Canlas"; mobileNumber: "973-767-3008" } 150 | ListElement { firstName: "Ernest"; lastName: "Syrop"; mobileNumber: "301-998-9644" } 151 | ListElement { firstName: "Ernie"; lastName: "Stenseth"; mobileNumber: "201-709-6245" } 152 | ListElement { firstName: "Estrella"; lastName: "Samu"; mobileNumber: "608-976-7199" } 153 | ListElement { firstName: "Ettie"; lastName: "Hoopengardner"; mobileNumber: "509-755-5393" } 154 | ListElement { firstName: "Eun"; lastName: "Coody"; mobileNumber: "864-256-3620" } 155 | ListElement { firstName: "Evangelina"; lastName: "Radde"; mobileNumber: "215-964-3284" } 156 | ListElement { firstName: "Ezekiel"; lastName: "Chui"; mobileNumber: "410-669-1642" } 157 | ListElement { firstName: "Fabiola"; lastName: "Hauenstein"; mobileNumber: "717-809-3119" } 158 | ListElement { firstName: "Fannie"; lastName: "Lungren"; mobileNumber: "512-587-5746" } 159 | ListElement { firstName: "Fatima"; lastName: "Saylors"; mobileNumber: "952-768-2416" } 160 | ListElement { firstName: "Fausto"; lastName: "Agramonte"; mobileNumber: "212-313-1783" } 161 | ListElement { firstName: "Felicidad"; lastName: "Poullion"; mobileNumber: "856-305-9731" } 162 | ListElement { firstName: "Felix"; lastName: "Hirpara"; mobileNumber: "717-491-5643" } 163 | ListElement { firstName: "Fernanda"; lastName: "Jillson"; mobileNumber: "410-387-5260" } 164 | ListElement { firstName: "Filiberto"; lastName: "Tawil"; mobileNumber: "323-765-2528" } 165 | ListElement { firstName: "Fletcher"; lastName: "Flosi"; mobileNumber: "815-828-2147" } 166 | ListElement { firstName: "Flo"; lastName: "Bookamer"; mobileNumber: "308-726-2182" } 167 | ListElement { firstName: "France"; lastName: "Buzick"; mobileNumber: "718-478-8504" } 168 | ListElement { firstName: "Francine"; lastName: "Vocelka"; mobileNumber: "505-977-3911" } 169 | ListElement { firstName: "Franklyn"; lastName: "Emard"; mobileNumber: "215-558-8189" } 170 | ListElement { firstName: "Frederica"; lastName: "Blunk"; mobileNumber: "214-428-2285" } 171 | ListElement { firstName: "Freeman"; lastName: "Gochal"; mobileNumber: "610-476-3501" } 172 | ListElement { firstName: "Gail"; lastName: "Kitty"; mobileNumber: "907-435-9166" } 173 | ListElement { firstName: "Gail"; lastName: "Similton"; mobileNumber: "760-616-5388" } 174 | ListElement { firstName: "Galen"; lastName: "Cantres"; mobileNumber: "216-600-6111" } 175 | ListElement { firstName: "Garry"; lastName: "Keetch"; mobileNumber: "215-979-8776" } 176 | ListElement { firstName: "Gary"; lastName: "Nunlee"; mobileNumber: "317-542-6023" } 177 | ListElement { firstName: "Gayla"; lastName: "Schnitzler"; mobileNumber: "510-686-3407" } 178 | ListElement { firstName: "Gearldine"; lastName: "Gellinger"; mobileNumber: "972-934-6914" } 179 | ListElement { firstName: "Geoffrey"; lastName: "Acey"; mobileNumber: "847-222-1734" } 180 | ListElement { firstName: "Georgene"; lastName: "Montezuma"; mobileNumber: "925-615-5185" } 181 | ListElement { firstName: "Gertude"; lastName: "Witten"; mobileNumber: "513-977-7043" } 182 | ListElement { firstName: "Gilma"; lastName: "Liukko"; mobileNumber: "516-393-9967" } 183 | ListElement { firstName: "Gladys"; lastName: "Rim"; mobileNumber: "414-661-9598" } 184 | ListElement { firstName: "Glen"; lastName: "Bartolet"; mobileNumber: "206-697-5796" } 185 | ListElement { firstName: "Glendora"; lastName: "Sarbacher"; mobileNumber: "707-653-8214" } 186 | ListElement { firstName: "Glenn"; lastName: "Berray"; mobileNumber: "515-370-7348" } 187 | ListElement { firstName: "Glenna"; lastName: "Slayton"; mobileNumber: "901-640-9178" } 188 | ListElement { firstName: "Glory"; lastName: "Kulzer"; mobileNumber: "410-224-9462" } 189 | ListElement { firstName: "Glory"; lastName: "Schieler"; mobileNumber: "325-869-2649" } 190 | ListElement { firstName: "Golda"; lastName: "Kaniecki"; mobileNumber: "732-628-9909" } 191 | ListElement { firstName: "Goldie"; lastName: "Schirpke"; mobileNumber: "207-295-7569" } 192 | ListElement { firstName: "Gracia"; lastName: "Melnyk"; mobileNumber: "904-235-3633" } 193 | ListElement { firstName: "Graciela"; lastName: "Ruta"; mobileNumber: "440-780-8425" } 194 | ListElement { firstName: "Gregoria"; lastName: "Pawlowicz"; mobileNumber: "516-212-1915" } 195 | ListElement { firstName: "Gwenn"; lastName: "Suffield"; mobileNumber: "631-258-6558" } 196 | ListElement { firstName: "Harrison"; lastName: "Haufler"; mobileNumber: "203-801-6193" } 197 | ListElement { firstName: "Haydee"; lastName: "Denooyer"; mobileNumber: "212-792-8658" } 198 | ListElement { firstName: "Heike"; lastName: "Berganza"; mobileNumber: "973-936-5095" } 199 | ListElement { firstName: "Helaine"; lastName: "Halter"; mobileNumber: "201-832-4168" } 200 | ListElement { firstName: "Helene"; lastName: "Rodenberger"; mobileNumber: "623-461-8551" } 201 | ListElement { firstName: "Helga"; lastName: "Fredicks"; mobileNumber: "716-752-4114" } 202 | ListElement { firstName: "Herman"; lastName: "Demesa"; mobileNumber: "518-497-2940" } 203 | ListElement { firstName: "Hermila"; lastName: "Thyberg"; mobileNumber: "401-893-4882" } 204 | ListElement { firstName: "Herminia"; lastName: "Nicolozakes"; mobileNumber: "602-954-5141" } 205 | ListElement { firstName: "Hillary"; lastName: "Skulski"; mobileNumber: "352-242-2570" } 206 | ListElement { firstName: "Hoa"; lastName: "Sarao"; mobileNumber: "386-526-7800" } 207 | ListElement { firstName: "Howard"; lastName: "Paulas"; mobileNumber: "303-623-4241" } 208 | ListElement { firstName: "Hubert"; lastName: "Walthall"; mobileNumber: "330-903-1345" } 209 | ListElement { firstName: "Ilene"; lastName: "Eroman"; mobileNumber: "410-914-9018" } 210 | ListElement { firstName: "Iluminada"; lastName: "Ohms"; mobileNumber: "480-293-2882" } 211 | ListElement { firstName: "Irma"; lastName: "Wolfgramm"; mobileNumber: "973-545-7355" } 212 | ListElement { firstName: "Izetta"; lastName: "Funnell"; mobileNumber: "770-844-3447" } 213 | ListElement { firstName: "Izetta"; lastName: "Dewar"; mobileNumber: "410-473-1708" } 214 | ListElement { firstName: "Jaclyn"; lastName: "Bachman"; mobileNumber: "719-853-3600" } 215 | ListElement { firstName: "Jacqueline"; lastName: "Rowling"; mobileNumber: "814-865-8113" } 216 | ListElement { firstName: "Jade"; lastName: "Farrar"; mobileNumber: "803-352-5387" } 217 | ListElement { firstName: "Jamal"; lastName: "Vanausdal"; mobileNumber: "732-234-1546" } 218 | ListElement { firstName: "James"; lastName: "Butt"; mobileNumber: "504-621-8927" } 219 | ListElement { firstName: "Janey"; lastName: "Gabisi"; mobileNumber: "608-967-7194" } 220 | ListElement { firstName: "Jani"; lastName: "Biddy"; mobileNumber: "206-711-6498" } 221 | ListElement { firstName: "Janine"; lastName: "Rhoden"; mobileNumber: "718-228-5894" } 222 | ListElement { firstName: "Jeanice"; lastName: "Claucherty"; mobileNumber: "305-988-4162" } 223 | ListElement { firstName: "Jenelle"; lastName: "Regusters"; mobileNumber: "813-932-8715" } 224 | ListElement { firstName: "Jennie"; lastName: "Drymon"; mobileNumber: "570-218-4831" } 225 | ListElement { firstName: "Jennifer"; lastName: "Fallick"; mobileNumber: "847-979-9545" } 226 | ListElement { firstName: "Jerry"; lastName: "Zurcher"; mobileNumber: "321-518-5938" } 227 | ListElement { firstName: "Jerry"; lastName: "Dallen"; mobileNumber: "804-762-9576" } 228 | ListElement { firstName: "Jess"; lastName: "Chaffins"; mobileNumber: "212-510-4633" } 229 | ListElement { firstName: "Jesusa"; lastName: "Shin"; mobileNumber: "931-273-8709" } 230 | ListElement { firstName: "Jesusita"; lastName: "Flister"; mobileNumber: "717-885-9118" } 231 | ListElement { firstName: "Jettie"; lastName: "Mconnell"; mobileNumber: "908-802-3564" } 232 | ListElement { firstName: "Jina"; lastName: "Briddick"; mobileNumber: "617-399-5124" } 233 | ListElement { firstName: "Joanna"; lastName: "Leinenbach"; mobileNumber: "561-470-4574" } 234 | ListElement { firstName: "Joesph"; lastName: "Degonia"; mobileNumber: "510-677-9785" } 235 | ListElement { firstName: "Johnetta"; lastName: "Abdallah"; mobileNumber: "919-225-9345" } 236 | ListElement { firstName: "Johnna"; lastName: "Engelberg"; mobileNumber: "425-986-7573" } 237 | ListElement { firstName: "Jolanda"; lastName: "Hanafan"; mobileNumber: "207-458-9196" } 238 | ListElement { firstName: "Jolene"; lastName: "Ostolaza"; mobileNumber: "757-682-7116" } 239 | ListElement { firstName: "Joni"; lastName: "Breland"; mobileNumber: "847-519-5906" } 240 | ListElement { firstName: "Jose"; lastName: "Stockham"; mobileNumber: "212-675-8570" } 241 | ListElement { firstName: "Joseph"; lastName: "Cryer"; mobileNumber: "714-584-2237" } 242 | ListElement { firstName: "Josephine"; lastName: "Darakjy"; mobileNumber: "810-292-9388" } 243 | ListElement { firstName: "Josphine"; lastName: "Villanueva"; mobileNumber: "931-553-9774" } 244 | ListElement { firstName: "Jovita"; lastName: "Oles"; mobileNumber: "386-248-4118" } 245 | ListElement { firstName: "Judy"; lastName: "Aquas"; mobileNumber: "269-756-7222" } 246 | ListElement { firstName: "Junita"; lastName: "Brideau"; mobileNumber: "973-943-3423" } 247 | ListElement { firstName: "Junita"; lastName: "Stoltzman"; mobileNumber: "775-638-9963" } 248 | ListElement { firstName: "Justine"; lastName: "Mugnolo"; mobileNumber: "212-304-9225" } 249 | ListElement { firstName: "Justine"; lastName: "Ferrario"; mobileNumber: "909-993-3242" } 250 | ListElement { firstName: "Jutta"; lastName: "Amyot"; mobileNumber: "337-515-1438" } 251 | ListElement { firstName: "Kaitlyn"; lastName: "Ogg"; mobileNumber: "410-665-4903" } 252 | ListElement { firstName: "Kallie"; lastName: "Blackwood"; mobileNumber: "415-315-2761" } 253 | ListElement { firstName: "Kanisha"; lastName: "Waycott"; mobileNumber: "323-453-2780" } 254 | ListElement { firstName: "Karan"; lastName: "Karpin"; mobileNumber: "503-940-8327" } 255 | ListElement { firstName: "Karl"; lastName: "Klonowski"; mobileNumber: "908-877-6135" } 256 | ListElement { firstName: "Kasandra"; lastName: "Semidey"; mobileNumber: "314-732-9131" } 257 | ListElement { firstName: "Kate"; lastName: "Keneipp"; mobileNumber: "920-353-6377" } 258 | ListElement { firstName: "Kati"; lastName: "Rulapaugh"; mobileNumber: "785-463-7829" } 259 | ListElement { firstName: "Katina"; lastName: "Polidori"; mobileNumber: "978-626-2978" } 260 | ListElement { firstName: "Kattie"; lastName: "Vonasek"; mobileNumber: "216-923-3715" } 261 | ListElement { firstName: "Kayleigh"; lastName: "Lace"; mobileNumber: "337-740-9323" } 262 | ListElement { firstName: "Keneth"; lastName: "Borgman"; mobileNumber: "602-919-4211" } 263 | ListElement { firstName: "Kenneth"; lastName: "Grenet"; mobileNumber: "517-499-2322" } 264 | ListElement { firstName: "Kerry"; lastName: "Theodorov"; mobileNumber: "916-591-3277" } 265 | ListElement { firstName: "Kiley"; lastName: "Caldarera"; mobileNumber: "310-498-5651" } 266 | ListElement { firstName: "Kimberlie"; lastName: "Duenas"; mobileNumber: "785-629-8542" } 267 | ListElement { firstName: "Kimbery"; lastName: "Madarang"; mobileNumber: "973-310-1634" } 268 | ListElement { firstName: "Kirk"; lastName: "Herritt"; mobileNumber: "607-407-3716" } 269 | ListElement { firstName: "Kris"; lastName: "Marrier"; mobileNumber: "410-655-8723" } 270 | ListElement { firstName: "Krissy"; lastName: "Rauser"; mobileNumber: "631-443-4710" } 271 | ListElement { firstName: "Kristeen"; lastName: "Turinetti"; mobileNumber: "817-213-8851" } 272 | ListElement { firstName: "Kristel"; lastName: "Ehmann"; mobileNumber: "915-452-1290" } 273 | ListElement { firstName: "Kristofer"; lastName: "Bennick"; mobileNumber: "812-368-1511" } 274 | ListElement { firstName: "Lai"; lastName: "Gato"; mobileNumber: "847-728-7286" } 275 | ListElement { firstName: "Lai"; lastName: "Harabedian"; mobileNumber: "415-423-3294" } 276 | ListElement { firstName: "Larae"; lastName: "Gudroe"; mobileNumber: "985-890-7262" } 277 | ListElement { firstName: "Lashandra"; lastName: "Klang"; mobileNumber: "610-809-1818" } 278 | ListElement { firstName: "Lashaunda"; lastName: "Lizama"; mobileNumber: "410-678-2473" } 279 | ListElement { firstName: "Lashawnda"; lastName: "Stuer"; mobileNumber: "419-588-8719" } 280 | ListElement { firstName: "Lashon"; lastName: "Vizarro"; mobileNumber: "916-741-7884" } 281 | ListElement { firstName: "Laticia"; lastName: "Merced"; mobileNumber: "513-508-7371" } 282 | ListElement { firstName: "Latrice"; lastName: "Tolfree"; mobileNumber: "631-957-7624" } 283 | ListElement { firstName: "Lauran"; lastName: "Burnard"; mobileNumber: "307-342-7795" } 284 | ListElement { firstName: "Laurel"; lastName: "Reitler"; mobileNumber: "410-520-4832" } 285 | ListElement { firstName: "Laurel"; lastName: "Pagliuca"; mobileNumber: "509-695-5199" } 286 | ListElement { firstName: "Lavera"; lastName: "Perin"; mobileNumber: "305-606-7291" } 287 | ListElement { firstName: "Lavonda"; lastName: "Hengel"; mobileNumber: "701-898-2154" } 288 | ListElement { firstName: "Lavonna"; lastName: "Wolny"; mobileNumber: "703-483-1970" } 289 | ListElement { firstName: "Lawrence"; lastName: "Lorens"; mobileNumber: "401-465-6432" } 290 | ListElement { firstName: "Layla"; lastName: "Springe"; mobileNumber: "212-260-3151" } 291 | ListElement { firstName: "Leatha"; lastName: "Hagele"; mobileNumber: "214-339-1809" } 292 | ListElement { firstName: "Lemuel"; lastName: "Latzke"; mobileNumber: "631-748-6479" } 293 | ListElement { firstName: "Lenna"; lastName: "Paprocki"; mobileNumber: "907-385-4412" } 294 | ListElement { firstName: "Lenna"; lastName: "Newville"; mobileNumber: "919-623-2524" } 295 | ListElement { firstName: "Leonida"; lastName: "Gobern"; mobileNumber: "228-235-5615" } 296 | ListElement { firstName: "Leonora"; lastName: "Mauson"; mobileNumber: "973-412-2995" } 297 | ListElement { firstName: "Leota"; lastName: "Dilliard"; mobileNumber: "408-752-3500" } 298 | ListElement { firstName: "Leota"; lastName: "Ragel"; mobileNumber: "706-221-4243" } 299 | ListElement { firstName: "Leslie"; lastName: "Threets"; mobileNumber: "914-861-9748" } 300 | ListElement { firstName: "Lettie"; lastName: "Isenhower"; mobileNumber: "216-657-7668" } 301 | ListElement { firstName: "Levi"; lastName: "Munis"; mobileNumber: "508-456-4907" } 302 | ListElement { firstName: "Lezlie"; lastName: "Craghead"; mobileNumber: "919-533-3762" } 303 | ListElement { firstName: "Ligia"; lastName: "Reiber"; mobileNumber: "517-906-1108" } 304 | ListElement { firstName: "Lili"; lastName: "Paskin"; mobileNumber: "201-431-2989" } 305 | ListElement { firstName: "Lilli"; lastName: "Scriven"; mobileNumber: "325-631-1560" } 306 | ListElement { firstName: "Lindsey"; lastName: "Dilello"; mobileNumber: "909-639-9887" } 307 | ListElement { firstName: "Linn"; lastName: "Paa"; mobileNumber: "901-412-4381" } 308 | ListElement { firstName: "Lisha"; lastName: "Centini"; mobileNumber: "703-235-3937" } 309 | ListElement { firstName: "Lizbeth"; lastName: "Kohl"; mobileNumber: "310-699-1222" } 310 | ListElement { firstName: "Lizette"; lastName: "Stem"; mobileNumber: "856-487-5412" } 311 | ListElement { firstName: "Lonna"; lastName: "Diestel"; mobileNumber: "910-922-3672" } 312 | ListElement { firstName: "Lonny"; lastName: "Weglarz"; mobileNumber: "801-293-9853" } 313 | ListElement { firstName: "Lorean"; lastName: "Martabano"; mobileNumber: "210-856-4979" } 314 | ListElement { firstName: "Loren"; lastName: "Asar"; mobileNumber: "570-648-3035" } 315 | ListElement { firstName: "Loreta"; lastName: "Timenez"; mobileNumber: "301-696-6420" } 316 | ListElement { firstName: "Lorrie"; lastName: "Nestle"; mobileNumber: "931-875-6644" } 317 | ListElement { firstName: "Lorrine"; lastName: "Worlds"; mobileNumber: "813-769-2939" } 318 | ListElement { firstName: "Louisa"; lastName: "Cronauer"; mobileNumber: "510-828-7047" } 319 | ListElement { firstName: "Louvenia"; lastName: "Beech"; mobileNumber: "310-820-2117" } 320 | ListElement { firstName: "Lucina"; lastName: "Lary"; mobileNumber: "321-749-4981" } 321 | ListElement { firstName: "Lucy"; lastName: "Treston"; mobileNumber: "508-769-5250" } 322 | ListElement { firstName: "Luisa"; lastName: "Jurney"; mobileNumber: "617-365-2134" } 323 | ListElement { firstName: "Lynelle"; lastName: "Auber"; mobileNumber: "973-860-8610" } 324 | ListElement { firstName: "Ma"; lastName: "Layous"; mobileNumber: "203-721-3388" } 325 | ListElement { firstName: "Maile"; lastName: "Linahan"; mobileNumber: "336-670-2640" } 326 | ListElement { firstName: "Malcolm"; lastName: "Tromblay"; mobileNumber: "703-221-5602" } 327 | ListElement { firstName: "Malinda"; lastName: "Hochard"; mobileNumber: "317-722-5066" } 328 | ListElement { firstName: "Margart"; lastName: "Meisel"; mobileNumber: "513-617-2362" } 329 | ListElement { firstName: "Marge"; lastName: "Limmel"; mobileNumber: "850-430-1663" } 330 | ListElement { firstName: "Marguerita"; lastName: "Hiatt"; mobileNumber: "925-634-7158" } 331 | ListElement { firstName: "Mari"; lastName: "Lueckenbach"; mobileNumber: "858-793-9684" } 332 | ListElement { firstName: "Mariann"; lastName: "Bilden"; mobileNumber: "512-223-4791" } 333 | ListElement { firstName: "Marjory"; lastName: "Mastella"; mobileNumber: "610-814-5533" } 334 | ListElement { firstName: "Markus"; lastName: "Lukasik"; mobileNumber: "586-970-7380" } 335 | ListElement { firstName: "Marti"; lastName: "Maybury"; mobileNumber: "773-775-4522" } 336 | ListElement { firstName: "Martina"; lastName: "Staback"; mobileNumber: "407-471-6908" } 337 | ListElement { firstName: "Marvel"; lastName: "Raymo"; mobileNumber: "979-718-8968" } 338 | ListElement { firstName: "Maryann"; lastName: "Royster"; mobileNumber: "518-966-7987" } 339 | ListElement { firstName: "Matthew"; lastName: "Neither"; mobileNumber: "952-651-7597" } 340 | ListElement { firstName: "Mattie"; lastName: "Poquette"; mobileNumber: "602-277-4385" } 341 | ListElement { firstName: "Maurine"; lastName: "Yglesias"; mobileNumber: "414-748-1374" } 342 | ListElement { firstName: "Meaghan"; lastName: "Garufi"; mobileNumber: "931-313-9635" } 343 | ListElement { firstName: "Melissa"; lastName: "Wiklund"; mobileNumber: "419-939-3613" } 344 | ListElement { firstName: "Melodie"; lastName: "Knipp"; mobileNumber: "805-690-1682" } 345 | ListElement { firstName: "Merilyn"; lastName: "Bayless"; mobileNumber: "408-758-5015" } 346 | ListElement { firstName: "Merissa"; lastName: "Tomblin"; mobileNumber: "562-579-6900" } 347 | ListElement { firstName: "Merlyn"; lastName: "Lawler"; mobileNumber: "201-588-7810" } 348 | ListElement { firstName: "Micaela"; lastName: "Rhymes"; mobileNumber: "925-647-3298" } 349 | ListElement { firstName: "Minna"; lastName: "Amigon"; mobileNumber: "215-874-1229" } 350 | ListElement { firstName: "Mireya"; lastName: "Frerking"; mobileNumber: "914-868-5965" } 351 | ListElement { firstName: "Mirta"; lastName: "Mallett"; mobileNumber: "212-870-1286" } 352 | ListElement { firstName: "Mitsue"; lastName: "Tollner"; mobileNumber: "773-573-6914" } 353 | ListElement { firstName: "Mitsue"; lastName: "Scipione"; mobileNumber: "530-986-9272" } 354 | ListElement { firstName: "Mitzie"; lastName: "Hudnall"; mobileNumber: "303-402-1940" } 355 | ListElement { firstName: "Mollie"; lastName: "Mcdoniel"; mobileNumber: "419-975-3182" } 356 | ListElement { firstName: "Mona"; lastName: "Delasancha"; mobileNumber: "307-403-1488" } 357 | ListElement { firstName: "Moon"; lastName: "Parlato"; mobileNumber: "585-866-8313" } 358 | ListElement { firstName: "Mozell"; lastName: "Pelkowski"; mobileNumber: "650-947-1215" } 359 | ListElement { firstName: "My"; lastName: "Rantanen"; mobileNumber: "215-491-5633" } 360 | ListElement { firstName: "Myra"; lastName: "Munns"; mobileNumber: "817-914-7518" } 361 | ListElement { firstName: "Nan"; lastName: "Koppinger"; mobileNumber: "336-370-5333" } 362 | ListElement { firstName: "Nana"; lastName: "Wrinkles"; mobileNumber: "914-855-2115" } 363 | ListElement { firstName: "Natalie"; lastName: "Fern"; mobileNumber: "307-704-8713" } 364 | ListElement { firstName: "Nelida"; lastName: "Sawchuk"; mobileNumber: "201-971-1638" } 365 | ListElement { firstName: "Nichelle"; lastName: "Meteer"; mobileNumber: "773-225-9985" } 366 | ListElement { firstName: "Nickolas"; lastName: "Juvera"; mobileNumber: "352-598-8301" } 367 | ListElement { firstName: "Nicolette"; lastName: "Brossart"; mobileNumber: "508-837-9230" } 368 | ListElement { firstName: "Nieves"; lastName: "Gotter"; mobileNumber: "503-527-5274" } 369 | ListElement { firstName: "Noah"; lastName: "Kalafatis"; mobileNumber: "414-263-5287" } 370 | ListElement { firstName: "Nobuko"; lastName: "Halsey"; mobileNumber: "508-855-9887" } 371 | ListElement { firstName: "Norah"; lastName: "Waymire"; mobileNumber: "415-306-7897" } 372 | ListElement { firstName: "Novella"; lastName: "Degroot"; mobileNumber: "808-477-4775" } 373 | ListElement { firstName: "Nu"; lastName: "Mcnease"; mobileNumber: "973-751-9003" } 374 | ListElement { firstName: "Olive"; lastName: "Matuszak"; mobileNumber: "760-938-6069" } 375 | ListElement { firstName: "Oretha"; lastName: "Menter"; mobileNumber: "617-418-5043" } 376 | ListElement { firstName: "Ozell"; lastName: "Shealy"; mobileNumber: "212-332-8435" } 377 | ListElement { firstName: "Pamella"; lastName: "Schmierer"; mobileNumber: "305-420-8970" } 378 | ListElement { firstName: "Pamella"; lastName: "Fortino"; mobileNumber: "303-404-2210" } 379 | ListElement { firstName: "Paris"; lastName: "Wide"; mobileNumber: "404-505-4445" } 380 | ListElement { firstName: "Paz"; lastName: "Sahagun"; mobileNumber: "601-927-8287" } 381 | ListElement { firstName: "Peggie"; lastName: "Sturiale"; mobileNumber: "619-608-1763" } 382 | ListElement { firstName: "Penney"; lastName: "Weight"; mobileNumber: "907-797-9628" } 383 | ListElement { firstName: "Pete"; lastName: "Dubaldi"; mobileNumber: "201-825-2514" } 384 | ListElement { firstName: "Portia"; lastName: "Stimmel"; mobileNumber: "908-722-7128" } 385 | ListElement { firstName: "Quentin"; lastName: "Birkner"; mobileNumber: "952-702-7993" } 386 | ListElement { firstName: "Quentin"; lastName: "Swayze"; mobileNumber: "734-561-6170" } 387 | ListElement { firstName: "Raina"; lastName: "Brachle"; mobileNumber: "406-318-1515" } 388 | ListElement { firstName: "Rasheeda"; lastName: "Sayaphon"; mobileNumber: "408-805-4309" } 389 | ListElement { firstName: "Raul"; lastName: "Upthegrove"; mobileNumber: "619-509-5282" } 390 | ListElement { firstName: "Raylene"; lastName: "Kampa"; mobileNumber: "574-499-1454" } 391 | ListElement { firstName: "Raymon"; lastName: "Calvaresi"; mobileNumber: "317-825-4724" } 392 | ListElement { firstName: "Rebecka"; lastName: "Gesick"; mobileNumber: "512-213-8574" } 393 | ListElement { firstName: "Reena"; lastName: "Maisto"; mobileNumber: "410-351-1863" } 394 | ListElement { firstName: "Refugia"; lastName: "Jacobos"; mobileNumber: "510-974-8671" } 395 | ListElement { firstName: "Regenia"; lastName: "Kannady"; mobileNumber: "480-726-1280" } 396 | ListElement { firstName: "Reita"; lastName: "Leto"; mobileNumber: "317-234-1135" } 397 | ListElement { firstName: "Renea"; lastName: "Monterrubio"; mobileNumber: "770-679-4752" } 398 | ListElement { firstName: "Ressie"; lastName: "Auffrey"; mobileNumber: "305-604-8981" } 399 | ListElement { firstName: "Rhea"; lastName: "Aredondo"; mobileNumber: "718-560-9537" } 400 | ListElement { firstName: "Rickie"; lastName: "Plumer"; mobileNumber: "419-693-1334" } 401 | ListElement { firstName: "Rikki"; lastName: "Nayar"; mobileNumber: "305-968-9487" } 402 | ListElement { firstName: "Rima"; lastName: "Bevelacqua"; mobileNumber: "310-858-5079" } 403 | ListElement { firstName: "Rodolfo"; lastName: "Butzen"; mobileNumber: "507-210-3510" } 404 | ListElement { firstName: "Rolande"; lastName: "Spickerman"; mobileNumber: "808-315-3077" } 405 | ListElement { firstName: "Rolland"; lastName: "Francescon"; mobileNumber: "973-649-2922" } 406 | ListElement { firstName: "Ronny"; lastName: "Caiafa"; mobileNumber: "215-605-7570" } 407 | ListElement { firstName: "Roosevelt"; lastName: "Hoffis"; mobileNumber: "305-622-4739" } 408 | ListElement { firstName: "Rory"; lastName: "Papasergi"; mobileNumber: "570-867-7489" } 409 | ListElement { firstName: "Roselle"; lastName: "Estell"; mobileNumber: "419-571-5920" } 410 | ListElement { firstName: "Rosio"; lastName: "Cork"; mobileNumber: "336-243-5659" } 411 | ListElement { firstName: "Roslyn"; lastName: "Chavous"; mobileNumber: "601-234-9632" } 412 | ListElement { firstName: "Roxane"; lastName: "Campain"; mobileNumber: "907-231-4722" } 413 | ListElement { firstName: "Rozella"; lastName: "Ostrosky"; mobileNumber: "805-832-6163" } 414 | ListElement { firstName: "Ruthann"; lastName: "Keener"; mobileNumber: "830-258-2769" } 415 | ListElement { firstName: "Ryan"; lastName: "Harnos"; mobileNumber: "972-558-1665" } 416 | ListElement { firstName: "Sabra"; lastName: "Uyetake"; mobileNumber: "803-925-5213" } 417 | ListElement { firstName: "Sage"; lastName: "Wieser"; mobileNumber: "605-414-2147" } 418 | ListElement { firstName: "Salena"; lastName: "Karpel"; mobileNumber: "330-791-8557" } 419 | ListElement { firstName: "Salome"; lastName: "Lacovara"; mobileNumber: "804-550-5097" } 420 | ListElement { firstName: "Samira"; lastName: "Heintzman"; mobileNumber: "206-311-4137" } 421 | ListElement { firstName: "Sarah"; lastName: "Candlish"; mobileNumber: "770-732-1194" } 422 | ListElement { firstName: "Scarlet"; lastName: "Cartan"; mobileNumber: "229-735-3378" } 423 | ListElement { firstName: "Selma"; lastName: "Husser"; mobileNumber: "201-991-8369" } 424 | ListElement { firstName: "Serina"; lastName: "Zagen"; mobileNumber: "260-273-3725" } 425 | ListElement { firstName: "Shalon"; lastName: "Shadrick"; mobileNumber: "718-232-2337" } 426 | ListElement { firstName: "Sharee"; lastName: "Maile"; mobileNumber: "231-467-9978" } 427 | ListElement { firstName: "Sharen"; lastName: "Bourbon"; mobileNumber: "516-816-1541" } 428 | ListElement { firstName: "Sharika"; lastName: "Eanes"; mobileNumber: "407-312-1691" } 429 | ListElement { firstName: "Shawna"; lastName: "Palaspas"; mobileNumber: "805-275-3566" } 430 | ListElement { firstName: "Shawnda"; lastName: "Yori"; mobileNumber: "407-538-5106" } 431 | ListElement { firstName: "Shenika"; lastName: "Seewald"; mobileNumber: "818-423-4007" } 432 | ListElement { firstName: "Sheridan"; lastName: "Zane"; mobileNumber: "951-645-3605" } 433 | ListElement { firstName: "Sherita"; lastName: "Saras"; mobileNumber: "719-669-1664" } 434 | ListElement { firstName: "Sheron"; lastName: "Louissant"; mobileNumber: "718-976-8610" } 435 | ListElement { firstName: "Shonda"; lastName: "Greenbush"; mobileNumber: "973-482-2430" } 436 | ListElement { firstName: "Simona"; lastName: "Morasca"; mobileNumber: "419-503-2484" } 437 | ListElement { firstName: "Skye"; lastName: "Fillingim"; mobileNumber: "612-508-2655" } 438 | ListElement { firstName: "Solange"; lastName: "Shinko"; mobileNumber: "504-979-9175" } 439 | ListElement { firstName: "Staci"; lastName: "Schmaltz"; mobileNumber: "626-866-2339" } 440 | ListElement { firstName: "Stephaine"; lastName: "Barfield"; mobileNumber: "310-774-7643" } 441 | ListElement { firstName: "Stephaine"; lastName: "Vinning"; mobileNumber: "415-767-6596" } 442 | ListElement { firstName: "Stephane"; lastName: "Myricks"; mobileNumber: "859-717-7638" } 443 | ListElement { firstName: "Stephen"; lastName: "Emigh"; mobileNumber: "330-537-5358" } 444 | ListElement { firstName: "Stevie"; lastName: "Westerbeck"; mobileNumber: "949-867-4077" } 445 | ListElement { firstName: "Sue"; lastName: "Kownacki"; mobileNumber: "972-666-3413" } 446 | ListElement { firstName: "Sylvia"; lastName: "Cousey"; mobileNumber: "410-209-9545" } 447 | ListElement { firstName: "Sylvie"; lastName: "Ryser"; mobileNumber: "918-644-9555" } 448 | ListElement { firstName: "Talia"; lastName: "Riopelle"; mobileNumber: "973-245-2133" } 449 | ListElement { firstName: "Tamar"; lastName: "Hoogland"; mobileNumber: "740-343-8575" } 450 | ListElement { firstName: "Tammara"; lastName: "Wardrip"; mobileNumber: "650-803-1936" } 451 | ListElement { firstName: "Tarra"; lastName: "Nachor"; mobileNumber: "415-411-1775" } 452 | ListElement { firstName: "Taryn"; lastName: "Moyd"; mobileNumber: "703-322-4041" } 453 | ListElement { firstName: "Tasia"; lastName: "Andreason"; mobileNumber: "201-920-9002" } 454 | ListElement { firstName: "Tawna"; lastName: "Buvens"; mobileNumber: "212-674-9610" } 455 | ListElement { firstName: "Teddy"; lastName: "Pedrozo"; mobileNumber: "203-892-3863" } 456 | ListElement { firstName: "Tegan"; lastName: "Arceo"; mobileNumber: "732-730-2692" } 457 | ListElement { firstName: "Teri"; lastName: "Ennaco"; mobileNumber: "570-889-5187" } 458 | ListElement { firstName: "Terrilyn"; lastName: "Rodeigues"; mobileNumber: "504-463-4384" } 459 | ListElement { firstName: "Thaddeus"; lastName: "Ankeny"; mobileNumber: "916-920-3571" } 460 | ListElement { firstName: "Theodora"; lastName: "Restrepo"; mobileNumber: "305-936-8226" } 461 | ListElement { firstName: "Theola"; lastName: "Frey"; mobileNumber: "516-948-5768" } 462 | ListElement { firstName: "Thurman"; lastName: "Manno"; mobileNumber: "609-524-3586" } 463 | ListElement { firstName: "Tiera"; lastName: "Frankel"; mobileNumber: "626-636-4117" } 464 | ListElement { firstName: "Tiffiny"; lastName: "Steffensmeier"; mobileNumber: "305-385-9695" } 465 | ListElement { firstName: "Timothy"; lastName: "Mulqueen"; mobileNumber: "718-332-6527" } 466 | ListElement { firstName: "Tonette"; lastName: "Wenner"; mobileNumber: "516-968-6051" } 467 | ListElement { firstName: "Tracey"; lastName: "Modzelewski"; mobileNumber: "936-264-9294" } 468 | ListElement { firstName: "Tresa"; lastName: "Sweely"; mobileNumber: "314-359-9566" } 469 | ListElement { firstName: "Trinidad"; lastName: "Mcrae"; mobileNumber: "415-331-9634" } 470 | ListElement { firstName: "Truman"; lastName: "Feichtner"; mobileNumber: "973-852-2736" } 471 | ListElement { firstName: "Twana"; lastName: "Felger"; mobileNumber: "503-939-3153" } 472 | ListElement { firstName: "Ty"; lastName: "Smith"; mobileNumber: "201-672-1553" } 473 | ListElement { firstName: "Tyra"; lastName: "Shields"; mobileNumber: "215-255-1641" } 474 | ListElement { firstName: "Valentin"; lastName: "Klimek"; mobileNumber: "312-303-5453" } 475 | ListElement { firstName: "Valentine"; lastName: "Gillian"; mobileNumber: "210-812-9597" } 476 | ListElement { firstName: "Vallie"; lastName: "Mondella"; mobileNumber: "208-862-5339" } 477 | ListElement { firstName: "Van"; lastName: "Shire"; mobileNumber: "908-409-2890" } 478 | ListElement { firstName: "Venita"; lastName: "Maillard"; mobileNumber: "714-523-6653" } 479 | ListElement { firstName: "Veronika"; lastName: "Inouye"; mobileNumber: "408-540-1785" } 480 | ListElement { firstName: "Vi"; lastName: "Rentfro"; mobileNumber: "732-605-4781" } 481 | ListElement { firstName: "Vilma"; lastName: "Berlanga"; mobileNumber: "616-737-3085" } 482 | ListElement { firstName: "Vincent"; lastName: "Meinerding"; mobileNumber: "215-372-1718" } 483 | ListElement { firstName: "Vincenza"; lastName: "Zepp"; mobileNumber: "619-603-5125" } 484 | ListElement { firstName: "Viola"; lastName: "Bitsuie"; mobileNumber: "818-864-4875" } 485 | ListElement { firstName: "Virgie"; lastName: "Kiel"; mobileNumber: "303-776-7548" } 486 | ListElement { firstName: "Virgina"; lastName: "Tegarden"; mobileNumber: "414-214-8697" } 487 | ListElement { firstName: "Viva"; lastName: "Toelkes"; mobileNumber: "773-446-5569" } 488 | ListElement { firstName: "Weldon"; lastName: "Acuff"; mobileNumber: "847-353-2156" } 489 | ListElement { firstName: "Whitley"; lastName: "Tomasulo"; mobileNumber: "817-526-4408" } 490 | ListElement { firstName: "Wilda"; lastName: "Giguere"; mobileNumber: "907-870-5536" } 491 | ListElement { firstName: "Willard"; lastName: "Kolmetz"; mobileNumber: "972-303-9197" } 492 | ListElement { firstName: "Willodean"; lastName: "Konopacki"; mobileNumber: "337-253-8384" } 493 | ListElement { firstName: "Willow"; lastName: "Kusko"; mobileNumber: "212-582-4976" } 494 | ListElement { firstName: "Winfred"; lastName: "Brucato"; mobileNumber: "208-252-4552" } 495 | ListElement { firstName: "Wynell"; lastName: "Dorshorst"; mobileNumber: "650-473-1262" } 496 | ListElement { firstName: "Xochitl"; lastName: "Discipio"; mobileNumber: "512-233-1831" } 497 | ListElement { firstName: "Xuan"; lastName: "Rochin"; mobileNumber: "650-933-5072" } 498 | ListElement { firstName: "Yoko"; lastName: "Fishburne"; mobileNumber: "203-506-4706" } 499 | ListElement { firstName: "Yolando"; lastName: "Luczki"; mobileNumber: "315-304-4759" } 500 | ListElement { firstName: "Youlanda"; lastName: "Schemmer"; mobileNumber: "541-548-8197" } 501 | ListElement { firstName: "Yuki"; lastName: "Whobrey"; mobileNumber: "313-288-7937" } 502 | ListElement { firstName: "Yvonne"; lastName: "Tjepkema"; mobileNumber: "973-714-1721" } 503 | ListElement { firstName: "Zona"; lastName: "Colla"; mobileNumber: "203-461-1949" } 504 | } 505 | --------------------------------------------------------------------------------