From: Piotr Dabrowski Date: Wed, 2 Oct 2013 08:19:31 +0000 (+0200) Subject: application sources from tizen_2.2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=79f9ba132e14712728230f5930f9e91660066449;p=apps%2Fweb%2Fsample%2FBluetoothChat.git application sources from tizen_2.2 Change-Id: Iefce6159f80ca8d9dae4852c2884af5c8a4da1da --- diff --git a/bluetoothchat-snapshot.png b/bluetoothchat-snapshot.png new file mode 100644 index 0000000..e972127 Binary files /dev/null and b/bluetoothchat-snapshot.png differ diff --git a/description.xml b/description.xml new file mode 100755 index 0000000..65570f3 --- /dev/null +++ b/description.xml @@ -0,0 +1,10 @@ + + + + BluetoothChat + 1.0.0 + bluetoothchat-snapshot.png + + A sample application demonstrating the tizen device API usage. + + diff --git a/description.xsl b/description.xsl new file mode 100755 index 0000000..1f4f57f --- /dev/null +++ b/description.xsl @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + +
+ + + +
+ Type: JavaScript +

+ +

+
+ + + + + +
+ + +
+ +
diff --git a/project/.project b/project/.project new file mode 100644 index 0000000..ea56a52 --- /dev/null +++ b/project/.project @@ -0,0 +1,63 @@ + + + BluetoothChat + + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + json.validation.builder + + + + + org.tizen.web.jslint.nature.JSLintBuilder + + + + + org.tizen.web.css.nature.CSSBuilder + + + + + org.eclipse.wst.validation.validationbuilder + + + + + org.tizen.web.project.builder.WebBuilder + + + usedLibraryType + WebUIFramework + + + + + org.tizen.web.privilege.nature.PrivilegeBuilder + + + + + org.tizen.web.editor.css.nature.CSSBuilder + + + + + + json.validation.nature + org.tizen.web.jslint.nature.JSLintNature + org.tizen.web.css.nature.CSSNature + org.eclipse.wst.jsdt.core.jsNature + org.eclipse.wst.common.project.facet.core.nature + org.tizen.web.project.builder.WebNature + org.tizen.web.privilege.nature.PrivilegeNature + org.tizen.web.editor.css.nature.CSSNature + + diff --git a/project/AUTHORS b/project/AUTHORS new file mode 100644 index 0000000..4350a55 --- /dev/null +++ b/project/AUTHORS @@ -0,0 +1,7 @@ +Dariusz Paziewski +Tomasz Lukawski +Pawel Sierszen +Piotr Wronski +Tomasz Paciorek +Artur Kobylinski +Grzegorz Sala diff --git a/project/LICENSE.Flora b/project/LICENSE.Flora new file mode 100644 index 0000000..4a0af40 --- /dev/null +++ b/project/LICENSE.Flora @@ -0,0 +1,206 @@ +Flora License + +Version 1.1, April, 2013 + +http://floralicense.org/license/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and +all other entities that control, are controlled by, or are +under common control with that entity. For the purposes of +this definition, "control" means (i) the power, direct or indirect, +to cause the direction or management of such entity, +whether by contract or otherwise, or (ii) ownership of fifty percent (50%) +or more of the outstanding shares, or (iii) beneficial ownership of +such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation source, +and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, +made available under the License, as indicated by a copyright notice +that is included in or attached to the work (an example is provided +in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial +revisions, annotations, elaborations, or other modifications represent, +as a whole, an original work of authorship. For the purposes of this License, +Derivative Works shall not include works that remain separable from, +or merely link (or bind by name) to the interfaces of, the Work and +Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original +version of the Work and any modifications or additions to that Work or +Derivative Works thereof, that is intentionally submitted to Licensor +for inclusion in the Work by the copyright owner or by an individual or +Legal Entity authorized to submit on behalf of the copyright owner. +For the purposes of this definition, "submitted" means any form of +electronic, verbal, or written communication sent to the Licensor or +its representatives, including but not limited to communication on +electronic mailing lists, source code control systems, and issue +tracking systems that are managed by, or on behalf of, the Licensor +for the purpose of discussing and improving the Work, but excluding +communication that is conspicuously marked or otherwise designated +in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +"Tizen Certified Platform" shall mean a software platform that complies +with the standards set forth in the Tizen Compliance Specification +and passes the Tizen Compliance Tests as defined from time to time +by the Tizen Technical Steering Group and certified by the Tizen +Association or its designated agent. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work +solely as incorporated into a Tizen Certified Platform, where such +license applies only to those patent claims licensable by such +Contributor that are necessarily infringed by their Contribution(s) +alone or by combination of their Contribution(s) with the Work solely +as incorporated into a Tizen Certified Platform to which such +Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim +in a lawsuit) alleging that the Work or a Contribution incorporated +within the Work constitutes direct or contributory patent infringement, +then any patent licenses granted to You under this License for that +Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof pursuant to the copyright license +above, in any medium, with or without modifications, and in Source or +Object form, provided that You meet the following conditions: + + 1. You must give any other recipients of the Work or Derivative Works + a copy of this License; and + 2. You must cause any modified files to carry prominent notices stating + that You changed the files; and + 3. You must retain, in the Source form of any Derivative Works that + You distribute, all copyright, patent, trademark, and attribution + notices from the Source form of the Work, excluding those notices + that do not pertain to any part of the Derivative Works; and + 4. If the Work includes a "NOTICE" text file as part of its distribution, + then any Derivative Works that You distribute must include a readable + copy of the attribution notices contained within such NOTICE file, + excluding those notices that do not pertain to any part of + the Derivative Works, in at least one of the following places: + within a NOTICE text file distributed as part of the Derivative Works; + within the Source form or documentation, if provided along with the + Derivative Works; or, within a display generated by the Derivative Works, + if and wherever such third-party notices normally appear. + The contents of the NOTICE file are for informational purposes only + and do not modify the License. You may add Your own attribution notices + within Derivative Works that You distribute, alongside or as an addendum + to the NOTICE text from the Work, provided that such additional attribution + notices cannot be construed as modifying the License. You may add Your own + copyright statement to Your modifications and may provide additional or + different license terms and conditions for use, reproduction, or + distribution of Your modifications, or for any such Derivative Works + as a whole, provided Your use, reproduction, and distribution of + the Work otherwise complies with the conditions stated in this License + and your own copyright statement or terms and conditions do not conflict + the conditions stated in the License including section 3. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Flora License to your work + +To apply the Flora License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Flora License, Version 1.1 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://floralicense.org/license/ + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/project/NOTICE b/project/NOTICE new file mode 100644 index 0000000..092bc04 --- /dev/null +++ b/project/NOTICE @@ -0,0 +1,4 @@ +Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved. +Except as noted, this software is licensed under Flora License, Version 1.1 +Please, see the LICENSE.Flora file for Flora License, Version 1.1 terms and conditions. + diff --git a/project/config.xml b/project/config.xml new file mode 100644 index 0000000..10ca79d --- /dev/null +++ b/project/config.xml @@ -0,0 +1,14 @@ + + + + + + Bluetooth Chat + + + + + + + + diff --git a/project/css/style.css b/project/css/style.css new file mode 100644 index 0000000..4e74a37 --- /dev/null +++ b/project/css/style.css @@ -0,0 +1,184 @@ +html, body { + overflow: hidden; +} + +.box { + margin: 0px; + padding: 0px; + display: table-cell; + vertical-align: middle; + height: inherit; + width: inherit; +} + +#start-header .ui-btn-back { + width: 34px; +} + +#chat-header .ui-btn-back { + width: 34px; +} + +#chat-header .ui-btn-footer-down { + top: 19px; + right: 0px; +} + +.ui-btn-start { + margin: 10px auto; + padding: 0; + width: 90%; + font-size: 26px; +} + +.ui-btn-start > span { + padding-top: 30px; + padding-bottom: 30px; +} + +/* +#keyboard-text { + width: 100%; + margin: 0px; + padding: 0px; + border: 0px; + border-radius: 0px; + overflow: hidden; + font-size: 24px; + outline: none; +} +*/ + +#keyboard-text { + width: 90%; + margin: 16px auto; +} + +#keyboard-content .ui-scrollview-view { + padding: 0px; + text-align: center; +} + +html { + background: white !important; +} + +#start-content { + background-color: white; +} + +#keyboard-content { + background-color: white; +} + +#keyboard-back-button { + position: absolute; + right: 50px; + bottom: 15px; + width: 40px; +} + +#ui-textArea { + position: absolute; + width: 100%; + height: 160px; + display: -webkit-box; + -webkit-box-orient: horizontal; +} + +#ui-textArea-text { + -webkit-box-flex: 1; +} + +#ui-textArea-button { + width: 100px; + height: 108px; +} + +#text { + background-color: #fff; + color: #555; + position: absolute; + left: 10px; + right: 110px; + top: 10px; + height: 140px; + margin: 0px; + border: 0px solid #000; + border-radius: 5px; + font-size: 22px; + overflow: auto; +} + +#ui-mySend { + float: left; + width: 85px; + margin-top: 10px; + font-size: 22px; +} + +#ui-myCounter { + float: left; + margin-top: 47px; +} + +#ui-myCounter p { + font-size: 20px; +} + +#chat-footer { + height: 164px; +} + +#start-monit { + text-align: center; + width: 100%; +} + +#start-monit a { + font-size: 1.4em; +} + +#chat-header { + min-height: 2.5em; +} + +#chat-header-type { + font-size: 0.5em; + position: absolute; + top: 3px; + left: 3px; + white-space: nowrap; + overflow: hidden !important; + text-overflow: ellipsis; + width: 90%; +} + +#chat-header-name { + font-size: 1em; + position: absolute; + top: 25px; + left: 3px; + white-space: nowrap; + overflow: hidden !important; + text-overflow: ellipsis; + width: 60%; +} + +.server-row-name { + white-space: nowrap; + overflow: hidden !important; + text-overflow: ellipsis; + width: 80%; +} + +#discovering { + right: 8px; + top: 8px; +} + +.focus {} + +.ui-btn-back { + visibility:hidden; +} diff --git a/project/icon.png b/project/icon.png new file mode 100644 index 0000000..5934757 Binary files /dev/null and b/project/icon.png differ diff --git a/project/index.html b/project/index.html new file mode 100644 index 0000000..974a9f5 --- /dev/null +++ b/project/index.html @@ -0,0 +1,36 @@ + + + + + + + + + Bluetooth chat + + + + + + + + + +
+
+

Bluetooth chat

+
+ +
+
+ + + + +
+
+
+ + \ No newline at end of file diff --git a/project/js/app.client.js b/project/js/app.client.js new file mode 100644 index 0000000..10dd388 --- /dev/null +++ b/project/js/app.client.js @@ -0,0 +1,112 @@ +/*jslint plusplus: true, sloppy: true, todo: true, vars: true, browser: true, devel: true, maxerr: 999 */ +/*global $, tizen, app, ClientModel */ +/** + * @class Client + */ + +function Client(adapter, serviceUUID) { + 'use strict'; + this.adapter = adapter; + this.serviceUUID = serviceUUID; + this.globalSocket = null; + this.init(); + this.discovering = false; + this.chatServerDevice = null; +} + +(function () { // strict mode wrapper + 'use strict'; + + Client.prototype = { + /** + * @type clientModel + */ + model: null, + + /** + * Initialisation function + */ + init: function Client_init() { + this.model = new ClientModel(this); + return this; + }, + + setDiscovering: function Client_setDiscovering(boolean) { + this.discovering = boolean; + app.ui.setDiscoveringProgress(boolean); + }, + + getDiscovering: function Client_getDiscovering() { + return this.discovering; + }, + + searchServer: function Client_searchServer() { + this.model.searchServer(); + }, + + addDeviceToList: function Client_addDeviceToList(device) { + app.ui.addDeviceToList(device); + }, + + stopServerSearching: function Client_stopServerSearching(address) { + if (address !== undefined) { + this.model.stopServerSearching(this.startBonding.bind(this, address, this.connectToService.bind(this))); + } else { + this.model.stopServerSearching(); + } + }, + + startBonding: function Client_startBonding(address, callback) { + this.model.startBonding(address, callback); + }, + + connectToService: function Client_connectToService(device) { + this.model.connectToService(device, this.serviceUUID, this.connectToServiceSuccess.bind(this, device), this.connectToServiceError.bind(this)); + }, + + connectToServiceSuccess: function Client_connectToServiceSuccess(device, socket) { + this.globalSocket = socket; + socket.onmessage = function () { + var data, recvmsg = '', i, len, messageObj; + data = socket.readData(); + len = data.length; + for (i = 0; i < len; i += 1) { + recvmsg += String.fromCharCode(data[i]); + } + messageObj = JSON.parse(recvmsg); + app.ui.displayReceivedMessage(messageObj.name, messageObj.text, messageObj.ping, messageObj.bye); + }; + socket.onerror = function (e) { + console.error('Client onerror'); + socket.close(); + }; + socket.onclose = function () { + this.globalSocket = null; + app.setConnection(false); + }; + app.setConnection(true); + app.ui.showChatPage(device.name); + this.sendPing(); + }, + + connectToServiceError: function Client_connectToServiceError(error) { + console.error('Client_connectToServiceError: ' + error.message); + }, + + sendPing: function Client_sendPing() { + this.model.sendPing(this.adapter.name, this.globalSocket); + }, + + sendMessage: function Client_sendMessage(message, callback) { + this.model.sendMessage(this.adapter.name, this.globalSocket, message, callback); + }, + + sendBye: function Client_sendBye() { + this.model.sendBye(this.adapter.name, this.globalSocket); + }, + + destroyBonding: function Client_destroyBonding() { + this.model.destroyBonding(this.chatServerDevice, app.restartBluetooth.bind(app), app.ui.showStartButtons); + } + }; +}()); diff --git a/project/js/app.client.model.js b/project/js/app.client.model.js new file mode 100644 index 0000000..8b5960e --- /dev/null +++ b/project/js/app.client.model.js @@ -0,0 +1,141 @@ +/*jslint plusplus: true, sloppy: true, todo: true, vars: true, browser: true, devel: true, maxerr: 999 */ +/*global $, tizen, app */ +/** + * @class Model + */ +function ClientModel(parent) { + 'use strict'; + this.client = parent; + this.init(); +} + +(function () { // strict mode wrapper + 'use strict'; + ClientModel.prototype = { + + /** + * API module initialisation + */ + init: function ClientModel_init() {}, + + searchServer: function ClientModel_searchServer() { + var discoverDevicesSuccessCallback = { + onstarted: function () { + this.client.setDiscovering(true); + }.bind(this), + ondevicefound: function (device) { + this.client.addDeviceToList(device); + }.bind(this), + ondevicedisappeared: function (address) {}, + onfinished: function (devices) { + this.client.setDiscovering(false); + }.bind(this) + }; + + this.client.adapter.discoverDevices(discoverDevicesSuccessCallback, function (e) { this.client.setDiscovering(false); }); + }, + + stopServerSearching: function ClientModel_stopServerSearching(callback) { + if (this.client.getDiscovering()) { + this.client.adapter.stopDiscovery(function () { + this.client.setDiscovering(false); + if (typeof callback === 'function') { + callback(); + } + }.bind(this), function (e) { + console.error("Error while stopDiscovery:" + e.message); + }); + } else if (typeof callback === 'function') { + callback(); + } + }, + + startBonding: function ClientModel_startBonding(address, callback) { + this.client.adapter.createBonding(address, function (device) { callback(device); }, function (error) { console.error('bondError: ' + error.message); }); + }, + + connectToService: function ClientModel_connectToService(device, serviceUUID, successCallback, errorCallback) { + this.client.chatServerDevice = device; + try { + device.connectToServiceByUUID(serviceUUID, successCallback, errorCallback); + } catch (error) { + console.error('connectToServiceByUUID ERROR: ' + error.message); + } + }, + + sendPing: function ClientModel_sendPing(name, socket) { + var sendTextMsg, messageObj, messageObjToString, i, len; + sendTextMsg = []; + messageObj = {name: encodeURIComponent(name), text: '', ping: true, bye: false}; + messageObjToString = JSON.stringify(messageObj); + len = messageObjToString.length; + + for (i = 0; i < len; i += 1) { + sendTextMsg[i] = messageObjToString.charCodeAt(i); + } + try { + if (socket !== null && socket.state === "OPEN") { + socket.writeData(sendTextMsg); + } + } catch (error) { + console.error('sendPing: ' + error.message); + } + }, + + sendMessage: function ClientModel_sendMessage(name, socket, message, callback) { + var sendTextMsg = [], messageObj, messageObjToString, i, len; + name = encodeURIComponent(name); + message = encodeURIComponent(message); + messageObj = {name: name, text: message, ping: false, bye: false}; + messageObjToString = JSON.stringify(messageObj); + len = messageObjToString.length; + for (i = 0; i < len; i += 1) { + sendTextMsg[i] = messageObjToString.charCodeAt(i); + } + try { + if (socket !== null && socket.state === "OPEN") { + socket.writeData(sendTextMsg); + callback(message); + } + } catch (error) { + console.error('sendMessage: ' + error.message); + } + }, + + sendBye: function ClientModel_sendBye(name, socket) { + var sendTextMsg = [], messageObj, messageObjToString, i, len; + name = encodeURIComponent(name); + messageObj = {name: name, text: '', ping: false, bye: true}; + messageObjToString = JSON.stringify(messageObj); + len = messageObjToString.length; + for (i = 0; i < len; i += 1) { + sendTextMsg[i] = messageObjToString.charCodeAt(i); + } + try { + if (socket !== null && socket.state === "OPEN") { + socket.writeData(sendTextMsg); + } + } catch (error) { + console.error('sendBye: ' + error.message); + } + }, + + destroyBonding: function ClientModel_destroyBonding(device, restartCallback, showStartButtonsCallback) { + if (device !== null) { + if (device.isBonded) { + this.client.adapter.destroyBonding(device.address, function () { + device = null; + restartCallback(); + }, function (error) { console.error('ClientModel_destroyBonding: ' + error); }); + } else { + device = null; + restartCallback(); + } + } else { + this.stopServerSearching(); + showStartButtonsCallback(); + } + } + + }; +}()); diff --git a/project/js/app.config.js b/project/js/app.config.js new file mode 100644 index 0000000..c40c601 --- /dev/null +++ b/project/js/app.config.js @@ -0,0 +1,29 @@ +/*jslint plusplus: true, sloppy: true, todo: true, vars: true, browser: true, devel: true, maxerr: 999 */ +/*global $, tizen, app */ +/** + * @class Config + */ +function Config() { + 'use strict'; +} + +(function () { // strict mode wrapper + 'use strict'; + Config.prototype = { + + properties: { + 'templateDir': 'templates', + 'templateExtension': '.tpl' + }, + + /** + * Returns config value + */ + get: function (value, defaultValue) { + if (this.properties.hasOwnProperty(value)) { + return this.properties[value]; + } + return defaultValue; + } + }; +}()); diff --git a/project/js/app.helpers.js b/project/js/app.helpers.js new file mode 100644 index 0000000..0437bc6 --- /dev/null +++ b/project/js/app.helpers.js @@ -0,0 +1,19 @@ +/*jslint plusplus: true, sloppy: true, todo: true, vars: true, browser: true, devel: true, maxerr: 999 */ +/*global $, tizen, app */ +/** + * @class Helpers + */ +function Helpers() { + 'use strict'; +} + +(function () { // strict mode wrapper + 'use strict'; + Helpers.prototype = { + + checkStringLength: function Helpers_checkStringLength(value) { + return value.length > 0 ? true : false; + } + + }; +}()); diff --git a/project/js/app.js b/project/js/app.js new file mode 100644 index 0000000..c02479e --- /dev/null +++ b/project/js/app.js @@ -0,0 +1,213 @@ +/*global $, tizen, app, Config, Helpers, Model, Ui, Server, Client */ +var App = null; + +(function () { // strict mode wrapper + 'use strict'; + + /** + * Creates a new application object + * + * @class Application + */ + App = function App() {}; + + App.prototype = { + /** + * @type Array + */ + requires: ['js/app.config.js', + 'js/app.helpers.js', + 'js/app.model.js', + 'js/app.ui.js', + 'js/app.ui.templateManager.js', + 'js/app.ui.events.js', + 'js/app.client.js', + 'js/app.client.model.js', + 'js/app.server.js', + 'js/app.server.model.js' + ], + + /** + * @type Model + */ + model: null, + + /** + * @type Ui + */ + ui: null, + + /** + * @type Config + */ + config: null, + + /** + * @type Helpers + */ + helpers: null, + + /** + * @type Client + */ + client: null, + + /** + * @type Server + */ + server: null, + + /** + * @type String + */ + currentName: '', + + /** + * @type Boolean + */ + doNotSendBye: false, + + /** + * @type Boolean + */ + connection: false, + + /** + * Initialisation function + */ + init: function App_init() { + this.config = new Config(); + this.helpers = new Helpers(); + this.model = new Model(); + this.ui = new Ui(this.initModel.bind(this)); + }, + + initModel: function App_initModel() { + this.model.init(this.checkPowerState.bind(this)); + }, + + /** + * exit application action + */ + exit: function App_exit() { + tizen.application.getCurrentApplication().exit(); + }, + + isConnection: function App_isConnection() { + return this.connection; + }, + + setConnection: function App_setConnection(bool) { + this.connection = bool; + }, + + getDoNotSendBye: function App_getDoNotSendBye() { + return this.doNotSendBye; + }, + + setDoNotSendBye: function App_setDoNotSendBye(bool) { + this.doNotSendBye = bool; + }, + + getCurrentName: function App_getCurrentName() { + return this.currentName; + }, + + getApplicationMode: function App_getApplicationMode() { + var mode = 'start'; + if (this.client !== null) { + mode = 'client'; + } else if (this.server !== null) { + mode = 'server'; + } + return mode; + }, + + resetApplicationMode: function App_resetApplicationMode() { + this.client = null; + this.server = null; + }, + + checkPowerState: function App_checkPowerState() { + this.ui.setContentStartAttributes( + this.model.checkPowerState.bind( + this.model, + this.ui.showPowerOnButton, + this.ui.showStartButtons + ) + ); + }, + + powerOn: function App_powerOn() { + this.model.powerOn(this.ui.showStartButtons); + }, + + powerOff: function App_powerOff() { + this.model.powerOff(this.exit); + }, + + restartBluetooth: function App_restartBluetooth() { + this.model.restartBluetooth(this.powerOn.bind(this)); + }, + + startServer: function App_startServer() { + this.server = new Server(this.model.adapter, this.model.serviceUUID); + this.showKeyboardPage(); + }, + + startClient: function App_startClient() { + this.client = new Client(this.model.adapter, this.model.serviceUUID); + this.showKeyboardPage(); + }, + + showKeyboardPage: function App_showKeyboardPage() { + this.ui.showKeyboardPage(); + }, + + setUserName: function App_setUserName(value) { + this.currentName = value; + }, + + setAdapterName: function App_setAdapterName() { + var changeName = false, mode = this.getApplicationMode(); + if (this.model.adapter.name !== this.currentName) { + changeName = true; + } + if (mode === 'server') { + this.model.setAdapterName(changeName, this.server.registerServer.bind(this.server)); + } else if (mode === 'client') { + this.model.setAdapterName(changeName, this.client.searchServer.bind(this.client)); + } + }, + + isBluetoothVisible: function App_isBluetoothVisible() { + return this.model.adapter.visible; + }, + + clearListOfServers: function App_clearListOfServers() { + this.ui.clearListOfServers(); + }, + + displaySentMessage: function App_displaySentMessage(message) { + this.ui.displaySentMessage(message); + }, + + sendMessage: function App_sendMessage(message) { + var mode = this.getApplicationMode(); + if (mode === 'server') { + this.server.sendMessage(message, this.displaySentMessage.bind(this)); + } else if (mode === 'client') { + this.client.sendMessage(message, this.displaySentMessage.bind(this)); + } + }, + + sendBye: function App_sendBye() { + var mode = this.getApplicationMode(); + if (mode === 'server') { + this.server.sendBye(); + } else if (mode === 'client') { + this.client.sendBye(); + } + } + }; +}()); diff --git a/project/js/app.model.js b/project/js/app.model.js new file mode 100644 index 0000000..9cf77a4 --- /dev/null +++ b/project/js/app.model.js @@ -0,0 +1,85 @@ +/*jslint plusplus: true, sloppy: true, todo: true, vars: true, browser: true, devel: true, maxerr: 999 */ +/*global $, tizen, app */ +/** + * @class Model + */ +function Model() { + 'use strict'; + this.adapter = null; + this.serviceUUID = '5BCE9431-6C75-32AB-AFE0-2EC108A30860'; +} + +(function () { // strict mode wrapper + 'use strict'; + Model.prototype = { + + /** + * API module initialisation + */ + init: function Model_init(callback) { + try { + console.log('getDefaultAdapter'); + var time = new Date().getTime(); + this.adapter = tizen.bluetooth.getDefaultAdapter(); + console.log('getDefaultAdapter OK: ' + (new Date().getTime() - time) + ' milliseconds.'); + callback(); + } catch (error) { + app.ui.showMessagePopup('Problem with bluetooth device. Application can\'t work properly: ' + error.message); + tizen.application.getCurrentApplication().exit(); + } + }, + + checkPowerState: function Model_checkPowerState(showPowerOnButtonCallback, showStartButtonsCallback) { + if (!this.adapter.powered) { + showPowerOnButtonCallback(); + } else { + showStartButtonsCallback(); + } + }, + + powerOn: function Model_powerOn(callback) { + if (!this.adapter.powered) { + try { + this.adapter.setPowered(true, + function () { + setTimeout(function () { callback(); }, 500); + } + ); + } catch (error) { + app.ui.showMessagePopup(error.message); + app.ui.showPowerOnButton(); + } + } else { + callback(); + } + }, + + powerOff: function Model_powerOff(callback) { + var app = tizen.application.getCurrentApplication(); + if (this.adapter.powered) { + this.adapter.setPowered(false, function () { callback(); }, function () { callback(); }); + } else { + callback(); + } + }, + + restartBluetooth: function Model_restartBluetooth(callback) { + if (this.adapter.powered) { + this.adapter.setPowered(false, function () { setTimeout(function () { callback(); }, 500); }, function () {}); + } else { + callback(); + } + }, + + setAdapterName: function Model_setAdapterName(changeName, callback) { + if (changeName) { + this.adapter.setName(app.currentName, function () { + callback(); + }, function () { + }); + } else { + callback(); + } + } + }; +}()); diff --git a/project/js/app.server.js b/project/js/app.server.js new file mode 100644 index 0000000..6cf5c5a --- /dev/null +++ b/project/js/app.server.js @@ -0,0 +1,93 @@ +/*global $, tizen, app, ServerModel */ +/** + * @class Server + */ + +function Server(adapter, serviceUUID) { + 'use strict'; + this.adapter = adapter; + this.serviceUUID = serviceUUID; + this.numberOfClients = 0; + this.globalRecordHandler = null; + this.globalSocket = null; + this.init(); +} + +(function () { // strict mode wrapper + 'use strict'; + + Server.prototype = { + /** + * @type clientModel + */ + model: null, + + /** + * Initialisation function + */ + init: function Server_init() { + this.model = new ServerModel(this); + return this; + }, + + getNumberOfClients: function Server_getNumberOfClients() { + return this.numberOfClients; + }, + + registerServer: function Server_registerServer() { + this.model.registerServer(this.adapter, this.serviceUUID, this.registerServerSuccess.bind(this)); + }, + + registerServerSuccess: function Server_registerServerSuccess(recordHandler) { + this.globalRecordHandler = recordHandler; + recordHandler.onconnect = function (socket) { + this.numberOfClients += 1; + this.globalSocket = socket; + socket.onmessage = function () { + var data, recvmsg = '', i, len, messageObj; + data = socket.readData(); + len = data.length; + for (i = 0; i < len; i += 1) { + recvmsg += String.fromCharCode(data[i]); + } + messageObj = JSON.parse(recvmsg); + app.ui.displayReceivedMessage(messageObj.name, messageObj.text, messageObj.ping, messageObj.bye); + }; + socket.onerror = function (e) { + console.error('Server onerror'); + socket.close(); + }; + socket.onclose = function () { + this.globalSocket = null; + app.setConnection(false); + }; + app.setConnection(true); + }.bind(this); + app.ui.showChatPage(); + }, + + unregisterChatServer: function Server_unregisterChatServer() { + this.model.unregisterChatServer(this.globalRecordHandler, this.unregisterChatServerSuccess.bind(this), this.unregisterChatServerError.bind(this), app.ui.showStartButtons); + }, + + unregisterChatServerSuccess: function Server_unregisterChatServerSuccess() { + this.globalRecordHandler = null; + this.numberOfClients = 0; + app.restartBluetooth(); + }, + + unregisterChatServerError: function Server_unregisterChatServerError() { + console.error('Server_unregisterChatServerError'); + this.numberOfClients = 0; + app.restartBluetooth(); + }, + + sendMessage: function Server_sendMessage(message, callback) { + this.model.sendMessage(this.adapter.name, this.globalSocket, message, callback); + }, + + sendBye: function Server_sendBye() { + this.model.sendBye(this.adapter.name, this.globalSocket); + } + }; +}()); diff --git a/project/js/app.server.model.js b/project/js/app.server.model.js new file mode 100644 index 0000000..771353e --- /dev/null +++ b/project/js/app.server.model.js @@ -0,0 +1,81 @@ +/*global $, tizen, app */ +/** + * @class Model + */ +function ServerModel(parent) { + 'use strict'; + this.server = parent; + this.init(); +} + +(function () { // strict mode wrapper + 'use strict'; + ServerModel.prototype = { + + /** + * API module initialisation + */ + init: function ServerModel_init() {}, + + registerServer: function ServerModel_registerServer(adapter, serviceUUID, callback) { + if (this.server.getNumberOfClients() === 0) { + try { + adapter.registerRFCOMMServiceByUUID(serviceUUID, 'Chat service', callback, function (error) { console.error(error.message); }); + } catch (error) { + console.error(error.message); + } + } + }, + + unregisterChatServer: function ServerModel_unregisterChatServer(globalRecordHandler, successCallback, errorCallback, showButtonsCallback) { + try { + if (globalRecordHandler !== null) { + globalRecordHandler.unregister(successCallback, errorCallback); + } else { + showButtonsCallback(); + } + } catch (error) { + errorCallback(); + } + }, + + sendMessage: function ServerModel_sendMessage(name, socket, message, callback) { + var sendTextMsg = [], messageObj, messageObjToString, i, len; + name = encodeURIComponent(name); + message = encodeURIComponent(message); + messageObj = {name: name, text: message, ping: false, bye: false}; + messageObjToString = JSON.stringify(messageObj); + len = messageObjToString.length; + for (i = 0; i < len; i += 1) { + sendTextMsg[i] = messageObjToString.charCodeAt(i); + } + try { + if (socket !== null && socket.state === "OPEN") { + socket.writeData(sendTextMsg); + callback(message); + } + } catch (error) { + console.error('sendMessage: ' + error.message); + } + }, + + sendBye: function ServerModel_sendBye(name, socket) { + var sendTextMsg = [], messageObj, messageObjToString, i, len; + name = encodeURIComponent(name); + messageObj = {name: name, text: '', ping: false, bye: true}; + messageObjToString = JSON.stringify(messageObj); + len = messageObjToString.length; + for (i = 0; i < len; i += 1) { + sendTextMsg[i] = messageObjToString.charCodeAt(i); + } + try { + if (socket !== null && socket.state === "OPEN") { + socket.writeData(sendTextMsg); + } + } catch (error) { + console.error('sendBye: ' + error.message); + } + } + + }; +}()); diff --git a/project/js/app.ui.events.js b/project/js/app.ui.events.js new file mode 100644 index 0000000..f9ae99f --- /dev/null +++ b/project/js/app.ui.events.js @@ -0,0 +1,214 @@ +/*global $, tizen, app */ +/** + * @class UiEvents + */ +function UiEvents(parent) { + 'use strict'; + this.ui = parent; +} + +(function () { // strict mode wrapper + 'use strict'; + UiEvents.prototype = { + + /** + * Initialization + */ + init: function UiEvents_init() { + this.addPageEvents(); + }, + + /** + * Bind events to pages + */ + addPageEvents: function UiEvents_addPageEvents() { + var self = this; + + $('#start-header .ui-btn-back').on('click', function (event) { + event.preventDefault(); + event.stopPropagation(); + app.powerOff(); + }); + + $('#choose-footer').on('click', '.ui-btn-back', function (event) { + event.preventDefault(); + event.stopPropagation(); + app.setDoNotSendBye(true); + $.mobile.changePage('#start'); + }); + + $('#chat-header').on('click', '.ui-btn-back', function (event) { + event.preventDefault(); + event.stopPropagation(); + if (!app.isConnection()) { + app.setDoNotSendBye(true); + } + $.mobile.changePage('#start'); + }); + + $('#chat-header').on('click', '.ui-btn-back', function (event) { + event.preventDefault(); + }); + + $('#start').on('pagebeforeshow', function () { + self.ui.hideStartButtons(); + self.ui.clearChatDialog(); + if (!app.getDoNotSendBye()) { + app.sendBye(); + } else { + app.setDoNotSendBye(false); + } + if (app.getApplicationMode() === 'server') { + app.server.unregisterChatServer(); + } else if (app.getApplicationMode() === 'client') { + app.client.destroyBonding(); + } + }); + + $('#turnOnButton').on('click', function (event) { + self.ui.hideStartButtons(); + app.powerOn(); + }); + + $('#serverButton').on('click', function (event) { + app.resetApplicationMode(); + app.startServer(); + }); + + $('#clientButton').on('click', function (event) { + app.resetApplicationMode(); + app.startClient(); + }); + + $('#keyboard').on('pagebeforeshow', function () { + $('#keyboard-text').val('').attr('placeholder', 'Type ' + app.getApplicationMode() + ' name'); + $('#keyboard-header > h1').html('Type ' + app.getApplicationMode() + ' name'); + }); + + $('#keyboard').on('pageshow', function () { + setTimeout(function () { $('#keyboard-text').focus(); }, 500); + }); + + $('#keyboard-ok-button').on('click', function (event) { + event.preventDefault(); + var value = $('#keyboard-text').val(), mode; + if (value.length !== 0) { + app.setUserName(value); + mode = app.getApplicationMode(); + if (mode === 'server') { + app.setAdapterName(); + } else if (mode === 'client') { + $.mobile.changePage('#choose'); + } + } + }); + + $('#choose').on('pagebeforeshow', function () { + app.setAdapterName(); + }); + + $('#choose').on('pagehide', function () { + app.clearListOfServers(); + app.client.stopServerSearching(); + }); + + $('#choose-content').on('click', 'ul.ui-listview li', function () { + app.client.stopServerSearching($(this).attr('address')); + }); + + $('#chat').on('pagebeforeshow', function () { + $('#chat-header-type').html(app.getApplicationMode()); + $('#chat-header-name').html(app.getCurrentName()); + if ($(this).data('serverName') !== undefined) { + $('#chat-header-type').append(' - connected to ' + $(this).data('serverName')); + $(this).removeData('serverName'); + } + self.ui.checkSendButtonState(); + }); + + $('#chat').on('pageshow', function () { + if (app.getApplicationMode() === 'server' && !app.isBluetoothVisible()) { + setTimeout(function () { app.ui.showMessagePopup('Please make your Bluetooth visible in Settings.'); }, 500); + } + }); + + $('#chat').on('pagehide', function () { + $('#text').val(''); + app.setConnection(false); + }); + + $('#text').on('input', function () { + self.ui.checkSendButtonState(); + }); + + $('#text').on('focus', function () { + var content = $('#chat-content'); + if (self.ui.scrolltimeout !== null) { + clearTimeout(self.ui.scrolltimeout); + } + self.ui.scrolltimeout = setTimeout(function () { + self.ui.scrolltimeout = null; + self.ui.scrollToBottom(content); + }, 1000); + }); + + $('#text').on('blur', function () { + var content = $('#chat-content'); + if (self.ui.scrolltimeout !== null) { + clearTimeout(self.ui.scrolltimeout); + } + self.ui.scrolltimeout = setTimeout(function () { + self.ui.scrolltimeout = null; + self.ui.scrollToBottom(content); + }, 700); + }); + + $('#ui-mySend').on('click', function (event) { + event.stopPropagation(); + var message = $('#text').val(); + $('#text').val(''); + self.ui.disableSendButton(); + app.sendMessage(message); + }); + + $('body').on('click', '#byeOK', function () { + self.ui.hideByePopup(); + $('#keyboard-back-button').trigger('click'); + }); + + $('body').on('touchstart', '#byePopup-screen', function () { + $('#byeOK').trigger('click'); + }); + + $('body').on('click', '#messageOK', function () { + self.ui.hideMessagePopup(); + }); + + $('body').on('touchstart', '#messagePopup-screen', function () { + $('#messageOK').trigger('click'); + }); + + $('#chat-content').on('touchstart', function () { + if (self.ui.scrolltimeout !== null) { + clearTimeout(self.ui.scrolltimeout); + self.ui.scrolltimeout = null; + } + }); + + window.addEventListener('tizenhwkey', function(e) { + if (e.keyName == "back") { + event.preventDefault(); + app.setDoNotSendBye(true); + if ($.mobile.activePage.attr('id') === 'start') { + tizen.application.getCurrentApplication().exit(); + } else if ($.mobile.activePage.attr('id') === 'chat') { + $.mobile.changePage('#start'); + } else { + history.back(); + } + } + }); + + } + }; +}()); diff --git a/project/js/app.ui.js b/project/js/app.ui.js new file mode 100644 index 0000000..9bd9d99 --- /dev/null +++ b/project/js/app.ui.js @@ -0,0 +1,245 @@ +/*global $, tizen, app, UiEvents, TemplateManager, document, window, setTimeout */ +/** + * @class Ui + */ + +function Ui(callback) { + 'use strict'; + this.init(callback); +} + +(function () { // strict mode wrapper + 'use strict'; + Ui.prototype = { + + templateManager: null, + + /** + * UI object for UI events + */ + uiEvents: null, + + /** + * Timeout for chat scroll. + */ + scrolltimeout: null, + + /** + * UI module initialisation + */ + init: function Ui_init(callback) { + this.templateManager = new TemplateManager(); + this.uiEvents = new UiEvents(this); + $.mobile.tizen.disableSelection(document); + + $(document).ready(this.domInit.bind(this, callback)); + }, + + /** + * When DOM is ready, initialise it (bind events) + */ + domInit: function Ui_domInit(callback) { + var templates = [ + 'keyboard_page', + 'chat_page', + 'choose_page', + 'server_row', + 'left_bubble', + 'right_bubble', + 'bye_popup', + 'message_popup' + ]; + + this.templateManager.loadToCache(templates, this.initPages.bind(this, callback)); + }, + + initPages: function Ui_initPages(callback) { + var pages = [], body = $('body'); + + body.append(this.templateManager.get('bye_popup')) + .append(this.templateManager.get('message_popup')) + .trigger('create'); + + pages.push(this.templateManager.get('keyboard_page')); + pages.push(this.templateManager.get('chat_page')); + pages.push(this.templateManager.get('choose_page')); + body.append(pages.join('')); + + this.uiEvents.init(); + this.fixContentHeight(); + callback(); + }, + + setContentStartAttributes: function Ui_setContentStartAttributes(callback) { + var contentStart, contentStartHeight; + contentStart = $('#start-content'); + if (contentStart.height() > $(window).height()) { + contentStartHeight = $(window).height() - $('#start-header').height() + - parseInt(contentStart.css('padding-top'), 10) - parseInt(contentStart.css('padding-bottom'), 10); + } else { + contentStartHeight = contentStart.height(); + } + setTimeout(function () { // workaround (setTimeout with 0 delay) + contentStart + .css('height', contentStartHeight + 'px') + .css('min-height', 'auto') + .css('width', contentStart.width() + 'px'); + $('#start').css('min-height', 'auto'); + callback(); + }, 0); + }, + + showChatPage: function Ui_showChatPage(serverName) { + if (serverName !== undefined) { + $('#chat').data('serverName', serverName); + } + $.mobile.changePage('#chat'); + }, + + showKeyboardPage: function Ui_showKeyboardPage() { + $.mobile.changePage('#keyboard'); + }, + + clearChatDialog: function Ui_clearChatDialog() { + $('#chat-content .ui-listview').empty(); + }, + + showPowerOnButton: function Ui_showPowerOnButton() { + $('#start-monit').hide(); + $('#serverButton').hide(); + $('#clientButton').hide(); + $('#turnOnButton').show(); + }, + + showStartButtons: function Ui_showStartButtons() { + $('#start-monit').hide(); + $('#turnOnButton').hide(); + $('#serverButton').show(); + $('#clientButton').show(); + }, + + hideStartButtons: function Ui_hideStartButtons() { + $('#serverButton').hide(); + $('#clientButton').hide(); + $('#turnOnButton').hide(); + $('#start-monit').show(); + }, + + addDeviceToList: function Ui_addDeviceToList(device) { + var listElement, address, sub2, ul = $('#choose-content ul.ui-listview'); + + listElement = this.templateManager.get('server_row', { + 'deviceAddress': device.address, + 'deviceName': device.name + }); + + ul.append(listElement); + ul.listview('refresh'); + }, + + clearListOfServers: function Ui_clearListOfServers() { + $('#choose-content ul.ui-listview').empty(); + }, + + showByePopup: function Ui_showByePopup(name) { + var mode = app.getApplicationMode(), message = $('#byeMessage'); + if (mode === 'server') { + message.html('Client name "' + name + '" is unavailable.\nYour bluetooth device will be automatically restarted.'); + } else if (mode === 'client') { + message.html('Server name "' + name + '" is unavailable.\nYour bluetooth device will be automatically restarted.'); + } + $('#byePopup').popup('open', {'positionTo': 'window'}); + }, + + hideByePopup: function Ui_hideByePopup() { + $('#byePopup').popup('close'); + }, + + showMessagePopup: function Ui_showMessagePopup(message) { + var messagePopup = $('#messagePopup'); + messagePopup.find('.ui-popup-text').text(message); + messagePopup.popup('open', {'positionTo': 'window'}); + }, + + hideMessagePopup: function Ui_hideMessagePopup() { + $('#messagePopup').popup('close'); + }, + + displayReceivedMessage: function Ui_displayReceivedMessage(name, text, ping, bye) { + var listElement, span, ul; + text = decodeURIComponent(text); + name = decodeURIComponent(name); + if (bye) { + this.showByePopup(name); + } else if (ping) { + app.setConnection(true); + $('#chat-header-type').append(' - connected with ' + name); + this.checkSendButtonState(); + } else { + listElement = this.templateManager.get('left_bubble', { + 'text': text + }); + ul = $('#chat-content > .ui-scrollview-view > ul'); + ul.append(listElement); + ul.listview('refresh'); + } + }, + + enableSendButton: function Ui_enableSendButton() { + $('#ui-mySend') + .css({'pointer-events': 'auto', 'color': '#000'}) + .removeClass('ui-disabled'); + }, + + disableSendButton: function Ui_disableSendButton() { + setTimeout( function() { + $('#ui-mySend') + .css({'pointer-events': 'none', 'color': '#bbb'}) + .addClass('ui-disabled'); + }, + 0 + ); + }, + + checkSendButtonState: function Ui_checkSendButtonState() { + if (app.helpers.checkStringLength($('#text').val().trim()) && app.isConnection()) { + this.enableSendButton(); + } else { + this.disableSendButton(); + } + }, + + scrollToBottom: function Ui_scrollToBottom(element) { + var bottom = element.children().first().outerHeight(true) - element.height(); + element.scrollview('scrollTo', 0, -Math.max(0, bottom), 0); + }, + + displaySentMessage: function Ui_displaySentMessage(message) { + var listElement, span, ul, content, self = this; + message = decodeURIComponent(message); + listElement = this.templateManager.get('right_bubble', { + 'text': message + }); + content = $('#chat-content'); + ul = content.find('ul'); + ul.append(listElement); + ul.listview('refresh'); + this.checkSendButtonState(); + this.scrolltimeout = setTimeout(function () { + self.scrolltimeout = null; + self.scrollToBottom(content); + }, 700); + }, + + setDiscoveringProgress: function Ui_setDiscoveringProgress(boolean) { + $('#discovering').progress('hide', !boolean).progress('running', boolean); + }, + + fixContentHeight: function Ui_fixContentHeight() { + var contentHeight = screen.availHeight - $('div[data-role="header"]').outerHeight() - $('div[data-role="footer"]').outerHeight(); + $('div[data-role="content"]').css('height', contentHeight); + } + + }; + +}()); diff --git a/project/js/app.ui.templateManager.js b/project/js/app.ui.templateManager.js new file mode 100644 index 0000000..68c6678 --- /dev/null +++ b/project/js/app.ui.templateManager.js @@ -0,0 +1,111 @@ +/*global tizen, $, app */ +/** + * @class TemplateManager + */ +function TemplateManager() { + 'use strict'; + this.init(); +} + +(function () { // strict mode wrapper + 'use strict'; + TemplateManager.prototype = { + + /** + * Template cache + */ + cache: {}, + + /** + * UI module initialisation + */ + init: function init() { + }, + + /** + * Returns template html (from cache) + * @param {string} tplName + * @param {string} tplParams + */ + get: function TemplateManager_get(tplName, tplParams) { + if (this.cache[tplName] !== undefined) { + return this.getCompleted(this.cache[tplName], tplParams); + } + return ''; + }, + + /** + * Load templates to cache + * @param {string} tplNames + * @param {function} onSuccess + */ + loadToCache: function TemplateManager_loadToCache(tplNames, onSuccess) { + var self = this, + cachedTemplates = 0, + tplName, + tplPath; + + if ($.isArray(tplNames)) { + + // for each template + $.each(tplNames, function (index, fileName) { + + // cache template html + if (self.cache[fileName] === undefined) { + tplName = [fileName, app.config.get('templateExtension')].join(''); + tplPath = [app.config.get('templateDir'), tplName].join('/'); + + $.ajax({ + url: tplPath, + cache: true, + dataType: 'html', + async: true, + success: function (data) { + // increase counter + cachedTemplates += 1; + + // save to cache + self.cache[fileName] = data; + + // if all templates are cached launch callback + if (cachedTemplates >= tplNames.length && typeof onSuccess === 'function') { + onSuccess(); + } + }, + error: function (jqXHR, textStatus, errorThrown) { + console.error('templateManagerError: ' + errorThrown); + } + }); + } else { + // template is already cached + cachedTemplates += 1; + // if all templates are cached launch callback + if (cachedTemplates >= tplNames.length && typeof onSuccess === 'function') { + onSuccess(); + } + } + }); + + } + }, + + /** + * Returns template completed by specified params + * @param {string} tplHtml + * @param {string} tplParams + */ + getCompleted: function TemplateManager_getCompleted(tplHtml, tplParams) { + var tplParam, replaceRegExp; + + for (tplParam in tplParams) { + if (tplParams.hasOwnProperty(tplParam)) { + replaceRegExp = new RegExp(['%', tplParam, '%'].join(''), 'g'); + tplHtml = tplHtml.replace(replaceRegExp, tplParams[tplParam]); + } + } + + return tplHtml; + } + }; + +}()); \ No newline at end of file diff --git a/project/js/main.js b/project/js/main.js new file mode 100644 index 0000000..1a2870e --- /dev/null +++ b/project/js/main.js @@ -0,0 +1,63 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*global $, tizen, App */ +var app = null; + +(function () { // strict mode wrapper + 'use strict'; + + ({ + /** + * Loader init - load the App constructor + */ + init: function init() { + var self = this; + $.getScript('js/app.js') + .done(function () { + app = new App(); + self.loadLibs(); + }) + .fail(this.onGetScriptError); + }, + + /** + * Load dependencies + */ + loadLibs: function loadLibs() { + var loadedLibs = 0; + if ($.isArray(app.requires)) { + $.each(app.requires, function (index, filename) { + $.getScript(filename) + .done(function () { + loadedLibs += 1; + if (loadedLibs >= app.requires.length) { + app.init(); + } + }) + .fail(this.onGetScriptError); + }); + } + }, + + /** + * Handle ajax errors + */ + onGetScriptError: function onGetScriptError(e, jqxhr, setting, exception) { + console.error('An error occurred: ' + e.message); + } + }).init(); // run the loader +}()); diff --git a/project/templates/bye_popup.tpl b/project/templates/bye_popup.tpl new file mode 100644 index 0000000..69bc1bf --- /dev/null +++ b/project/templates/bye_popup.tpl @@ -0,0 +1,6 @@ +
+
+
+ Ok +
+
diff --git a/project/templates/chat_page.tpl b/project/templates/chat_page.tpl new file mode 100644 index 0000000..57ae3aa --- /dev/null +++ b/project/templates/chat_page.tpl @@ -0,0 +1,20 @@ +
+
+

+
+ +
+
    +
    + + +
    \ No newline at end of file diff --git a/project/templates/choose_page.tpl b/project/templates/choose_page.tpl new file mode 100644 index 0000000..102d358 --- /dev/null +++ b/project/templates/choose_page.tpl @@ -0,0 +1,10 @@ +
    +
    +

    Choose your server

    +
    +
    + +
    +
      +
      +
      \ No newline at end of file diff --git a/project/templates/keyboard_page.tpl b/project/templates/keyboard_page.tpl new file mode 100644 index 0000000..1722089 --- /dev/null +++ b/project/templates/keyboard_page.tpl @@ -0,0 +1,16 @@ +
      +
      +

      +
      + +
      + +
      +
      +
      + +
      +
      +
      \ No newline at end of file diff --git a/project/templates/left_bubble.tpl b/project/templates/left_bubble.tpl new file mode 100644 index 0000000..2d20b1b --- /dev/null +++ b/project/templates/left_bubble.tpl @@ -0,0 +1,3 @@ +
    • + %text% +
    • \ No newline at end of file diff --git a/project/templates/message_popup.tpl b/project/templates/message_popup.tpl new file mode 100644 index 0000000..d69aafd --- /dev/null +++ b/project/templates/message_popup.tpl @@ -0,0 +1,6 @@ +
      +
      +
      + OK +
      +
      diff --git a/project/templates/right_bubble.tpl b/project/templates/right_bubble.tpl new file mode 100644 index 0000000..507e30f --- /dev/null +++ b/project/templates/right_bubble.tpl @@ -0,0 +1,3 @@ +
    • + %text% +
    • \ No newline at end of file diff --git a/project/templates/server_row.tpl b/project/templates/server_row.tpl new file mode 100644 index 0000000..7780cc4 --- /dev/null +++ b/project/templates/server_row.tpl @@ -0,0 +1,7 @@ +
    • + +
      %deviceName%
      + %deviceAddress% + +
      +
    • \ No newline at end of file diff --git a/tizen-app-template.xml b/tizen-app-template.xml new file mode 100755 index 0000000..9bad27d --- /dev/null +++ b/tizen-app-template.xml @@ -0,0 +1,12 @@ + + + BluetoothChat + TIZEN + + description.xml + + + + + + diff --git a/tizen_32.png b/tizen_32.png new file mode 100644 index 0000000..61f35c0 Binary files /dev/null and b/tizen_32.png differ diff --git a/tizen_64.png b/tizen_64.png new file mode 100644 index 0000000..b188083 Binary files /dev/null and b/tizen_64.png differ