summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbrianjjones <brian.j.jones@intel.com>2014-03-06 15:29:50 -0800
committerbrianjjones <brian.j.jones@intel.com>2014-03-06 15:55:00 -0800
commit53fd8518caff406b6b011a4bafb9f106edd95d36 (patch)
treebc20706e97738a16e6a479004c4cc83d4f9915a4
downloadhtml5_UI_Phone-53fd8518caff406b6b011a4bafb9f106edd95d36.tar.gz
html5_UI_Phone-53fd8518caff406b6b011a4bafb9f106edd95d36.tar.bz2
html5_UI_Phone-53fd8518caff406b6b011a4bafb9f106edd95d36.zip
Change-Id: Ief600f67c9b101aa5634858a8606ed48d513d462
-rw-r--r--Makefile20
-rw-r--r--config.xml18
-rw-r--r--css/contacts_library.css216
-rw-r--r--css/images/null.pngbin0 -> 2791 bytes
-rw-r--r--css/phone_style.css277
-rw-r--r--data/contacts.json202
-rw-r--r--data/history.json313
-rw-r--r--data/photos/nophoto.pngbin0 -> 3117 bytes
-rw-r--r--icon.pngbin0 -> 2511 bytes
-rw-r--r--index.html182
-rw-r--r--js/callhistorycarousel.js268
-rw-r--r--js/contacts_library.js210
-rw-r--r--js/main.js809
-rw-r--r--js/phone.js361
-rw-r--r--packaging/html5-ui-phone.changes4
-rw-r--r--packaging/html5-ui-phone.spec36
-rw-r--r--templates/contactCarouselDelegate.html24
-rw-r--r--templates/libraryContactDetailDelegate.html47
-rw-r--r--templates/template-contacts.html10
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
new file mode 100644
index 0000000..841863c
--- /dev/null
+++ b/css/images/null.png
Binary files differ
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
new file mode 100644
index 0000000..3f812de
--- /dev/null
+++ b/data/photos/nophoto.png
Binary files differ
diff --git a/icon.png b/icon.png
new file mode 100644
index 0000000..5d8de5d
--- /dev/null
+++ b/icon.png
Binary files differ
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>