diff options
author | brianjjones <brian.j.jones@intel.com> | 2014-03-06 15:29:50 -0800 |
---|---|---|
committer | brianjjones <brian.j.jones@intel.com> | 2014-03-06 15:55:00 -0800 |
commit | 53fd8518caff406b6b011a4bafb9f106edd95d36 (patch) | |
tree | bc20706e97738a16e6a479004c4cc83d4f9915a4 | |
download | html5_UI_Phone-53fd8518caff406b6b011a4bafb9f106edd95d36.tar.gz html5_UI_Phone-53fd8518caff406b6b011a4bafb9f106edd95d36.tar.bz2 html5_UI_Phone-53fd8518caff406b6b011a4bafb9f106edd95d36.zip |
Initial commit of the Phone appsubmit/tizen/20140307.000903submit/tizen/20140307.000454accepted/tizen/ivi/20140307.031731
Change-Id: Ief600f67c9b101aa5634858a8606ed48d513d462
-rw-r--r-- | Makefile | 20 | ||||
-rw-r--r-- | config.xml | 18 | ||||
-rw-r--r-- | css/contacts_library.css | 216 | ||||
-rw-r--r-- | css/images/null.png | bin | 0 -> 2791 bytes | |||
-rw-r--r-- | css/phone_style.css | 277 | ||||
-rw-r--r-- | data/contacts.json | 202 | ||||
-rw-r--r-- | data/history.json | 313 | ||||
-rw-r--r-- | data/photos/nophoto.png | bin | 0 -> 3117 bytes | |||
-rw-r--r-- | icon.png | bin | 0 -> 2511 bytes | |||
-rw-r--r-- | index.html | 182 | ||||
-rw-r--r-- | js/callhistorycarousel.js | 268 | ||||
-rw-r--r-- | js/contacts_library.js | 210 | ||||
-rw-r--r-- | js/main.js | 809 | ||||
-rw-r--r-- | js/phone.js | 361 | ||||
-rw-r--r-- | packaging/html5-ui-phone.changes | 4 | ||||
-rw-r--r-- | packaging/html5-ui-phone.spec | 36 | ||||
-rw-r--r-- | templates/contactCarouselDelegate.html | 24 | ||||
-rw-r--r-- | templates/libraryContactDetailDelegate.html | 47 | ||||
-rw-r--r-- | templates/template-contacts.html | 10 |
19 files changed, 2997 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cb90ad0 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +PROJECT = html5UIPhone + +VERSION := 0.0.1 +PACKAGE = $(PROJECT)-$(VERSION) + +INSTALL_FILES = $(PROJECT).wgt +INSTALL_DIR = ${DESTDIR}/opt/usr/apps/.preinstallWidgets + +wgtPkg: + cp -r ${DESTDIR}/opt/usr/apps/_common/js/services js/ + cp -r ${DESTDIR}/opt/usr/apps/_common/css/* css/ + zip -r $(PROJECT).wgt config.xml css data icon.png index.html js templates + +install: + @echo "Installing Phone, stand by..." + mkdir -p $(INSTALL_DIR)/ + cp $(PROJECT).wgt $(INSTALL_DIR)/ + +dist: + tar czf ../$(PACKAGE).tar.bz2 . diff --git a/config.xml b/config.xml new file mode 100644 index 0000000..4de9d2a --- /dev/null +++ b/config.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<widget xmlns="http://www.w3.org/ns/widgets" xmlns:tizen="http://tizen.org/ns/widgets" id="http://com.intel.tizen/phone" version="0.5.0" viewmodes="fullscreen"> + <tizen:application id="html5POC09.Phone" package="html5POC09" required_version="2.1"/> + <icon src="icon.png"/> + <content src="index.html"/> + <name>Phone</name> + <tizen:privilege name="http://tizen.org/privilege/tizen"/> + <tizen:privilege name="http://tizen.org/privilege/application.launch"/> + <tizen:privilege name="http://tizen.org/privilege/filesystem.read"/> + <tizen:privilege name="http://tizen.org/privilege/filesystem.write"/> + <tizen:privilege name="http://tizen.org/privilege/contact.read"/> + <tizen:privilege name="http://tizen.org/privilege/contact.write"/> + <tizen:privilege name="http://tizen.org/privilege/content.read" /> + <tizen:privilege name="http://tizen.org/privilege/speech" /> + <tizen:privilege name="http://tizen.org/privilege/bluetooth.admin" /> + <tizen:privilege name="http://tizen.org/privilege/bluetooth.spp" /> + <tizen:privilege name="http://tizen.org/privilege/bluetooth.gap" /> +</widget> diff --git a/css/contacts_library.css b/css/contacts_library.css new file mode 100644 index 0000000..5cee618 --- /dev/null +++ b/css/contacts_library.css @@ -0,0 +1,216 @@ +.contactsLibraryContentList .contactElement { + padding: 10px; + height: 70px; + box-shadow: none; + border-bottom-style: solid; + border-bottom-width: 2px; + overflow: hidden; + white-space: nowrap; + cursor: pointer; +} + +.phoneIcon { + background-repeat: no-repeat; + background-position: center center; + width: 44px; + height: 44px; + display: inline-block; +} + +.emailIcon { + background-repeat: no-repeat; + background-position: center center; + width: 44px; + height: 44px; + display: inline-block; +} + +.addressIcon { + background-repeat: no-repeat; + background-position: center center; + width: 44px; + height: 44px; + display: inline-block; +} + +.contactsLibraryContentList .phoneIcon { + padding-top: 23px; +} + +.contactsLibraryContentList .contactPhotoBox { + width: 70px; + height: 70px; + display: inline-block; + margin-left: 22px; + background-position: center center; + background-repeat: no-repeat; + background-size: 100% 100%; +} + +.contactsLibraryContentList .contactPhoto { + width: 100%; + height: 100%; +} + +.contactsLibraryContentList .contactName { + display: inline-block; + vertical-align: top; + padding-left: 18px; + line-height: 70px; + background-color: transparent !important; + text-transform: uppercase; +} + +.contactsLibraryContentGrid { + line-height: 0; +} + +.contactsLibraryContentGrid .contactElement { + width: 180px; + height: 180px; + display: inline-block; + margin-right: 7px; + margin-bottom: 10px; + cursor: pointer; +} + +.contactsLibraryContentGrid .phoneIcon { + display: none; +} + +.contactsLibraryContentGrid .contactPhotoBox { + width: 100%; + height: 100%; + background-size: 100% 100%; +} + +.contactsLibraryContentGrid .contactPhoto { + width: 100%; + height: 100%; +} + +.contactsLibraryContentGrid .contactName { + position: relative; + padding-left: 10px; + padding-right: 10px; + padding-top: 10px; + top: -80px; + left: 0; + height: 70px; + overflow: hidden; + text-transform: uppercase; +} + +.contactDetail { + margin-top: 48px; +} + +.contactDetailBox1 { + margin-left: 45px; + width: 120px; + display: inline-block; + vertical-align: top; +} + +.contactDetailPhotoBox { + width: 120px; + height: 120px; + display: block; + background-position: center center; + background-repeat: no-repeat; + background-size: 100% 100%; +} + +.contactDetailPhoto { + width: 100%; + height: 100%; +} + +.contactDetailFavorite { + width: 48px; + height: 48px; + margin: 30px auto 0 auto; + background-position: center center; + background-repeat: no-repeat; + background-size: 100% 100%; +} + +.contactDetailBox2 { + display: inline-block; + vertical-align: top; + margin-left: 53px; + width: 385px; +} + +.contactDetailBox3 { + margin-bottom: 30px; +} + + +.contactDetailBox4 { + margin-left: 15px; + vertical-align: top; + display: inline-block; + width: 320px; +} + +.missedCallIcon { + background-repeat: no-repeat; + background-position: center center; + width: 42px; + height: 36px; + display: inline-block; +} + +.missedNewCallIcon { + background-repeat: no-repeat; + background-position: center center; + width: 42px; + height: 36px; + display: inline-block; +} + +.receivedCallIcon { + background-repeat: no-repeat; + background-position: center center; + width: 42px; + height: 36px; + display: inline-block; +} + +.dialedCallIcon { + background-repeat: no-repeat; + background-position: center center; + width: 42px; + height: 36px; + display: inline-block; +} + +.callHistoryBox { + margin-top: 20px; + border-top-style: solid; + border-top-width: 2px; +} + +.callHistoryElement { + padding: 18px 0 18px 0; + box-shadow: none; + border-bottom-style: solid; + border-bottom-width: 2px; + overflow: hidden; + white-space: nowrap; +} + +.callHistoryIcon { + padding-top: 8px; + margin-left: 20px; +} +.callHistoryIconGen { + top: -10px; + background-size: 70%; +} + +.callHistoryDetails { + display: inline-block; + vertical-align: top; +} diff --git a/css/images/null.png b/css/images/null.png Binary files differnew file mode 100644 index 0000000..841863c --- /dev/null +++ b/css/images/null.png diff --git a/css/phone_style.css b/css/phone_style.css new file mode 100644 index 0000000..fe5cf59 --- /dev/null +++ b/css/phone_style.css @@ -0,0 +1,277 @@ +.phoneBody { + width: 720px; + margin: 0; + min-height: 1280px; + height: auto !important; + height: 100%; + background-position: center top; + background-repeat: no-repeat; + background-size: 100% 100%; +} + +.inputPhoneNumberBox { + padding-top: 195px; + padding-left: 190px; +} + +.inputPhoneNumber { + width: 215px; + height: 75px; + line-height: 75px; + padding-right: 90px; + padding-left: 30px; + border: none; + background-color: rgb(0, 0, 0); + background-color: rgba(0, 0, 0, 0.2); +} + +.inputPhoneNumber:focus { + outline: none; +} + +.inputPhoneNumberBox .deleteButton { + display: inline-block; + vertical-align: top; + margin: 18px 0 0 -73px; + padding: 0; + width: 55px; + height: 36px; + background-position: center center; + background-repeat: no-repeat; + cursor: pointer; +} +.noPairedDevice{ + top:130px; + left: 200px; + margin-top: 20px; + width:100%; + height:100%; +} +.loadingSpinnerHistory { + top:130px; + width:100%; + height:100%; +} +.numbersBox { + margin-top: 7px; + display: inline-block; + width: 430px; + font-size: 0; + margin-bottom: 44px; +} + +.numberButton { + float: left; + padding: 0; + padding-top: 20px; + width: 105px; + height: 80px; + margin-right: 10px; + margin-bottom: 10px; + background-color: rgb(0, 0, 0); + background-color: rgba(0, 0, 0, 0.2); + text-transform: uppercase; +} + +.numberButtonLetters { + margin-top: -3px; +} + +.specialChar { + padding-top: 30px; + height: 70px; +} + +.contactsCarousel { + margin: 0; + padding: 0; + list-style: none; + display: block; +} + +.contactsCarousel li { + display: block; + float: left; +} + +.carouselBox { + width: 240px; + height: 315px; + margin: 0 15px 20px 0; + white-space: normal; + background-color: rgb(0, 0, 0); + background-color: rgba(0, 0, 0, 0.2); + box-shadow: 0 2px 30px rgba(0, 0, 0, .7); + border-width: 1px; + border-style: solid; + text-transform: uppercase; +} + +.carouselEdgeBox { + width: 240px; + height: 315px; + margin: 0 15px 20px 0; + white-space: normal; + visibility: hidden; +} + +.carouselPhotoArea { + margin: 5px; + text-align: center; + border-style: solid; + border-width: 0; + border-bottom-width: 2px; +} + +.carouselPhotoBox { + margin: 32px auto 28px auto; + width: 120px; + height: 120px; + background-position: center center; + background-repeat: no-repeat; + background-size: 100% 100%; +} + +.carouselPhoto { + width: 100%; + height: 100%; +} + +.carouselInfoBox { + width: 205px; + text-align: left; + white-space: nowrap; + overflow: hidden; + margin: 0 auto; +} + +.carouselCallContact { + margin-top: 15px; + margin-bottom: -5px; +} + +.carouselName { + margin-top: 5px; + margin-bottom: 10px; +} + +.carouselNumber { + background-repeat: no-repeat; + background-position: left center; + width: 160px; + padding-left: 45px; + line-height: 38px; + cursor: pointer; +} + +.callBox { + position: absolute; + margin: 0; + margin-left: 61px; + top: 770px; + width: 720px; + height: 160px; + z-index: 100; + -webkit-transition-timing-function: linear; + -webkit-transition: left 1s; + -moz-transition-timing-function: linear; + -moz-transition: left 1s; + -ms-transition-timing-function: linear; + -ms-transition: left 1s; + -o-transition-timing-function: linear; + -o-transition: left 1s +} + +.callBoxShow { + left: 0; +} + +.callBoxHidden { + left: 720px; +} + +.callInfoBox { + width: 300px; + vertical-align: top; + white-space: nowrap; + overflow: hidden; + display: inline-block; +} + +.callPhotoBox { + width: 120px; + height: 120px; + display: inline-block; + background-position: center center; + background-repeat: no-repeat; + background-size: 100% 100%; +} + +.callPhoto { + width: 100%; + height: 100%; +} + +.callDuration { + margin-left: 60px; + display: inline-block; + vertical-align: top; + padding-top: 46px; +} + +.inCallWith { + margin-top: 11px; +} + +.callName { + margin-top: 5px; + text-transform: uppercase; +} + +.callControlsBox { + width: 328px; + display: inline-block; + vertical-align: top; + margin-top: 5px; + margin-left: 13px; + font-size: 0; +} + +.controlButton { + display: inline-block; + padding: 70px 0 13px 0; + width: 140px; + margin-right: 6px; + margin-bottom: 8px; + background-repeat: no-repeat; + background-position: center 15px; + box-shadow: 10px 10px 100px rgba(0, 0, 0, .7); +} + +#callVolumeControl { + margin-left: 0; + margin-top: 25px; + width: 600px; + height: 55px; + top: 20px; +} + +.contactsCarouselBox { + width: 765px; + position: absolute; + top: 770px; + left: -16px; + padding: 0; + margin: 0; + display: block; + transition: left 1s; + -webkit-transition: left 1s; +} + +.contactsCarouselBoxShow { + left: -16px; +} + +.contactsCarouselBoxHide { + left: -765px; +}
\ No newline at end of file diff --git a/data/contacts.json b/data/contacts.json new file mode 100644 index 0000000..5859fdf --- /dev/null +++ b/data/contacts.json @@ -0,0 +1,202 @@ +{ + "contacts": [{ + "id": "F926BBEC-9FE7-4360-831B-814A03DAC655", + "lastUpdated": "2012-10-05T12:05:57.386Z", + "name": { + "prefix": "", + "firstName": "Erick", + "middleName": "", + "lastName": "Briar", + "nicknames": ["ebi"], + "phoneticName": "", + "displayName": null + }, + "account": null, + "addresses": [], + "photoURI": "", + "phoneNumbers": [{ + "number": "+123 422 789", + "types": [] + }], + "emails": [{ + "email": "erick@hgost.com", + "types": [] + }], + "birthday": null, + "anniversaries": null, + "organization": null, + "notes": [], + "urls": [], + "isFavorite": true, + "ringtoneURI": null, + "categories": [] + }, + { + "id": "F926BBEC-9FE7-4360-831B-814A03DAC632", + "lastUpdated": "2012-10-05T12:05:57.386Z", + "name": { + "prefix": "", + "firstName": "Gilbert", + "middleName": "", + "lastName": "Ted", + "nicknames": ["gil"], + "phoneticName": "", + "displayName": null + }, + "account": null, + "addresses": [], + "photoURI": "", + "phoneNumbers": [{ + "number": "6548 523 574", + "types": [] + }], + "emails": [{ + "email": "ted@qqaa.com", + "types": [] + }], + "birthday": null, + "anniversaries": null, + "organization": null, + "notes": [], + "urls": [], + "isFavorite": false, + "ringtoneURI": null, + "categories": [] + }, + { + "id": "F926BBEC-9FE7-4360-831B-814A03DAC631", + "lastUpdated": "2012-10-05T12:05:57.386Z", + "name": { + "prefix": "", + "firstName": "Ivor", + "middleName": "", + "lastName": "Kodey", + "nicknames": ["ivo"], + "phoneticName": "", + "displayName": null + }, + "account": null, + "addresses": [], + "photoURI": "", + "phoneNumbers": [{ + "number": "1234 555 666", + "types": [] + }], + "emails": [{ + "email": "ivor@srbhufc.net", + "types": [] + }], + "birthday": null, + "anniversaries": null, + "organization": null, + "notes": [], + "urls": [], + "isFavorite": false, + "ringtoneURI": null, + "categories": [] + }, + { + "id": "A27F67A5-8879-49D4-9312-31510F018405", + "lastUpdated": "2012-10-05T09:03:27.781Z", + "name": { + "prefix": "", + "firstName": "Jonty", + "middleName": "", + "lastName": "Dana", + "nicknames": ["jodan"], + "phoneticName": "", + "displayName": null + }, + "account": null, + "addresses": [], + "photoURI": "", + "phoneNumbers": [{ + "number": "9659 421 777", + "types": [] + }], + "emails": [{ + "email": "jodan@lamxyx.org", + "types": [] + }], + "birthday": null, + "anniversaries": null, + "organization": null, + "notes": [], + "urls": [], + "isFavorite": false, + "ringtoneURI": null, + "categories": [] + }, + { + "id": "F57AA60B-011D-4210-9450-08E6BEDEAE8B", + "lastUpdated": "2012-10-05T13:23:26.862Z", + "name": { + "prefix": "", + "firstName": "Brad", + "middleName": "", + "lastName": "Rob", + "nicknames": ["brob"], + "phoneticName": "", + "displayName": null + }, + "account": null, + "addresses": [{ + "country": "Kasabanan", + "region": "", + "city": "", + "streetAddress": "Crane 12", + "additionalInformation": "", + "postalCode": "478152", + "types": [] + }], + "photoURI": "", + "phoneNumbers": [{ + "number": "3456 124 231", + "types": [] + }], + "emails": [{ + "email": "rob@klatog.tr", + "types": [] + }], + "birthday": null, + "anniversaries": null, + "organization": null, + "notes": [], + "urls": [], + "isFavorite": true, + "ringtoneURI": null, + "categories": [] + }, + { + "id": "6C8394CE-9CA5-4FB3-A97C-4D93BEFC5559", + "lastUpdated": "2012-10-05T12:05:46.783Z", + "name": { + "prefix": "", + "firstName": "Derick", + "middleName": "", + "lastName": "Jojo", + "nicknames": ["dejo"], + "phoneticName": "", + "displayName": null + }, + "account": null, + "addresses": [], + "photoURI": "", + "phoneNumbers": [{ + "number": "0123 456 865", + "types": [] + }], + "emails": [{ + "email": "dejo@waxxos.py", + "types": [] + }], + "birthday": null, + "anniversaries": null, + "organization": null, + "notes": [], + "urls": [], + "isFavorite": false, + "ringtoneURI": null, + "categories": [] + }] +}
\ No newline at end of file diff --git a/data/history.json b/data/history.json new file mode 100644 index 0000000..690d613 --- /dev/null +++ b/data/history.json @@ -0,0 +1,313 @@ +{ + "history": [{ + "serviceId": 1350052304188, + "callType": "tizen.tel", + "tags": ["call.voice", + "call.video"], + "callParticipants": [{ + "id": "F57AA60B-011D-4210-9450-08E6BEDEAE8B", + "displayName": "Brad Rob", + "contactRef": null + }], + "forwardedFrom": { + + }, + "startTime": "2012-10-12T14:31:44.188Z", + "duration": 0, + "endReason": "local", + "direction": "missed-new", + "recording": [], + "cost": 0, + "currency": null + }, + { + "serviceId": 1350052299533, + "callType": "tizen.tel", + "tags": ["call.voice", + "call.video"], + "callParticipants": [{ + "displayName": "Brad Rob", + "contactRef": null + }], + "forwardedFrom": { + + }, + "startTime": "2012-10-12T14:31:39.533Z", + "duration": 0, + "endReason": "rejected", + "direction": "dialed", + "recording": [], + "cost": 0, + "currency": null + }, + { + "serviceId": 1349787460224, + "callType": "tizen.tel", + "tags": ["call.voice", + "call.video"], + "callParticipants": [{ + "id": "F57AA60B-011D-4210-9450-08E6BEDEAE8B", + "displayName": "Brad Rob", + "contactRef": null + }], + "forwardedFrom": { + + }, + "startTime": "2012-10-09T12:57:40.225Z", + "duration": 0, + "endReason": "remote", + "direction": "missed-new", + "recording": [], + "cost": 0, + "currency": null + }, + { + "serviceId": 1349787449337, + "callType": "tizen.tel", + "tags": ["call.voice", + "call.video"], + "callParticipants": [{ + "id": "F57AA60B-011D-4210-9450-08E6BEDEAE8B", + "displayName": "Brad Rob", + "contactRef": null + }], + "forwardedFrom": { + + }, + "startTime": "2012-10-09T12:57:29.337Z", + "duration": 0, + "endReason": "local", + "direction": "dialed", + "recording": [], + "cost": 0, + "currency": null + }, + { + "serviceId": 1349787440709, + "callType": "tizen.tel", + "tags": ["call.voice", + "call.video"], + "callParticipants": [{ + "id": "A27F67A5-8879-49D4-9312-31510F018405", + "displayName": "Jonty Dana", + "contactRef": null + }], + "forwardedFrom": { + + }, + "startTime": "2012-10-09T12:57:20.709Z", + "duration": 0, + "endReason": "local", + "direction": "dialed", + "recording": [], + "cost": 0, + "currency": null + }, + { + "serviceId": 1349787435870, + "callType": "tizen.tel", + "tags": ["call.voice", + "call.video"], + "callParticipants": [{ + "id": "F926BBEC-9FE7-4360-831B-814A03DAC631", + "displayName": "Ivor Kodey", + "contactRef": null + }], + "forwardedFrom": { + + }, + "startTime": "2012-10-09T12:57:15.870Z", + "duration": 0, + "endReason": "local", + "direction": "dialed", + "recording": [], + "cost": 0, + "currency": null + }, + { + "serviceId": 1349778842662, + "callType": "tizen.tel", + "tags": ["call.voice", + "call.video"], + "callParticipants": [{ + "displayName": "Ivor Kodey", + "contactRef": null + }], + "forwardedFrom": { + + }, + "startTime": "2012-10-09T10:34:02.662Z", + "duration": 3849, + "endReason": "local", + "direction": "dialed", + "recording": [], + "cost": 0, + "currency": null + }, + { + "serviceId": 1349771235969, + "callType": "tizen.tel", + "tags": ["call.voice", + "call.video"], + "callParticipants": [{ + "displayName": "Albert Test", + "contactRef": null + }], + "forwardedFrom": { + + }, + "startTime": "2012-10-09T08:27:15.969Z", + "duration": 0, + "endReason": "local", + "direction": "dialed", + "recording": [], + "cost": 0, + "currency": null + }, + { + "serviceId": 1349449039585, + "callType": "tizen.tel", + "tags": ["call.voice", + "call.video"], + "callParticipants": [{ + "id": "F57AA60B-011D-4210-9450-08E6BEDEAE8B", + "displayName": "Brad Rob", + "contactRef": null + }], + "forwardedFrom": { + + }, + "startTime": "2012-10-05T14:57:19.585Z", + "duration": 3007, + "endReason": "remote", + "direction": "received", + "recording": [], + "cost": 0, + "currency": null + }, + { + "serviceId": 1349449026162, + "callType": "tizen.tel", + "tags": ["call.voice", + "call.video"], + "callParticipants": [{ + "id": "F57AA60B-011D-4210-9450-08E6BEDEAE8B", + "displayName": "Brad Rob", + "contactRef": null + }], + "forwardedFrom": { + + }, + "startTime": "2012-10-05T14:57:06.162Z", + "duration": 3879, + "endReason": "local", + "direction": "dialed", + "recording": [], + "cost": 0, + "currency": null + }, + { + "serviceId": 1349449011962, + "callType": "tizen.tel", + "tags": ["call.voice", + "call.video"], + "callParticipants": [{ + "id": "F926BBEC-9FE7-4360-831B-814A03DAC631", + "displayName": "Ivor Kodey", + "contactRef": null + }], + "forwardedFrom": { + + }, + "startTime": "2012-10-05T14:56:51.962Z", + "duration": 2992, + "endReason": "local", + "direction": "dialed", + "recording": [], + "cost": 0, + "currency": null + }, + { + "serviceId": 1349448097026, + "callType": "tizen.tel", + "tags": ["call.voice", + "call.video"], + "callParticipants": [{ + "id": "F926BBEC-9FE7-4360-831B-814A03DAC631", + "displayName": "Ivor Kodey", + "contactRef": null + }], + "forwardedFrom": { + + }, + "startTime": "2012-10-05T14:41:37.026Z", + "duration": 2792, + "endReason": "remote", + "direction": "received", + "recording": [], + "cost": 0, + "currency": null + }, + { + "serviceId": 1350052304188, + "callType": "tizen.tel", + "tags": ["call.voice", + "call.video"], + "callParticipants": [{ + "id": "F57AA60B-011D-4210-9450-08E6BEDEAE8B", + "displayName": "Brad Rob", + "contactRef": null + }], + "forwardedFrom": { + + }, + "startTime": "2012-10-12T14:31:44.188Z", + "duration": 0, + "endReason": "local", + "direction": "missed-new", + "recording": [], + "cost": 0, + "currency": null + }, + { + "serviceId": 1350052299533, + "callType": "tizen.tel", + "tags": ["call.voice", + "call.video"], + "callParticipants": [{ + "displayName": "Brad Rob", + "contactRef": null + }], + "forwardedFrom": { + + }, + "startTime": "2012-10-12T14:31:39.533Z", + "duration": 0, + "endReason": "rejected", + "direction": "dialed", + "recording": [], + "cost": 0, + "currency": null + }, + { + "serviceId": 1349787460224, + "callType": "tizen.tel", + "tags": ["call.voice", + "call.video"], + "callParticipants": [{ + "id": "F57AA60B-011D-4210-9450-08E6BEDEAE8B", + "displayName": "Brad Rob", + "contactRef": null + }], + "forwardedFrom": { + + }, + "startTime": "2012-10-09T12:57:40.225Z", + "duration": 0, + "endReason": "remote", + "direction": "missed-new", + "recording": [], + "cost": 0, + "currency": null + }] +}
\ No newline at end of file diff --git a/data/photos/nophoto.png b/data/photos/nophoto.png Binary files differnew file mode 100644 index 0000000..3f812de --- /dev/null +++ b/data/photos/nophoto.png diff --git a/icon.png b/icon.png Binary files differnew file mode 100644 index 0000000..5d8de5d --- /dev/null +++ b/icon.png diff --git a/index.html b/index.html new file mode 100644 index 0000000..6e8b606 --- /dev/null +++ b/index.html @@ -0,0 +1,182 @@ +<!DOCTYPE html> +<html> + +<head> +<meta charset="utf-8" /> +<meta name="viewport" content="width=720, height=1280, user-scalable=no" /> +<meta name="description" content="Phone application" /> +<title>Phone application</title> + +<!-- jquery and plugins --> +<script type="text/javascript" src="./css/car/components/jQuery/jquery-1.8.2.js"></script> +<script type="text/javascript" src="./css/car/components/knockout/knockout.js"></script> +<script type="text/javascript" src="./css/car/components/jsViews/jsrender.js"></script> +<script type="text/javascript" src="./css/car/components/jsViews/template.js"></script> +<script type="text/javascript" src="./css/car/components/carousel/jquery.carouFredSel-6.2.1-packed.js"></script> +<script type="text/javascript" src="./css/car/components/carousel/jquery.touchSwipe.min.js"></script> +<script type="text/javascript" src="./css/car/components/dateTime/moment.min.js"></script> + +<!-- user and car theme, theme engine, ... --> +<link rel="stylesheet" href="./css/car/car.css" /> +<link rel="stylesheet" href="./css/car/components/incomingCall/incomingCall.css" /> + + +<link rel="stylesheet" href="./css/car/car.css" /> +<script type="text/javascript" src='./css/car/car.js'></script> +<script type='text/javascript' src='./js/services/bootstrap.js'></script> + +<script type='text/javascript' src='./css/car/components/dateTime/dateTime.js'></script> +<link rel="stylesheet" href="./css/car/components/dateTime/dateTime.css" /> +<script type='text/javascript' src='./css/car/components/boxCaption/boxCaption.js'></script> +<link rel="stylesheet" href="./css/car/components/boxCaption/boxCaption.css" /> +<script type="text/javascript" src='./css/car/components/alphabetBookmark/alphabetBookmark.js'></script> +<link rel="stylesheet" href="./css/car/components/alphabetBookmark/alphabetBookmark.css" /> +<script type="text/javascript" src='./css/car/components/library/library.js'></script> +<link rel="stylesheet" href="./css/car/components/library/library.css" /> +<script type='text/javascript' src='./css/car/components/topBarIcons/topBarIcons.js'></script> +<link rel="stylesheet" href="./css/car/components/topBarIcons/topBarIcons.css" /> +<script type="text/javascript" src="./css/car/components/bottomPanel/bottomPanel.js"></script> +<script type='text/javascript' src='./css/car/components/progressBar/progressBar.js'></script> +<link rel="stylesheet" href="./css/car/components/progressBar/progressBar.css" /> +<link rel="stylesheet" href="./css/car/components/progressBar/volumeSlider.css" /> + +<script type="text/javascript" src="./js/main.js"></script> +<script type="text/javascript" src="./js/callhistorycarousel.js"></script> +<script type="text/javascript" src="./js/phone.js"></script> +<script type="text/javascript" src="./js/contacts_library.js"></script> +<script type="text/javascript" src="./css/car/components/jQuery/jquery.nouisliderix.js"></script> + +<link rel="stylesheet" type="text/css" href="./css/phone_style.css" /> +<link rel="stylesheet" type="text/css" href="./css/contacts_library.css" /> + +</head> + +<body class="phoneBody"> + <div id="contactsLibraryButton" + class="button libraryButton fontSizeSmaller fontWeightBold fontColorNormal">A-Z</div> + <div id="clockElement"></div> + + <div class="inputPhoneNumberBox"> + <input id="inputPhoneNumber" + class="inputPhoneNumber fontSizeLarge fontSizeBold fontColorTheme boxShadow4 boxShadow4Active" + type="text" /> + <div id="deleteButton" class="button deleteButton"></div> + </div> + + <div id="callButton" + class="button callButton callingFalse boxShadow4Active"></div> + + <div class="numbersBox"> + <div id="numberButton" data-id="1" + class="button numberButton fontSizeXLarge fontWeightBold fontColorNormal boxShadow4 boxShadow4Active">1</div> + <div id="numberButton" data-id="2" + class="button numberButton fontSizeXLarge fontWeightBold fontColorNormal boxShadow4 boxShadow4Active"> + 2 + <div + class="numberButtonLetters fontSizeSmall fontWeightBold fontColorTheme">ABC</div> + </div> + <div id="numberButton" data-id="3" + class="button numberButton fontSizeXLarge fontWeightBold fontColorNormal boxShadow4 boxShadow4Active"> + 3 + <div + class="numberButtonLetters fontSizeSmall fontWeightBold fontColorTheme">DEF</div> + </div> + <div id="numberButton" data-id="4" + class="button numberButton fontSizeXLarge fontWeightBold fontColorNormal boxShadow4 boxShadow4Active"> + 4 + <div + class="numberButtonLetters fontSizeSmall fontWeightBold fontColorTheme">GHI</div> + </div> + <div id="numberButton" data-id="5" + class="button numberButton fontSizeXLarge fontWeightBold fontColorNormal boxShadow4 boxShadow4Active"> + 5 + <div + class="numberButtonLetters fontSizeSmall fontWeightBold fontColorTheme">JKL</div> + </div> + <div id="numberButton" data-id="6" + class="button numberButton fontSizeXLarge fontWeightBold fontColorNormal boxShadow4 boxShadow4Active"> + 6 + <div + class="numberButtonLetters fontSizeSmall fontWeightBold fontColorTheme">MNO</div> + </div> + <div id="numberButton" data-id="7" + class="button numberButton fontSizeXLarge fontWeightBold fontColorNormal boxShadow4 boxShadow4Active"> + 7 + <div + class="numberButtonLetters fontSizeSmall fontWeightBold fontColorTheme">PQRS</div> + </div> + <div id="numberButton" data-id="8" + class="button numberButton fontSizeXLarge fontWeightBold fontColorNormal boxShadow4 boxShadow4Active"> + 8 + <div + class="numberButtonLetters fontSizeSmall fontWeightBold fontColorTheme">TUV</div> + </div> + <div id="numberButton" data-id="9" + class="button numberButton fontSizeXLarge fontWeightBold fontColorNormal boxShadow4 boxShadow4Active"> + 9 + <div + class="numberButtonLetters fontSizeSmall fontWeightBold fontColorTheme">WXYZ</div> + </div> + <div id="numberButton" data-id="*" + class="button numberButton fontSizeXLarge fontWeightBold fontColorTheme boxShadow4 boxShadow4Active specialChar">*</div> + <div id="numberButton" data-id="0" + class="button numberButton fontSizeXLarge fontWeightBold fontColorNormal boxShadow4 boxShadow4Active"> + 0 + <div + class="numberButtonLetters fontSizeSmall fontWeightBold fontColorTheme">+</div> + </div> + <div id="numberButton" data-id="#" + class="button numberButton fontSizeXLarge fontWeightBold fontColorTheme boxShadow4 boxShadow4Active specialChar">#</div> + </div> + + <div id="contactsCarouselBox" class= "contactsCarouselBox"> + <div id="loadingHistorySpinnerWrapper" class="loadingSpinnerHistory" style="display: none; "> + <div id="loadingSpinner" class="loadingSpinner "> + <div id="loadingSpinnerContent" class="loading-container"> + <div id="loadingSpinnerImg" class="loading"></div> + <div id="loadingHistorySpinnerText" class="loading-text fontSizeXXSmall fontWeightBold fontColorNormal">DOWNLOADING HISTORY</div> + </div> + </div> + </div> + <div id = "noPairedDevice" class = "noPairedDevice fontSizeXLarge fontWeightBold fontColorNormal" style="display: none; ">NO BT DEVICE SELECTED</div> + <ul id="contactsCarousel" class="contactsCarousel"></ul> + </div> + + <div id="callBox" class="callBox callBoxHidden"> + <div class="callInfoBox"> + <div class="callPhotoBox noContactPhoto"> + <img id="callPhoto" class="callPhoto" /> + </div> + <div id="callDuration" + class="callDuration fontSizeLarge fontWeightBold fontColorNormal">00:00</div> + <div id="inCallWith" class="inCallWith"></div> + <div id="callName" + class="callName fontSizeXLarge fontWeightBold fontColorNormal"></div> + <div id="callNumber" + class="callNumber fontSizeSmall fontWeightBold fontColorTheme"></div> + </div> + <div class="callControlsBox"> + <div + class="button controlButton fontSizeSmaller fontColorNormal fontWeightBold bgColorDark boxShadow4Active speakerButton">SPEAKER</div> + <div + class="button controlButton fontSizeSmaller fontColorNormal fontWeightBold bgColorDark boxShadow4Active muteButton">MUTE</div> + <div + class="button controlButton fontSizeSmaller fontColorNormal fontWeightBold bgColorDark boxShadow4Active holdButton">HOLD</div> + <div + class="button controlButton fontSizeSmaller fontColorNormal fontWeightBold bgColorDark boxShadow4Active addCallButton">ADD + CALL</div> + </div> + <div id="callVolumeControl"> + <div id = "VCicon"></div> + <div class = "sliderStart"></div> + <div id = "VCline" class = "bgColorTheme"></div> + <div id = "VCinner" class = "bgColorTheme boxShadow3"></div> + <div class="noVolumeSlider"></div> + </div> + </div> + + <div id="topBarIcons"></div> + <div id="bottomPanel" class="bottomPanel bottomPanelImg"></div> + <div id="library" class="library pageBgColorNormalTransparent"></div> +</body> +</html>
\ No newline at end of file diff --git a/js/callhistorycarousel.js b/js/callhistorycarousel.js new file mode 100644 index 0000000..9306bb9 --- /dev/null +++ b/js/callhistorycarousel.js @@ -0,0 +1,268 @@ +/*global Phone, callContactCarousel*/ + +/** + */ + /** + * This class provides methods to operate with call history carousel. It wrapps handling CarouFredSel object and provides + * following operations: + * + * * Show placed, received and missed phone calls ordered by date of phone call + * * For each call displays phone number, contact name, date and time of phone call + * + * @module PhoneApplication + * @class Carousel + * @constructor + */ +var Carousel = function() { + "use strict"; + this.initializeSwipe(); +}; +/** +* This property holds call history data array for show in html. +* @property callHistory {Array} +*/ +Carousel.prototype.callHistory = []; +/** +* This property holds swipe object for internal use in carousel. +* @property swipe {Object} +* @private +*/ +Carousel.prototype.swipe = null; +/** +* This property holds callback function which is called after current element in carousel is changed. +* @property indexChangeCallback {Object} +* @private +*/ +Carousel.prototype.indexChangeCallback = null; +/** + * This method adds listener for current carousel element change. + * + * @method addIndexChangeListener + * @param indexChangeCallback {function()} Callback function called after current carousel element changed. + */ +Carousel.prototype.addIndexChangeListener = function(indexChangeCallback) { + "use strict"; + + this.indexChangeCallback = indexChangeCallback; +}; +/** + * This method initializes and configures carousel object + * + * @method initializeSwipe + * @private + */ +Carousel.prototype.initializeSwipe = function() { + "use strict"; + + var self = this; + if (!this.swipe) { + this.swipe = $('#contactsCarousel').carouFredSel({ + auto : false, + circular : false, + infinite : false, + width : 765, + items : { + visible : 3 + }, + swipe : { + items : 1, + duration : 150, + onMouse : true, + onTouch : true + }, + scroll : { + items : 1, + duration : 150, + onAfter : function(data) { + if (!!self.indexChangeCallback) { + self.indexChangeCallback(self.getCurrentPosition()); + } + } + } + }); + if (!this.swipe.length) { + this.swipe = null; + } + } +}; +/** + * This method provides the index of current selected item of the carousel. + * + * @method getCurrentPosition + * @return Current {Integer} index position in carousel. + */ +Carousel.prototype.getCurrentPosition = function() { + "use strict"; + var self = this; + if (!!self.swipe) { + var pos = parseInt(self.swipe.triggerHandler("currentPosition"), 10); + return pos; + } + return null; +}; +/** + * This method moves current position of the carousel to the given index. + * + * @method slideTo + * @param index {Integer} New index position in carousel + */ +Carousel.prototype.slideTo = function(index) { + "use strict"; + if (!!this.swipe && index >= 0 && index < this.callHistory.length) { + this.swipe.trigger("slideTo", index); + } +}; +/** + * This method fills carousel with call history data and resets its current position to start. + * + * @method loadCallHistory + * @param callHistory {Array} Call history array. + * @param index {Integer} New index position in carousel. + */ +Carousel.prototype.loadCallHistory = function(callHistory, index) { + "use strict"; + this.removeAllItems(); + this.callHistory = callHistory; + this.insertPagesToSwipe(); + if (index >= 0 && index < this.callHistory.length && !!this.swipe) { + this.swipe.trigger("slideTo", [ index, 0, { + duration : 0 + } ]); + } +}; +/** + * This method creates one carousel item for swipe. + * + * @method createSwipeItem + * @param callHistory {Array} Call history array. + * @param index {Integer} Carousel item index it is use as div id in html. + * @return {String} New carousel item as html string. + * @private + */ +Carousel.prototype.createSwipeItem = function(callHistoryEntry, index) { + "use strict"; + var self = this; + + if (!!callHistoryEntry) { + var carouselItem; + + var contact = null; + if (!!callHistoryEntry.remoteParties && callHistoryEntry.remoteParties.length) { + contact = Phone.getContactByPersonId(callHistoryEntry.remoteParties[0].personId); + } + + var id = ""; + var name = ""; + var photoURI = ""; + var phoneNumber = ""; + var startTime = callHistoryEntry.startTime || ""; + var direction = callHistoryEntry.direction || ""; + + if (!!callHistoryEntry.remoteParties && callHistoryEntry.remoteParties.length) { + // personId = phoneNumber + phoneNumber = callHistoryEntry.remoteParties[0].personId; + } + + if (!!contact) { + if (!!contact.id) { + id = contact.id; + } + if (!!contact.photoURI) { + photoURI = contact.photoURI; + } + if (phoneNumber === "" && !!contact.phoneNumbers && contact.phoneNumbers.length) { + phoneNumber = contact.phoneNumbers[0].number; + } + name = Phone.getDisplayNameStr(contact); + } + + if (name === "") { + name = "Unknown"; + } + + carouselItem = '<li>'; + carouselItem += '<div id="carouselBox_' + index + '" class="carouselBox borderColorTheme" data-id="' + id + '">'; + carouselItem += '<div class="carouselPhotoArea borderColorTheme">'; + carouselItem += '<div class="carouselPhotoBox noContactPhoto">'; + carouselItem += '<img class="carouselPhoto" src="' + photoURI + '" /></div></div>'; + carouselItem += '<div class="carouselInfoBox carouselName fontSizeLarger fontWeightBold fontColorNormal">' + name + '</div>'; + carouselItem += '<div class="carouselInfoBox carouselNumber fontSizeSmall fontWeightBold fontColorTheme">' + phoneNumber + '</div>'; + carouselItem += '<div class="callHistoryElement borderColorTheme">'; + carouselItem += '<div class="missedNewCallIcon callHistoryIcon callHistoryIconGen"></div>'; + carouselItem += '<div class="callHistoryDetails">'; + carouselItem += '<div class="fontSizeXXSmall fontColorNormal">' + startTime + '</div>'; + carouselItem += '<div class="fontSizeXXSmall fontWeightBold fontColorTheme">' + direction + '</div></div></div></div>'; + carouselItem += '</li>'; + + carouselItem = $(carouselItem); + carouselItem.data("callhistory", callHistoryEntry); + carouselItem.data("contact", contact); + carouselItem.click(function() { + self.swipe.trigger("slideTo", [ $(this), -1 ]); + var hystoryEntry = $(this).data("callhistory"); + var contactEntry = $(this).data("contact"); + var contact = { + name : { + displayName : contactEntry.name.displayName, + firstName : contactEntry.name.firstName, + lastName : contactEntry.name.lastName + }, + photoURI : contactEntry.photoURI, + phoneNumbers : [ { + number : hystoryEntry.remoteParties[0].personId + } ] + + }; + callContactCarousel(contact); + }); + return carouselItem; + } + + return null; +}; +/** + * This method inserts pages whit carousel elements to swipe. + * + * @method insertPagesToSwipe + * @private + */ +Carousel.prototype.insertPagesToSwipe = function() { + "use strict"; + var self = this; + var carouselItem; + for ( var index = this.callHistory.length - 1; index >= 0; --index) { + carouselItem = this.createSwipeItem(this.callHistory[index], index); + if (!!carouselItem && !!this.swipe) { + this.swipe.trigger("insertItem", [ carouselItem, 0 ]); + } + } + this.addCarouselEdges(); +}; +/** + * This method removes all item from carousel. + * + * @method removeAllItems + */ +Carousel.prototype.removeAllItems = function() { + "use strict"; + var carouselItem; + + if (!!this.swipe) { + for ( var index = this.callHistory.length + 1; index >= 0; --index) { + this.swipe.trigger("removeItem", index); + } + } +}; +/** + * This method adds emty carousel items to the beginning and the end of the carousel + * (to make sure first and last visible items appear in the middle of screen instead of at the edges when swiped to edges of carousel). + * @method addCarouselEdges + */ +Carousel.prototype.addCarouselEdges = function() { + "use strict"; + if (!!this.swipe) { + var html = "<li><div class='carouselEdgeBox'></div></li>"; + this.swipe.trigger("insertItem", [ html, 0 ]); + this.swipe.trigger("insertItem", [ html, "end", true ]); + } +}; diff --git a/js/contacts_library.js b/js/contacts_library.js new file mode 100644 index 0000000..172f879 --- /dev/null +++ b/js/contacts_library.js @@ -0,0 +1,210 @@ +/*global Phone, callContactCarousel, GRID_TAB, LIST_TAB, loadTemplate, ko*/ + +/** + * Class which provides methods to operate with contacts library which displays all contact information (name, phone number, photo) from paired device ordered by name. + * + * @class ContactsLibrary + * @module PhoneApplication + */ +var ContactsLibrary = { + currentSelectedContact : "", + /** + * Method initializes contacts library. + * + * @method init + */ + init : function() { + "use strict"; + $('#library').library("setSectionTitle", "PHONE CONTACTS"); + $('#library').library("init"); + + var tabMenuModel = { + Tabs : [ { + text : "CONTACTS A-Z", + selected : true + } ] + }; + + $('#library').library("tabMenuTemplateCompile", tabMenuModel); + + $('#library').bind('eventClick_GridViewBtn', function() { + ContactsLibrary.showContacts(); + }); + + $('#library').bind('eventClick_ListViewBtn', function() { + ContactsLibrary.showContacts(); + }); + + $('#library').bind('eventClick_SearchViewBtn', function() { + }); + + $('#library').bind('eventClick_menuItemBtn', function() { + ContactsLibrary.showContacts(); + }); + + $('#library').bind('eventClick_closeSubpanel', function() { + }); + + $("#alphabetBookmarkList").on("letterClick", function(event, letter) { + console.log(letter); + Phone.contactsAlphabetFilter(letter === "*" ? "" : letter); + }); + + ContactsLibrary.showContacts(); + }, + /** + * Method unhides library page. + * + * @method show + */ + show : function() { + "use strict"; + $('#library').library("showPage"); + }, + /** + * Method hides library page. + * + * @method hide + */ + hide : function() { + "use strict"; + $('#library').library("hidePage"); + }, + /** + * Method opens contact detail. + * + * @method openContactDetail + * @param contact + * {Object} Object representing contact's information. + */ + openContactDetail : function(contact) { + "use strict"; + if (!!contact) { + ContactsLibrary.currentSelectedContact = contact; + var history = Phone.getCallHistoryByPersonId(contact.personId); + var formattedContact = ContactsLibrary.initContactDetail(contact); + formattedContact.history = history; + ContactsLibrary.renderContactDetailView(formattedContact); + } else { + console.log("Supplied contact is null."); + } + }, + /** + * Method renders search view. + * + * @method renderContactDetailView + * @param contact + * {Object} Contact object. + */ + renderContactDetailView : function(contact) { + "use strict"; + console.log("open contact called"); + var subpanelModel = { + textTitle : "CONTACT", + textSubtitle : contact.name || "Unknown", + actionName : "BACK", + action : function() { + console.log("back clicked"); + ContactsLibrary.showContacts(); + ContactsLibrary.currentSelectedContact = ""; + } + }; + $('#library').library("subpanelContentTemplateCompile", subpanelModel); + $('#library').library("clearContent"); + $('#library').library("setContentDelegate", "templates/libraryContactDetailDelegate.html"); + $('#library').library("contentTemplateCompile", contact, "contactDetail", function() { + $("#contactDetailMobileTitle").boxCaptionPlugin('initSmall', "MOBILE"); + $("#contactDetailEmailTitle").boxCaptionPlugin('initSmall', "EMAIL"); + $("#contactDetailAddressTitle").boxCaptionPlugin('initSmall', "ADDRESS"); + }); + }, + /** + * Method which shows contacts in grid or list view. + * + * @method showContacts + */ + showContacts : function() { + "use strict"; + console.log("show contacts called"); + var view = ""; + switch ($('#library').library('getSelectetLeftTabIndex')) { + case GRID_TAB: + view = "contactsLibraryContentGrid"; + break; + case LIST_TAB: + view = "contactsLibraryContentList"; + break; + default: + view = "contactsLibraryContentList"; + break; + } + $('#library').library('closeSubpanel'); + $('#library').library("clearContent"); + $('#library').library("changeContentClass", view); + loadTemplate("templates/", "template-contacts", function() { + var contactsElement = '<div data-bind="template: { name: \'template-contacts\', foreach: Phone.contactsComputed }"></div>'; + $(contactsElement).appendTo($('.' + view)); + ko.applyBindings(Phone); + }); + }, + /** + * Method which initializes contact detail. + * + * @method initContactDetail + * @param contact + * {Object} Contact object. + */ + initContactDetail : function(contact) { + "use strict"; + var tempContact = { + id : "", + name : "", + phoneNumber : "", + email : "", + photoURI : "", + address : "", + isFavorite : false, + history : [] + }; + + if (!!contact) { + var str = ""; + + if (!!contact.uid) { + tempContact.id = contact.uid; + } + + if (!!contact.name) { + tempContact.name = Phone.getDisplayNameStr(contact); + } + + if (!!contact.phoneNumbers && contact.phoneNumbers.length && !!contact.phoneNumbers[0].number) { + tempContact.phoneNumber = contact.phoneNumbers[0].number.trim(); + } + + if (!!contact.emails && contact.emails.length && !!contact.emails[0].email) { + tempContact.email = contact.emails[0].email.trim(); + } + + if (!!contact.photoURI) { + tempContact.photoURI = contact.photoURI.trim(); + } + + if (!!contact.addresses && contact.addresses.length) { + str = !!contact.addresses[0].streetAddress ? contact.addresses[0].streetAddress.trim() + "<br />" : ""; + str += !!contact.addresses[0].city ? contact.addresses[0].city.trim() + "<br />" : ""; + str += !!contact.addresses[0].country ? contact.addresses[0].country.trim() + "<br />" : ""; + str += !!contact.addresses[0].postalCode ? contact.addresses[0].postalCode.trim() : ""; + + if (str.toString().trim() === "") { + str = "-"; + } + + tempContact.address = str.trim(); + } + + tempContact.isFavorite = contact.isFavorite; + } + return tempContact; + } +}; diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..e6d9826 --- /dev/null +++ b/js/main.js @@ -0,0 +1,809 @@ +/*global disconnectCall, Bootstrap, Carousel, ContactsLibrary, getAppByID, disconnectCall, Configuration, Speech, Phone, changeCssBgImageColor, ThemeKeyColorSelected */ + +/** + * This application provides voice call from paired Bluetooth phone. Application uses following APIs: + * + * * {{#crossLink "Phone"}}{{/crossLink}} library + * * [tizen.bt]() as replacement of [tizen.bluetooth](https://developer.tizen.org/dev-guide/2.2.0/org.tizen.web.device.apireference/tizen/bluetooth.html) API due to + * conficts in underlying framework + * + * Application supports multiple connected devices, however only one of the devices can be selected at the time. + * Selection is done from {{#crossLink "Bluetooth"}}{{/crossLink}} UI. In case that phone has active call additional {{#crossLink "Carousel"}}{{/crossLink}} + * element is replaced by {{#crossLink "CallDuration"}}{{/crossLink}} element. + * + * Application allows following operations: + * + * * {{#crossLink "Phone/acceptCall:method"}}Place call{{/crossLink}} + * * Handles incoming calls passed from {{#crossLink "IncomingCall"}}{{/crossLink}} widget + * * Display {{#crossLink "Carousel"}}call history{{/crossLink}} + * * Display {{#crossLink "ContactsLibrary"}}contact list{{/crossLink}} + * * Mute/unmute call - not working due to [TIVI-2448](https://bugs.tizen.org/jira/browse/TIVI-2448) + * + * Additionaly application can be controlled using speech recognition via {{#crossLink "Speech"}}{{/crossLink}} component. + * + * Hover and click on elements in images below to navigate to components of Phone application. + * + * <img id="Image-Maps_1201312180420487" src="../assets/img/phone.png" usemap="#Image-Maps_1201312180420487" border="0" width="649" height="1152" alt="" /> + * <map id="_Image-Maps_1201312180420487" name="Image-Maps_1201312180420487"> + * <area shape="rect" coords="0,0,573,78" href="../classes/TopBarIcons.html" alt="top bar icons" title="Top bar icons" /> + * <area shape="rect" coords="0,77,644,132" href="../classes/Clock.html" alt="clock" title="Clock" /> + * <area shape="rect" coords="0,994,644,1147" href="../classes/BottomPanel.html" alt="bottom panel" title="Bottom panel" /> + * <area shape="rect" coords="573,1,644,76" href="../modules/Settings.html" alt="Settings" title="Settings" /> + * <area shape="rect" coords="552,136,646,181" href="../classes/ContactsLibrary.html" alt="Contacts library" title="Contacts library" /> + * <area shape="rect" coords="95,345,164,491" href="../classes/Phone.html#method_acceptCall" alt="Call button" title="Call button" /> + * <area shape="rect" coords="1,668,644,984" href="../classes/Carousel.html" alt="" title="History carousel" /> + * <area shape="rect" coords="171,181,471,635" alt="" href="../classes/keyboard.html" alt="Keyboard input" title="Keyboard input" > + * </map> + * + * @module PhoneApplication + * @main PhoneApplication + * @class Phone + */ + +/** + * Holds object of input for dialing phone number. + * + * @property telInput {Object} + */ +var telInput; +/** + * Instance of class Bootstrap, this class provides unified way to boot up the HTML applications by loading shared components in proper order. + * * {{#crossLink "Bootstrap"}}{{/crossLink}} + * + * @property bootstrap {Object} + */ +var bootstrap; + +/** +* Instance of class Carousel, this class provides methods to operate with hystory carousel. +* * {{#crossLink "Carousel"}}{{/crossLink}} +* +* @property callHistoryCarousel {Object} +*/ +var callHistoryCarousel = null; + +/** +* This property holds information about accept Phone call from Other widgets. +* If is true, phone call came from another widget. +* @property acceptPhoneCallFromOtherWidget {Boolean} +*/ +var acceptPhoneCallFromOtherWidget = false; + +/** + * Class handling user input from keyboard + * + * @class keyboard + * @static + */ +var keyboard = { + /** + * property holding Interval within which next click on the same key is considered as rotating associated characters with the given key. + * + * @property clickInterval {int} + */ + clickInterval: 1000, + + /** + * property holding info about last pressed key + * + * @property pressedKey {string} + */ + pressedKey: "-1", + + /** + * Array of input object holding info about main key character, all associated characters with the given key and index of currently used key + * + * @property inputs {Array of Object} + */ + inputs: [{ + key: "1", + values: ["1"], + index: 0 + }, { + key: "2", + values: ["2", "A", "B", "C"], + index: 0 + }, { + key: "3", + values: ["3", "D", "E", "F"], + index: 0 + }, { + key: "4", + values: ["4", "G", "H", "I"], + index: 0 + }, { + key: "5", + values: ["5", "J", "K", "L"], + index: 0 + }, { + key: "6", + values: ["6", "M", "N", "O"], + index: 0 + }, { + key: "7", + values: ["7", "P", "Q", "R", "S"], + index: 0 + }, { + key: "8", + values: ["8", "T", "U", "V"], + index: 0 + }, { + key: "9", + values: ["9", "W", "X", "Y", "Z"], + index: 0 + }, { + key: "*", + values: ["*"], + index: 0 + }, { + key: "0", + values: ["0", "+"], + index: 0 + }, { + key: "#", + values: ["#"], + index: 0 + }], + + /** + * property holding time of clickedInterval started + * + * @property startedTime {Date} + */ + startedTime: null, + + /** + * property holding input object associated with last pressed key + * + * @property selectedInput {Object} + */ + selectedInput: null, + + /** + * function starting clickInterval + * + * @method startTimer + */ + startTimer: function() { + "use strict"; + keyboard.startedTime = new Date(); + }, + + /** + * function testing if clickInterval expired + * + * @method intervalExpired + * @param currTime {Date} + */ + intervalExpired: function(currTime) { + "use strict"; + if (currTime - keyboard.startedTime > keyboard.clickInterval) { + keyboard.resetIndices(); + return true; + } + return false; + }, + + /** + * function reseting indices for all input objects to 0 + * + * @method resetIndices + */ + resetIndices: function() { + "use strict"; + for (var i in keyboard.inputs) { + if (keyboard.inputs.hasOwnProperty(i)) { + keyboard.inputs[i].index = 0; + } + } + }, + + /** + * function cycling associated input characters within clickInterval + * + * @method nextKey + */ + nextKey: function() { + "use strict"; + for (var i in keyboard.inputs) { + if (keyboard.pressedKey === keyboard.inputs[i].key) { + if (keyboard.inputs[i].values.length > 1) { + if (keyboard.inputs[i].index < keyboard.inputs[i].values.length - 1) { + keyboard.inputs[i].index += 1; + } else { + keyboard.inputs[i].index = 0; + } + } else { + keyboard.inputs[i].index = 0; + } + keyboard.selectedInput = keyboard.inputs[i]; + return keyboard.inputs[i].values[keyboard.inputs[i].index]; + } + } + return keyboard.pressedKey; + }, + + /** + * function setting selected input object based on last pressed key + * + * @method selectInput + */ + selectInput: function() { + "use strict"; + for (var i in keyboard.inputs) { + if (keyboard.pressedKey === keyboard.inputs[i].key) { + keyboard.selectedInput = keyboard.inputs[i]; + } + } + } +}; + +/** + * Holds status of calling panel initialization. + * + * @property callingPanelInitialized {Boolean} if is true, calling panel is initialized + * @default false + */ +var callingPanelInitialized = false; + +/** + * Class which provides initialize call info. + * + * @method initializeCallInfo + * @param contact {Object} Contact object. + * @for Phone + */ +function initializeCallInfo(contact) { + "use strict"; + var callNumber; + console.log(contact); + if ( !! contact) { + + if ( !! contact.name) { + var nameStr; + if (contact.name.displayName) { + nameStr = contact.name.displayName; + } else { + nameStr = !! contact.name.firstName ? contact.name.firstName : ""; + nameStr += !! contact.name.lastName ? " " + contact.name.lastName : ""; + } + $("#callName").html(nameStr.trim()); + } else { + $("#callName").html("Unknown"); + } + + if ( !! contact.phoneNumbers && contact.phoneNumbers.length) { + callNumber = !! contact.phoneNumbers[0].number ? contact.phoneNumbers[0].number : ""; + $("#callNumber").html(callNumber); + } else { + $("#callNumber").html("Unknown"); + } + + if ( !! contact.photoURI) { + $("#callPhoto").attr("src", contact.photoURI); + } + } else { + $("#callName").html("Unknown"); + $("#callNumber").html("Unknown"); + } + + if (!callingPanelInitialized) { + $(".noVolumeSlider").noUiSlider({ + range: [0, 100], + step: 1, + start: 50, + handles: 1, + connect: "lower", + orientation: "horizontal", + slide: function() { + var VolumeSlider = parseInt($(".noVolumeSlider").val(), 10); + console.log("noVolumeSlider" + VolumeSlider); + } + }); + callingPanelInitialized = true; + } + + if ($("#callButton").hasClass("callingFalse")) { + $("#callButton").removeClass("callingFalse"); + $("#callButton").addClass("callingTrue"); + } +} + +/** + * Class which provides methods to operate with call duration. Component show information about current call (call number or call contact, time duration of call). + * + * @class CallDuration + * @static + */ +var CallDuration = { + /** + * Holds value of seconds. + * + * @property sec {Integer} + */ + sec: 0, + /** + * Holds value of minutes. + * + * @property min {Integer} + */ + min: 0, + /** + * Holds value of hours. + * + * @property hour {Integer} + */ + hour: 0, + /** + * Holds object of timer. + * + * @property timeout {Object} + */ + timeout: null, + /** + * Method provides initialization of call timers. + * + * @method initialize + */ + startWatch: function() { + "use strict"; + var self = this; + if (!this.timeout) { + this.timeout = window.setInterval(function() { + self.stopwatch(); + }, 1000); + } else { + this.resetIt(); + this.timeout = window.setInterval(function() { + self.stopwatch(); + }, 1000); + } + }, + + /** + * Method provides reset call timers. + * + * @method resetIt + */ + resetIt: function() { + "use strict"; + CallDuration.sec = 0; + CallDuration.min = 0; + CallDuration.hour = 0; + window.clearTimeout(CallDuration.timeout); + var callStatus = tizen.phone.activeCall.state.toLowerCase(); + if (callStatus === "DIALING".toLowerCase()) { + $("#callDuration").html("DIALING"); + } else if (callStatus === "DISCONNECTED".toLowerCase()) { + $("#callDuration").html("ENDED"); + } else { + $("#callDuration").html( + ((CallDuration.min <= 9) ? "0" + CallDuration.min : CallDuration.min) + ":" + ((CallDuration.sec <= 9) ? "0" + CallDuration.sec : CallDuration.sec)); + } + + }, + /** + * Method provides call stop watch. + * + * @method stopwatch + */ + stopwatch: function() { + "use strict"; + + var callStatus = tizen.phone.activeCall.state.toLowerCase(); + if (callStatus === "DIALING".toLowerCase()) { + $("#callDuration").html("DIALING"); + } else if (callStatus === "DISCONNECTED".toLowerCase()) { + $("#callDuration").html("ENDED"); + + } else { + CallDuration.sec++; + if (CallDuration.sec === 60) { + CallDuration.sec = 0; + CallDuration.min++; + } + + if (CallDuration.min === 60) { + CallDuration.min = 0; + CallDuration.hour++; + } + $("#callDuration").html( + ((CallDuration.min <= 9) ? "0" + CallDuration.min : CallDuration.min) + ":" + ((CallDuration.sec <= 9) ? "0" + CallDuration.sec : CallDuration.sec)); + } + } +}; + +/** +* This property holds information about mute of call. If is true, phone call is mute. +* +* @property VolumeMuteStatus +* @default false +*/ +var VolumeMuteStatus = false; + +/** + * Class provides a muting of a call + * + * @method muteCall + * @for Phone + */ +function muteCall() { + "use strict"; + VolumeMuteStatus = VolumeMuteStatus ? false : true; + + // Not working due to TIVI-2448 + if (tizen.phone) { + //tizen.phone.muteCall(VolumeMuteStatus); + } + if (VolumeMuteStatus) { + changeCssBgImageColor(".muteButton", ThemeKeyColorSelected); + $(".muteButton").addClass("fontColorSelected"); + } else { + changeCssBgImageColor(".muteButton", "#FFFFFF"); + $(".muteButton").removeClass("fontColorSelected"); + } +} + +/** + * Class which provides methods to call contact. + * + * @method acceptCall + * @param contact {Object} Contact object. + * @for Phone + */ +function acceptCall(contact) { + "use strict"; + + ContactsLibrary.hide(); + if ($("#settingsTabs").tabs) { + $("#settingsTabs").tabs("hidePage"); + } + $("#callBox").removeClass("callBoxHidden"); + $("#callBox").addClass("callBoxShow"); + $('#contactsCarouselBox').removeClass("contactsCarouselBoxShow"); + $('#contactsCarouselBox').addClass("contactsCarouselBoxHide"); + if (tizen.phone) { + CallDuration.resetIt(); + + initializeCallInfo(contact); + var callStatus = tizen.phone.activeCall.state.toLowerCase(); + if (callStatus !== "ACTIVE".toLowerCase() && callStatus !== "DIALING".toLowerCase()) { + + if (callStatus === "INCOMING".toLowerCase()) { + tizen.phone.answerCall(function(result) { + console.log(result.message); + }); + } else if (callStatus === "DISCONNECTED".toLowerCase()) { + + var callNumber = contact.phoneNumbers[0] && contact.phoneNumbers[0].number ? contact.phoneNumbers[0].number : ""; + tizen.phone.invokeCall(callNumber, function(result) { + console.log(result.message); + }); + + } + + } else if (callStatus === "ACTIVE".toLowerCase()) { + CallDuration.startWatch(); + } + } +} + +/** + * Class which provides disconnect call. + * + * @method disconnectCall + * @for Phone + */ +function disconnectCall() { + "use strict"; + $("#callButton").removeClass("callingTrue"); + $("#callButton").addClass("callingFalse"); + if (acceptPhoneCallFromOtherWidget !== true) { + $("#callBox").removeClass("callBoxShow"); + $("#callBox").addClass("callBoxHidden"); + $('#contactsCarouselBox').removeClass("contactsCarouselBoxHide"); + $('#contactsCarouselBox').addClass("contactsCarouselBoxShow"); + } + CallDuration.resetIt(); + if (tizen.phone) { + tizen.phone.hangupCall(function(result) { + console.log(result.message); + }); + } + $("#inputPhoneNumber").val(''); +} + +$(document).ready( + function() { + "use strict"; + setTimeout(function() { + /* initialize phone widget by remote device status */ + if (tizen.phone) { + tizen.phone.getSelectedRemoteDevice(function(selectedRemoteDevice){ + if (selectedRemoteDevice !== "") { + $("#noPairedDevice").hide(); + $("#loadingHistorySpinnerWrapper").show(); + } else { + $("#noPairedDevice").show(); + } + }); + /* initialize phone widget by active call status */ + var callStatus = tizen.phone.activeCall.state.toLowerCase(); + if (callStatus === "INCOMING".toLowerCase() || callStatus === "DIALING".toLowerCase() || callStatus === "ACTIVE".toLowerCase()) { + var contact; + if (tizen.phone.callState) { + contact = tizen.phone.activeCall.contact; + } + acceptPhoneCallFromOtherWidget = true; + acceptCall(contact); + } else if (callStatus === "DISCONNECTED".toLowerCase()) { + disconnectCall(); + } + } + /* start keyboard timer */ + keyboard.startTimer(); + /* initialize bootstrap */ + bootstrap = new Bootstrap(function(status) { + telInput = $("#inputPhoneNumber"); + $("#clockElement").ClockPlugin('init', 5); + $("#clockElement").ClockPlugin('startTimer'); + $("#topBarIcons").topBarIconsPlugin('init', 'phone'); + $('#bottomPanel').bottomPanel('init'); + if (!callHistoryCarousel) { + + callHistoryCarousel = new Carousel(); + } + + if (typeof Phone !== "undefined") { + /* add listener to selected remote device */ + tizen.phone.addRemoteDeviceSelectedListener(function(returnID) { + if ((!!returnID && !!returnID.error) || (!!returnID && !!returnID.value && returnID.value === "")) { + $("#loadingHistorySpinnerWrapper").hide(); + $(".caroufredsel_wrapper").hide(); + $("#noPairedDevice").show(); + } else { + $("#noPairedDevice").hide(); + $("#loadingHistorySpinnerWrapper").show(); + $(".caroufredsel_wrapper").show(); + } + }); + /* initialize contacts and call history, if not accept phone call from another widget */ + if (acceptPhoneCallFromOtherWidget !== true) { + window.setTimeout(function() { + Phone.loadContacts(function(err) { + if (!err) { + ContactsLibrary.init(); + Phone.loadCallHistory(function(err) { + if (!err) { + $("#loadingHistorySpinnerWrapper").hide(); + callHistoryCarousel.loadCallHistory(Phone.callHistory(), 0); + } + }); + } + }); + + }, 2000); + } + /* add listener to change contacts list */ + tizen.phone.addContactsChangedListener(function() { + if (acceptPhoneCallFromOtherWidget !== true) { + window.setTimeout(function() { + Phone.loadContacts(function(err) { + if (!err) { + ContactsLibrary.init(); + } + }); + }, 1000); + } + }); + /* add listener to change call history */ + tizen.phone.addCallHistoryChangedListener(function() { + $("#loadingHistorySpinnerWrapper").show(); + if (acceptPhoneCallFromOtherWidget !== true) { + window.setTimeout(function() { + Phone.loadCallHistory(function(err) { + if (!err) { + $("#loadingHistorySpinnerWrapper").hide(); + callHistoryCarousel.loadCallHistory(Phone.callHistory(), 0); + + } + }); + }, 1000); + } + + }); + } + + $("#contactsLibraryButton").bind('click', function() { + + ContactsLibrary.show(); + + }); + + $(".numbersBox").delegate("#numberButton", "click", function() { + var pressTime = new Date(), + number, oneCharPX = 32; + if (keyboard.intervalExpired(pressTime)) { + number = telInput.attr("value") + $(this).data("id"); + telInput.attr("value", number); + $('#inputPhoneNumber').scrollLeft(number.length * oneCharPX); + keyboard.pressedKey = $(this).data("id").toString(); + + } else { + if (keyboard.pressedKey === "-1" || keyboard.pressedKey !== $(this).data("id").toString()) { + number = telInput.attr("value") + $(this).data("id"); + telInput.attr("value", number); + $('#inputPhoneNumber').scrollLeft(number.length * oneCharPX); + keyboard.pressedKey = $(this).data("id").toString(); + } else { + var phoneNumText = telInput.attr("value"); + if (keyboard.pressedKey === $(this).data("id").toString() && keyboard.selectedInput !== null && keyboard.selectedInput.values.length === 1) { + number = telInput.attr("value") + $(this).data("id"); + telInput.attr("value", number); + $('#inputPhoneNumber').scrollLeft(number.length * oneCharPX); + } else { + var numToUpdate = phoneNumText.slice(0, phoneNumText.length - 1); + numToUpdate += keyboard.nextKey(); + telInput.attr("value", numToUpdate); + + } + } + } + keyboard.selectInput(); + keyboard.startTimer(); + return false; + }); + + $(".inputPhoneNumberBox").delegate("#deleteButton", "click", function() { + var number = telInput.attr("value"); + number = number.slice(0, number.length - 1); + telInput.attr("value", number); + return false; + }); + + $('#callButton').bind('click', function() { + var phoneNumber = $("#inputPhoneNumber").val(); + if ($("#callBox").hasClass("callBoxShow")) { + disconnectCall(); + } else if (phoneNumber !== "") { + var contact = Phone.getContactByPhoneNumber(phoneNumber); + if (contact === null) { + + contact = { + phoneNumbers: [{ + number: phoneNumber + }] + }; + + } + acceptCall(contact); + } + }); + $('.muteButton').bind('click', function() { + muteCall(); + }); + if (tizen.phone) { + /* add listener to change call history entry, because if call is ended tizen.phone give back only last history object */ + tizen.phone.addCallHistoryEntryAddedListener(function(contact) { + if (acceptPhoneCallFromOtherWidget !== true) { + + + var tmpCallHistory = Phone.callHistory(); + var tmpContact = []; + tmpContact.push(contact); + tmpContact = Phone.formatCallHistory(tmpContact); + tmpCallHistory.unshift(tmpContact[0]); + Phone.callHistory(tmpCallHistory); + + callHistoryCarousel.loadCallHistory(Phone.callHistory(), 0); + + } + }); + /* add listener to change call state */ + tizen.phone.addCallChangedListener(function(result) { + var contact; + if ( !! result.contact.name) { + contact = result.contact; + } else { + contact = { + phoneNumbers: [{ + /* jshint camelcase: false */ + number: tizen.phone.activeCall.line_id + /* jshint camelcase: true */ + }] + + }; + } + + console.log("result.state " + result.state); + + switch (result.state.toLowerCase()) { + case "DISCONNECTED".toLowerCase(): + + disconnectCall(contact); + + if (acceptPhoneCallFromOtherWidget === true) { + + window.setTimeout(function() { + if (typeof tizen !== "undefined") { + tizen.application.getCurrentApplication().exit(); + } + }, 1000); + } + + Configuration.set("acceptedCall", "false"); + + break; + case "ACTIVE".toLowerCase(): + if (Configuration._values.acceptedCall !== "true") { + /* global self */ + self.incomingCall.acceptIncommingCall(); + CallDuration.startWatch(); + console.log("phone active"); + Configuration.set("acceptedCall", "true"); + } + break; + case "DIALING".toLowerCase(): + acceptCall(contact); + break; + } + }); + } + + if (typeof(Speech) !== 'undefined') { + /* add listener to voice recognition */ + Speech.addVoiceRecognitionListener({ + oncall: function() { + if (ContactsLibrary.currentSelectedContact !== "" && $('#library').library("isVisible")) { + acceptCall(ContactsLibrary.currentSelectedContact); + } + } + }); + } else { + console.warn("Speech API is not available."); + } + + }); + }, 0); + + }); + +/** + * Class which provides call contact carousel. + * + * @method callContactCarousel + * @param contact {Object} Contact object. + * @for Phone + * + */ + +function callContactCarousel(contact) { + "use strict"; + + acceptCall(contact); +} + +/** + * Class which provides call by contact ID. + * + * @method callContactById + * @param contactId {String} Contact id. + * @for Phone + */ +function callContactById(contactId) { + "use strict"; + $("#contactDetailMobile").addClass("fontColorSelected "); + if (contactId !== "" && typeof contactId !== undefined) { + + var contactObject = Phone.getContactById(contactId); + if ( !! contactObject) { + window.setTimeout(function() { + acceptCall(contactObject); + $("#contactDetailMobile").removeClass("fontColorSelected "); + }, 500); + } else { + console.log("contact not found"); + $("#contactDetailMobile").removeClass("fontColorSelected "); + } + } +} diff --git a/js/phone.js b/js/phone.js new file mode 100644 index 0000000..35ebedd --- /dev/null +++ b/js/phone.js @@ -0,0 +1,361 @@ +/* global ko, formatPhoneNumber, moment */ + +/** + * This class provides methods to operate with contacts and call history from [tizen.phone](./native/group__phoned.html). + * Sample contact object looks like: + * + * { + * uid: "JeremyMartinson15417543010", + * personId: "15417543010", + * name: { + * firstName: "Jeremy", + * lastName: "Martinson" + * displayName :"Jeremy Martinson" + * }, + * phoneNumbers: { + * number:"1-541-754-3010" + * }, + * emails: { + * email: "Jeremy.Martinson@gmail.com" + * }, + * photoURI: { + * photoURI: "url://" + * }, + * addresses: { + * streetAddress: "455 Larkspur Dr.", + * city: "San Jose", + * country: "California", + * postalCode: " " + * } + * } + * + * @class Phone + * @module PhoneApplication + * @constructor + */ +var Phone = (function() { + "use strict"; + + function Phone() { + var self = this; + if (typeof(tizen.phone) !== 'undefined') { + this.phone = tizen.phone; + } else { + this.phone = null; + } + this.contacts = ko.observableArray([]); + this.callHistory = ko.observableArray([]); + + this.contactsAlphabetFilter = ko.observable(""); + + this.contactsComputed = ko.computed(function() { + if (self.contactsAlphabetFilter() !== "") { + return ko.utils.arrayFilter(self.contacts(), function(contact) { + if ( !! contact.name && !! contact.name.leftLastName) { + return contact.name.lastName.toString().toLowerCase().trim().indexOf( + self.contactsAlphabetFilter().toString().toLowerCase().trim()) === 0; + } + return false; + }); + } + return self.contacts(); + }); + } + +/** + * This method download contacts from tizen.phone. Using API method tizen.phone.getContacts. + * @method loadContacts + * @param callback {function(error)} Callback function called after method is finished. Parameter `error` will contain any error that was intercepted. + */ + + Phone.prototype.loadContacts = function(callback) { + var self = this; + var i, conntactsArrayLength; + if ( !! self.phone) { + + self.phone.getContacts(0, function(contactsArray) { + + contactsArray = self.formatContacts(contactsArray); + self.contacts(contactsArray); + if ( !! callback) { + callback(null); + } + }, function(err) { + console.log("Error(" + err.code + "): " + err.message); + if ( !! callback) { + callback(err); + } + }); + } else { + if ( !! callback) { + callback("Phone API is not available."); + } + } + }; + + /** + * This method provides compare contact by last name, it is use in observable array from [knockout library](http://knockoutjs.com/documentation/observableArrays.html). + * @method compareByLastName + * @param left {Object} left compare element + * @param right {Object} right compare element + */ + + Phone.prototype.compareByLastName = function(left, right) { + + var leftLastName = "Unknown"; + if ( !!left.name && !!left.name.lastName && left.name.lastName !== "") { + leftLastName = left.name.lastName; + } + leftLastName = leftLastName.toString().trim().toLowerCase(); + var rightLastName = "Unknown"; + if ( !!right.name && !!right.name.lastName && right.name.lastName !== "") { + rightLastName = right.name.lastName; + } + rightLastName = rightLastName.toString().trim().toLowerCase(); + return leftLastName === rightLastName ? 0 : (leftLastName < rightLastName) ? -1 : 1; + }; + /** + * This method compares call history items by date, it is used in observable array from [knockout library](http://knockoutjs.com/documentation/observableArrays.html). + * @method compareHistoryByDate + * @param left {Object} left compare element + * @param right {Object} right compare element + */ + Phone.prototype.compareHistoryByDate = function(left, right) { + + var monthNames = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"]; + + var tmpLeftDateTime = left.startTime.toUpperCase().split(" "); + var tmpLeftDate = tmpLeftDateTime[0].split("/"); + tmpLeftDate[0] = tmpLeftDate[0].indexOf(tmpLeftDate[0]) + 1; + var tmpLeftTime = tmpLeftDateTime[1].split(":"); + var leftDate = new Date(parseInt(tmpLeftDate[0], 10), parseInt(tmpLeftDate[1], 10), parseInt(tmpLeftDate[2], 10), parseInt(tmpLeftTime[0], 10), parseInt(tmpLeftTime[1], 10)); + + var tmpRightDateTime = right.startTime.toUpperCase().split(" "); + var tmpRightDate = tmpRightDateTime[0].split("/"); + tmpRightDate[0] = tmpRightDate[0].indexOf(tmpRightDate[0]) + 1; + var tmpRightTime = tmpRightDateTime[1].split(":"); + var rightDate = new Date(parseInt(tmpRightDate[0], 10), parseInt(tmpRightDate[1], 10), parseInt(tmpRightDate[2], 10), parseInt(tmpRightTime[0], 10), parseInt(tmpRightTime[1], 10)); + + return leftDate === rightDate ? 0 : (leftDate > rightDate) ? -1 : 1; + + }; + /** + * This method clear contacts array. + * @method clearContacts + */ + Phone.prototype.clearContacts = function() { + + var self = this; + self.contacts.removeAll(); + self.contacts([]); + }; + /** + * This method searches contact in contacts list by id . + * @method getContactById + * @param id {String} search contact id + * @return {Object} if contact is found, return contact. Else return first element from contacts list. + */ + Phone.prototype.getContactById = function(id) { + + var self = this; + var contact = ko.utils.arrayFirst(self.contacts(), function(contact) { + return contact.uid === id; + }); + return contact; + }; + /** + * This method searches contact in contacts list by personId. + * @method getContactByPersonId + * @param id {String} search contact personId + * @return {Object} if contact is found, return contact. Else return first element from contacts list. + */ + Phone.prototype.getContactByPersonId = function(personId) { + + var self = this; + var contact = ko.utils.arrayFirst(self.contacts(), function(contact) { + if (contact.personId === personId) { + return true; + } + + if ( !! contact.phoneNumbers && contact.phoneNumbers.length) { + for (var i = 0; i < contact.phoneNumbers.length; ++i) { + if ( !! contact.phoneNumbers[i].number && contact.phoneNumbers[i].number === personId) { + return true; + } + } + } + + return false; + }); + return contact; + }; + /** + * This method searches contact in contacts list by phoneNumber. + * @method getContactByPhoneNumber + * @param id {String} search contact phoneNumber + * @return {Object} if contact is found, return contact. Else return first element from contacts list. + */ + Phone.prototype.getContactByPhoneNumber = function(phoneNumber) { + + var self = this; + if ( !! phoneNumber && phoneNumber !== "") { + phoneNumber = formatPhoneNumber(phoneNumber); + var foundContact = ko.utils.arrayFirst(self.contacts(), function(contact) { + if ( !! contact.phoneNumbers && contact.phoneNumbers.length) { + + + for (var i = 0; i < contact.phoneNumbers.length; ++i) { + if ( !! contact.phoneNumbers[i].number && contact.phoneNumbers[i].number === phoneNumber) { + return true; + } + } + } + return false; + }); + return foundContact; + } + return null; + }; + /** + * This method composes display name from contact names. + * @method getDisplayNameStr + * @param contact {Object} contact object + * @return {String} contact name for display + */ + Phone.prototype.getDisplayNameStr = function(contact) { + + var self = this; + if ( !! contact && !! contact.name) { + var name = []; + if ( !! contact.name.lastName && contact.name.lastName !== "") { + name.push(contact.name.lastName); + } + if ( !! contact.name.firstName && contact.name.firstName !== "") { + name.push(contact.name.firstName); + } + if (name.length === 0 && !! contact.name.displayName && contact.name.displayName !== "") { + name.push(contact.name.displayName); + } + return name.join(" "); + } + return "Unknown"; + }; + /** + * This method downloads call history from tizen.phone. Using API method tizen.phone.getCallHistory. + + * @method loadCallHistory + * @param callback {function(error)} Callback function called after method is finished. Parameter `error` will contain any error that was intercepted. + */ + Phone.prototype.loadCallHistory = function(callback) { + + var self = this; + var i; + var callHistoryArrayLength; + if ( !! self.phone) { + self.phone.getCallHistory(25, function(callHistoryArray) { + callHistoryArray = self.formatCallHistory(callHistoryArray); + + self.callHistory(callHistoryArray); + if ( !! callback) { + callback(null); + } + }, function(err) { + console.log("Error(" + err.code + "): " + err.message); + if ( !! callback) { + callback(err); + } + }); + } else { + if ( !! callback) { + callback("Phone API is not available."); + } + } + }; + /** + * This method prepares contact data to be displayed in html. + + * @method formatContacts + * @param contactsList {Array} list of contacts + * @return {Array} list of contacts + */ + Phone.prototype.formatContacts = function(contactsList) { + var i, j; + var contactsListLength; + var phoneNumbersLength; + contactsListLength = contactsList.length; + for (i = 0; i < contactsListLength; i++) { + if (!contactsList[i].photoURI) { + contactsList[i].photoURI = null; + } + if (!contactsList[i].addresses) { + contactsList[i].addresses = null; + } + + if (!contactsList[i].emails) { + contactsList[i].emails = null; + } + if ( !! contactsList[i].phoneNumbers) { + phoneNumbersLength = contactsList[i].phoneNumbers.length; + for (j = 0; j < phoneNumbersLength; j++) { + contactsList[i].phoneNumbers[j].number = formatPhoneNumber(contactsList[i].phoneNumbers[j].number); + } + } + } + return contactsList; + }; + /** + * This method prepares call history data to be displayed in html. + + * @method formatCallHistory + * @param callHistoryList {Array} list of call history + * @return {Array} call history array + */ + Phone.prototype.formatCallHistory = function(callHistoryList) { + + var callHistoryListLength; + var i; + callHistoryListLength = callHistoryList.length; + for (i = 0; i < callHistoryListLength; i++) { + if ( !! callHistoryList[i].startTime) { + var date = callHistoryList[i].startTime; + callHistoryList[i].startTime = moment(date).format("MMM/DD/YYYY HH:mm"); + } else { + callHistoryList[i].startTime = null; + } + } + return callHistoryList; + }; + /** + * This method clears call history. + * @method clearCallHistory + */ + Phone.prototype.clearCallHistory = function() { + + var self = this; + self.callHistory.removeAll(); + self.callHistory([]); + }; + /** + * This method searches call history by contact personID. + * @method getCallHistoryByPersonId + * @param personId {string} search contact personId + * @return {Array} call history array + */ + Phone.prototype.getCallHistoryByPersonId = function(personId) { + + var self = this; + var callHistory = ko.utils.arrayFilter(self.callHistory(), function(callHistoryEntry) { + if ( !! callHistoryEntry.remoteParties) { + for (var i = 0; i < callHistoryEntry.remoteParties.length; ++i) { + if (callHistoryEntry.remoteParties[i].personId === personId) { + return true; + } + } + } + return false; + }); + return callHistory; + }; + window.__phone = undefined === window.__phone ? new Phone() : window.__phone; + return window.__phone; +})();
\ No newline at end of file diff --git a/packaging/html5-ui-phone.changes b/packaging/html5-ui-phone.changes new file mode 100644 index 0000000..ef2d9f8 --- /dev/null +++ b/packaging/html5-ui-phone.changes @@ -0,0 +1,4 @@ +* Thu Mar 06 2014 brianjjones <brian.j.jones@intel.com> 4245f58 +- Initial commit of the Phone app + + diff --git a/packaging/html5-ui-phone.spec b/packaging/html5-ui-phone.spec new file mode 100644 index 0000000..4298f0f --- /dev/null +++ b/packaging/html5-ui-phone.spec @@ -0,0 +1,36 @@ +Name: html5_UI_Phone +Summary: A proof of concept pure html5 UI +Version: 0.0.1 +Release: 1 +Group: Applications/System +License: Apache 2.0 +URL: http://www.tizen.org +Source0: %{name}-%{version}.tar.bz2 +BuildRequires: zip +BuildRequires: html5_UI_Common +Requires: wrt-installer +Requires: wrt-plugins-ivi + +%description +A proof of concept pure html5 UI + +%prep +%setup -q -n %{name}-%{version} + +%build + +make wgtPkg + +%install +rm -rf %{buildroot} +%make_install + +%post + wrt-installer -i /opt/usr/apps/.preinstallWidgets/html5UIPhone.wgt; + +%postun + wrt-installer -un html5POC09.Phone + +%files +%defattr(-,root,root,-) +/opt/usr/apps/.preinstallWidgets/html5UIPhone.wgt diff --git a/templates/contactCarouselDelegate.html b/templates/contactCarouselDelegate.html new file mode 100644 index 0000000..54a4efd --- /dev/null +++ b/templates/contactCarouselDelegate.html @@ -0,0 +1,24 @@ +<li><div id="carouselBox_{{:#index}}" class="carouselBox borderColorTheme" data-id="{{:id}}" onclick="callContactById('{{:id}}');"> + <div class="carouselPhotoArea borderColorTheme"> + <div class="carouselPhotoBox noContactPhoto"> + <img class="carouselPhoto" src="{{:photoURI}}" /> + </div> + </div> + <!-- <div class="carouselInfoBox carouselCallContact"></div> --> + <div + class="carouselInfoBox carouselName fontSizeLarger fontWeightBold fontColorNormal"> + {{if name}} {{if name.firstName}} {{:name.firstName}} {{/if}} {{if + name.lastName}} {{:name.lastName}} {{/if}} {{/if}}</div> + <div class="carouselInfoBox carouselNumber fontSizeSmall fontWeightBold fontColorTheme"> + {{if phoneNumbers}} {{if phoneNumbers[0]}} + {{:phoneNumbers[0].number}} {{/if}} {{/if}}</div> + <div class="callHistoryElement borderColorTheme"> + <div class="{{if direction == 'missed-new'}}missedNew{{else}}{{:direction.toLowerCase()}}{{/if}}CallIcon callHistoryIcon callHistoryIconGen"></div> + <div class="callHistoryDetails"> + <div class="fontSizeXXSmall fontColorNormal">{{:startTime.toString().toUpperCase()}}</div> + <div class="fontSizeXXSmall fontWeightBold fontColorTheme">{{:direction.toString().toUpperCase()}}</div> + </div> + </div> + + </div> +</li> diff --git a/templates/libraryContactDetailDelegate.html b/templates/libraryContactDetailDelegate.html new file mode 100644 index 0000000..4d46c86 --- /dev/null +++ b/templates/libraryContactDetailDelegate.html @@ -0,0 +1,47 @@ + +<div class="contactDetailBox1"> + <div class="contactDetailPhotoBox noContactPhoto"> + <img class="contactDetailPhoto" src="{{:photoURI}}" /> + </div> + <div + class="contactDetailFavorite isFavorite{{if isFavorite}}True{{else}}False{{/if}}"> + </div> +</div> + +<div class="contactDetailBox2"> + <div class="contactDetailBox3" onClick="callContactById('{{:id}}')"> + <div class="phoneIcon"></div> + <div class="contactDetailBox4"> + <div id="contactDetailMobileTitle">MOBILE</div> + <div id= "contactDetailMobile" class="fontSizeLarger fontWeightBold fontColorNormal">{{:phoneNumber}}</div> + </div> + + </div> + <div class="contactDetailBox3"> + <div class="emailIcon"></div> + <div class="contactDetailBox4"> + <div id="contactDetailEmailTitle">EMAIL</div> + <div class="fontSizeLarger fontWeightBold fontColorNormal">{{:email}}</div> + </div> + </div> + <div class="contactDetailBox3"> + <div class="addressIcon"></div> + <div class="contactDetailBox4"> + <div id="contactDetailAddressTitle">ADDRESS</div> + <div class="fontSizeLarger fontWeightBold fontColorNormal">{{:address.toUpperCase()}}</div> + </div> + </div> +</div> + +<div class="callHistoryBox borderColorTheme"> + {{foreach history}} + <div class="callHistoryElement borderColorTheme"> + <div + class="{{if direction == 'missed-new'}}missedNew{{else}}{{:direction.toString().toLowerCase()}}{{/if}}CallIcon callHistoryIcon"></div> + <div class="callHistoryDetails"> + <div class="fontSizeLarge fontWeightBold fontColorNormal">{{:startTime.toString().toUpperCase()}}</div> + <div class="fontSizeXSmall fontWeightBold fontColorTheme">{{:direction.toString().toUpperCase()}}</div> + </div> + </div> + {{/foreach}} +</div>
\ No newline at end of file diff --git a/templates/template-contacts.html b/templates/template-contacts.html new file mode 100644 index 0000000..b14892a --- /dev/null +++ b/templates/template-contacts.html @@ -0,0 +1,10 @@ +<div class="contactElement boxShadow4 borderColorTheme" + data-bind="click: ContactsLibrary.openContactDetail"> + <div class="phoneIcon"></div> + <div class="contactPhotoBox noContactPhoto"> + <img class="contactPhoto" data-bind="attr:{src: photoURI}" /> + </div> + <div + class="contactName fontSizeLarge fontWeightBold fontColorNormal textBgColorNormalTransparent" + data-bind="text: Phone.getDisplayNameStr($data)"></div> +</div> |