704747d4e96c77c9b647ba638566b38c2f2b72ce
[profile/ivi/sdk/web-ide-resources.git] / web-ui-fw / 0.0.2 / 0.0.2_Common / original / css / car / components / settings / js / bluetooth.js
1 /* global Configuration, Settings, hideLoadingSpinner, showLoadingSpinner, ko, $, loadTemplate */
2
3 /**
4  * Bluetooth class provides list view of nearby and known Bluetooth devices, detailed information view about Bluetooth device,
5  * functionality to pair and unpair remote Bluetooth device, mark phone as selected for incoming calls, contacts and
6  * call history, turn on and off Bluetooth adapter.
7  *
8  * This class requires following components:
9  *
10  * * {{#crossLink "Tabs"}}{{/crossLink}} component
11  * * {{#crossLink "Configuration"}}{{/crossLink}} component
12  * * {{#crossLink "Settings"}}{{/crossLink}} component
13  *
14  * @class Bluetooth
15  * @module Settings
16  * @constructor
17  */
18 var Bluetooth = function() {
19         "use strict";
20         var self = this;
21
22         self.loadDefaultAdapter();
23         self.registerDefaultAdapterChangeListener();
24
25         self.loadBluetoothConfig();
26         Configuration.addUpdateListener(function() {
27                 self.loadBluetoothConfig();
28         });
29
30         self.registerSelectedRemoteDeviceChangeListener();
31         self.loadSelectedRemoteDevice();
32
33         self._setRefreshDevicesInterval();
34
35         Settings.domElement.on('eventClick_hidePage', function() {
36                 self.isVisible(false);
37                 var attempts = 0;
38                 function stopScanning() {
39                         console.log("attempts: ", attempts);
40                         if (attempts < 3) {
41                                 attempts++;
42                                 self.stopScan(function(err) {
43                                         if (!!err) {
44                                                 setTimeout(function() {
45                                                         stopScanning();
46                                                 }, 1000);
47                                         }
48                                 }, false);
49                         }
50                 }
51                 stopScanning();
52         });
53
54         Settings.domElement.on('eventClick_showPage', function() {
55                 if ($(".bluetoothContent").length || $(".deviceInfoContent").length) {
56                         self.isVisible(true);
57                         var diff = -1;
58                         if (!!self.lastSync()) {
59                                 diff = new Date().getTime() - self.lastSync();
60                         }
61                         if (!self.scanning() && (diff === -1 || diff > 600000 || !self.devices().length)) {
62                                 self.clearDevices();
63                                 self.scan(true);
64                         }
65                         if (self.scanning()) {
66                                 self.showBluetoothLoadingSpinner();
67                         }
68                 }
69         });
70
71         /**
72          * Sets the state of a Bluetooth adapter to on or off by sending a request to Bluetooth hardware to change the power state.
73          * After Bluetooth adapter is enabled it starts scanning for nearby and known Bluetooth devices.
74          *
75          * @method togglePower
76          */
77         this.togglePower = function() {
78                 console.log("Bluetooth: toggle power called.");
79                 //self.loadDefaultAdapter();
80                 if (!!self.adapter() && !self.togglePowerLocked()) {
81                         self.togglePowerLocked(true);
82                         if (self.adapter().powered) {
83                                 showLoadingSpinner("Turning off");
84                                 self.stopScan(function(err) {
85                                         console.log('setPowered(false) called');
86                                         self.adapter().setPowered(false, function() {
87                                                 console.log('Successfully disable bluetooth subsystem.');
88                                                 self.togglePowerLocked(false);
89                                                 //self.loadDefaultAdapter();
90                                                 hideLoadingSpinner("Turning off");
91                                         }, function(err) {
92                                                 var error = "An error occured while turning bluetooth off.";
93                                                 console.log(error, err);
94                                                 alert(error);
95                                                 self.togglePowerLocked(false);
96                                                 //self.loadDefaultAdapter();
97                                                 hideLoadingSpinner("Turning off");
98                                         });
99                                 }, false);
100                         } else {
101                                 showLoadingSpinner("Turning on");
102                                 self.stopScan(function(err) {
103                                         console.log('setPowered(true) called');
104                                         self.adapter().setPowered(true, function() {
105                                                 console.log('Successfully enable bluetooth subsystem');
106                                                 self.togglePowerLocked(false);
107                                                 //self.loadDefaultAdapter();
108                                                 self.clearDevices();
109                                                 self.scanning(false);
110                                                 setTimeout(function() {
111                                                         hideLoadingSpinner("Turning on");
112                                                         self.scan(true);
113                                                 }, 1000);
114                                         }, function(err) {
115                                                 var error = "An error occured while turning bluetooth on.";
116                                                 console.log(error, err);
117                                                 alert(error);
118                                                 self.togglePowerLocked(false);
119                                                 //self.loadDefaultAdapter();
120                                                 hideLoadingSpinner("Turning on");
121                                         });
122                                 });
123                         }
124                 }
125         };
126
127         /**
128          * Creates a bond with a remote device by initiating the bonding process with peer device using the given device's MAC address or destroys
129          * the bond with a remote device (initiates the process of removing the specified address from the
130          * list of bonded devices).
131          *
132          * @method togglePair
133          * @param device {Object} Object representing remote Bluetooth device to be paired or unpaired according to current device state.
134          */
135         this.togglePair = function(device) {
136                 console.log("Bluetooth: toggle connection called.");
137                 console.log(device);
138                 if (!!device) {
139                         //self.loadDefaultAdapter();
140                         self._clearRefreshDevicesInterval();
141                         if (!device.isBonded) {
142                                 console.log('bluetooth pair to device: ' + device.address);
143                                 showLoadingSpinner("Pairing");
144                                 self.adapter().createBonding(device.address, function() {
145                                         console.log('bluetooth paired with ' + device.address);
146                                         self._restartRefreshDevicesInterval();
147                                         hideLoadingSpinner("Pairing");
148                                 }, function(e) {
149                                         console.log('Error: bluetooth pair failed: ', e);
150                                         self._restartRefreshDevicesInterval();
151                                         //alert("An error occured while pairing with " + device.name);
152                                         hideLoadingSpinner("Pairing");
153                                 });
154                         } else {
155                                 console.log('Bluetooth disconnect from device: ' + device.address);
156                                 showLoadingSpinner("Unpairing");
157                                 self.adapter().destroyBonding(device.address, function() {
158                                         console.log('bluetooth unpaired from ' + device.address);
159                                         self._restartRefreshDevicesInterval();
160                                         hideLoadingSpinner("Unpairing");
161                                 }, function(e) {
162                                         console.log('Error: bluetooth unpairing failed: ' + e);
163                                         self._restartRefreshDevicesInterval();
164                                         //alert("An error occured while unpairing from " + device.name);
165                                         hideLoadingSpinner("Unpairing");
166                                 });
167                         }
168                 }
169         };
170
171         /**
172          * Starts discovering nearby and known remote Bluetooth devices or stops an active discovery session.
173          *
174          * @method toogleScanDevices
175          */
176         this.toogleScanDevices = function() {
177                 console.log("Bluetooth: toggle scan devices called.");
178                 if (self.scanning()) {
179                         console.log("Bluetooth: stop scan called.");
180                         self.stopScan(function(err) {
181                                 if (!!err) {
182                                         alert("An error occured while stopping bluetooth discovery.");
183                                 }
184                         }, true);
185                 } else {
186                         console.log("Bluetooth: scan called.");
187                         self.clearDevices();
188                         self.scan(true);
189                 }
190         };
191
192         /**
193          * Shows more information about given remote Bluetooth device (like name, address, device class, bond, trusted, connection state) in a new view.
194          * That in addition allows to mark phone as selected for incoming calls and pair/unpair remote
195          * Bluetooth device.
196          *
197          * @method openDeviceDetails
198          * @param device {Object} Object representing remote Bluetooth device to be showed in detailed information view.
199          */
200         this.openDeviceDetails = function(device) {
201                 console.log("Bluetooth: open device details called: ", device);
202                 self.selectedDevice(null);
203                 if (!!device) {
204                         self.selectedDevice(device);
205                         var subpanelModel = {
206                                 textTitle : "DEVICE INFO",
207                                 textSubtitle : device.name.toUpperCase(),
208                                 actionName : "BACK",
209                                 action : function() {
210                                         console.log("bluetooth openDeviceDetails");
211                                         Settings.openSetting(Settings.selectedSetting);
212                                 }
213                         };
214
215                         var createDeviceInfoElement = function(key, value, deviceInfoContent) {
216                                 var deviceInfoElement = '<div class="wifiNetworkInfoElement fontSizeLarge fontWeightBold fontColorNormal">';
217                                 deviceInfoElement += '<span>';
218                                 deviceInfoElement += key;
219                                 deviceInfoElement += ": ";
220                                 deviceInfoElement += '</span>';
221                                 deviceInfoElement += '<span data-bind="text:' + value + '">';
222                                 deviceInfoElement += '</span>';
223                                 deviceInfoElement += '</div>';
224                                 return deviceInfoElement;
225                         };
226
227                         var loadDeviceInfoUI = function() {
228                                 if (!$("#bluetoothDeviceInfoBox").length) {
229                                         var button = "";
230                                         button += '<div id="wifiAutoConnectButton" class="toggleButton subPanelToggleButton subPanelToggleButtonWide" data-bind="with: Settings.Bluetooth.selectedDevice, click: Settings.Bluetooth.toggleSelectionOfSelectedDevice, style: { display: Settings.Bluetooth.isSelectedDeviceSelectable() ? \'block\' : \'none\'}">';
231                                         button += '<div class="bgColorThemeTransparent boxShadowInset toggleButtonBackground"></div>';
232                                         button += '<div class="fontColorNormal fontSizeMedium fontWeightBold toggleButtonText" data-bind="text: \'SELECTED\', css: { fontColorSelected: Settings.Bluetooth.isDeviceSelected($data) }"></div>';
233                                         button += '</div>';
234                                         $(button).appendTo($('.tabsTopSubPanel'));
235
236                                         var deviceInfo = '<div id="bluetoothDeviceInfoBox" data-bind="with: Settings.Bluetooth.selectedDevice">';
237                                         deviceInfo += createDeviceInfoElement("Device name", "name");
238                                         deviceInfo += createDeviceInfoElement("Device address", "address");
239                                         deviceInfo += createDeviceInfoElement("Device class", "Settings.Bluetooth.getDeviceClassStr(deviceClass)");
240                                         deviceInfo += createDeviceInfoElement("Paired", "isBonded ? 'Yes' : 'No'");
241                                         deviceInfo += createDeviceInfoElement("Connected", "isConnected ? 'Yes' : 'No'");
242                                         deviceInfo += createDeviceInfoElement("Trusted", "isTrusted ? 'Yes' : 'No'");
243                                         deviceInfo += '<div id="networkConnectButton" class="toggleButton networkConnectButton" data-bind="click: Settings.Bluetooth.togglePair">';
244                                         deviceInfo += '<div class="bgColorThemeTransparent boxShadowInset toggleButtonBackground"></div>';
245                                         deviceInfo += '<div class="fontColorNormal fontSizeMedium fontWeightBold toggleButtonText" data-bind="text: isBonded ? \'UNPAIR\' : \'PAIR\'"></div>';
246                                         deviceInfo += '</div>';
247                                         deviceInfo += '</div>';
248                                         $(deviceInfo).appendTo($('.' + deviceInfoContent));
249                                         ko.applyBindings(window.Settings);
250                                 }
251                         };
252
253                         var deviceInfoContent = "deviceInfoContent";
254                         Settings.domElement.tabs("clearContent");
255                         Settings.domElement.tabs("changeContentClass", deviceInfoContent);
256                         Settings.domElement.tabs("subpanelContentTemplateCompile", subpanelModel, loadDeviceInfoUI);
257                 }
258         };
259
260         /**
261          * Returns major Bluetooth device class in human readable form.
262          *
263          * @method getDeviceClassStr
264          * @param deviceClass {Object} Object representing Bluetooth device class information.
265          * @return {String} Major Bluetooth device class.
266          */
267         this.getDeviceClassStr = function(deviceClass) {
268                 var classStr = "";
269                 switch (deviceClass.major) {
270                 case tizen.bluetooth.deviceMajor.MISC:
271                         classStr = "MISC";
272                         break;
273                 case tizen.bluetooth.deviceMajor.COMPUTER:
274                         classStr = "COMPUTER";
275                         break;
276                 case tizen.bluetooth.deviceMajor.PHONE:
277                         classStr = "PHONE";
278                         break;
279                 case tizen.bluetooth.deviceMajor.NETWORK:
280                         classStr = "NETWORK";
281                         break;
282                 case tizen.bluetooth.deviceMajor.AUDIO_VIDEO:
283                         classStr = "AUDIO/VIDEO";
284                         break;
285                 case tizen.bluetooth.deviceMajor.PERIPHERAL:
286                         classStr = "PERIPHERAL";
287                         break;
288                 case tizen.bluetooth.deviceMajor.IMAGING:
289                         classStr = "IMAGING";
290                         break;
291                 case tizen.bluetooth.deviceMajor.WEARABLE:
292                         classStr = "WEARABLE";
293                         break;
294                 case tizen.bluetooth.deviceMajor.TOY:
295                         classStr = "TOY";
296                         break;
297                 case tizen.bluetooth.deviceMajor.HEALTH:
298                         classStr = "HEALTH";
299                         break;
300                 case tizen.bluetooth.deviceMajor.UNCATEGORIZED:
301                         classStr = "UNCATEGORIZED";
302                         break;
303                 default:
304                         classStr = "UNKNOWN";
305                         break;
306                 }
307                 return classStr;
308         };
309
310         /**
311          * Tests if supplied remote Bluetooth device is marked as selected for incoming calls.
312          *
313          * @method isDeviceSelected
314          * @param device {Object} Object representing remote Bluetooth device to be checked for selection state.
315          * @return {Boolean} True if Bluetooth device is selected otherwise false.
316          */
317         this.isDeviceSelected = function(device) {
318                 if (!!device && !!self.selectedPhone() && self.selectedPhone() === device.address) {
319                         return true;
320                 }
321                 return false;
322         };
323
324         /**
325          * Tests if remote Bluetooth device opened in detail view is selectable for incoming calls (only Bluetooth devices of class Phone can be selected).
326          *
327          * @method isSelectedDeviceSelectable
328          * @return {Boolean} True if remote Bluetooth device is selectable otherwise false.
329          */
330         this.isSelectedDeviceSelectable = function() {
331                 if (!!self.selectedDevice()) {
332                         if (self.isDeviceSelected(self.selectedDevice())) {
333                                 return true;
334                         }
335                         if (!!self.selectedDevice().isBonded && !!self.selectedDevice().address && !!self.selectedDevice().deviceClass && self.selectedDevice().deviceClass.major === tizen.bluetooth.deviceMajor.PHONE) {
336                                 return true;
337                         }
338                 }
339                 return false;
340         };
341
342         /**
343          * Marks and unmarks remote Bluetooth device opened in detail view as selected for incoming calls.
344          *
345          * @method toggleSelectionOfSelectedDevice
346          */
347         this.toggleSelectionOfSelectedDevice = function() {
348                 console.log("Wifi: toggle select device called", self.selectedDevice());
349                 if (self.isDeviceSelected(self.selectedDevice())) {
350                         self.unselectRemoteDevice();
351                 } else {
352                         if (!!self.selectedPhone()) {
353                                 self.unselectRemoteDevice();
354                         }
355                         self.selectRemoteDevice(self.selectedDevice());
356                 }
357         };
358 };
359
360 /**
361  * Contains array of nearby and known remote Bluetooth devices.
362  *
363  * @property devices
364  * @public
365  * @type ko.observableArray
366  * @default []
367  */
368 Bluetooth.prototype.devices = ko.observableArray([]);
369 /**
370  * Provides access to control the device's Bluetooth adapter.
371  *
372  * @property adapter
373  * @public
374  * @type ko.observable
375  * @default null
376  */
377 Bluetooth.prototype.adapter = ko.observable(null);
378 /**
379  * Represents remote Bluetooth device showed in detail view.
380  *
381  * @property selectedDevice
382  * @public
383  * @type ko.observable
384  * @default null
385  */
386 Bluetooth.prototype.selectedDevice = ko.observable(null);
387 /**
388  * Indicates if there is active discovery session for nearby and known remote Bluetooth devices.
389  *
390  * @property scanning
391  * @public
392  * @type ko.observable
393  * @default false
394  */
395 Bluetooth.prototype.scanning = ko.observable(false);
396 /**
397  * Indicates if Bluetooth settings view is visible (in a viewport).
398  *
399  * @property isVisible
400  * @public
401  * @type ko.observable
402  * @default false
403  */
404 Bluetooth.prototype.isVisible = ko.observable(false);
405 /**
406  * Defines Bluetooth hardware address of phone marked as selected for incoming calls, contacts and call history.
407  *
408  * @property selectedPhone
409  * @public
410  * @type ko.observable
411  * @default null
412  */
413 Bluetooth.prototype.selectedPhone = ko.observable(null);
414 /**
415  * Holds information about last synchronisation of nearby and known remote Bluetooth devices with local offline storage.
416  *
417  * @property lastSync
418  * @public
419  * @type ko.observable
420  * @default null
421  */
422 Bluetooth.prototype.lastSync = ko.observable(null);
423 /**
424  * Indicates if Bluetooth power button is clickable.
425  *
426  * @property togglePowerLocked
427  * @public
428  * @type ko.observable
429  * @default false
430  */
431 Bluetooth.prototype.togglePowerLocked = ko.observable(false);
432
433 Bluetooth.prototype._refreshDevicesInterval = null;
434 Bluetooth.prototype._setRefreshDevicesInterval = function() {
435         "use strict";
436         var self = this;
437         self._clearRefreshDevicesInterval();
438         self._refreshDevicesInterval = setInterval(function() {
439                 if (!!self.adapter() && self.isVisible() && !self.scanning() && !document.webkitHidden) {
440                         self.refreshDevices();
441                 }
442         }, 10000);
443 };
444 Bluetooth.prototype._clearRefreshDevicesInterval = function() {
445         "use strict";
446         var self = this;
447         if (!!self._refreshDevicesInterval) {
448                 clearInterval(self._refreshDevicesInterval);
449                 self._refreshDevicesInterval = null;
450         }
451 };
452
453 /**
454  * Loads saved remote Bluetooth devices from local offline storage.
455  *
456  * @method loadBluetoothConfig
457  */
458 Bluetooth.prototype.loadBluetoothConfig = function() {
459         "use strict";
460         var self = this;
461         self._clearRefreshDevicesInterval();
462         var bluetooth = Configuration.get("bluetooth");
463         console.log("loaded bluetooth config: ", JSON.stringify(bluetooth));
464         //self.loadDefaultAdapter();
465         if (!!bluetooth) {
466                 self.lastSync(bluetooth.lastSync);
467                 if (!!self.adapter() && self.adapter().powered && !!bluetooth.devices) {
468                         self.clearDevices();
469                         self.refreshDevices();
470                         self._setRefreshDevicesInterval();
471                 }
472         }
473 };
474
475 /**
476  * Updates or adds Bluetooth device for a given device's hardware address.
477  *
478  * @method getDevice
479  * @param device {Object} Object representing Bluetooth device information to be updated or added.
480  */
481 Bluetooth.prototype.getDevice = function(device) {
482         "use strict";
483         var self = this;
484         self.adapter().getDevice(device.address, function(dev) {
485                 // console.log("getDevice ", dev);
486                 self.addUpdateDevice(dev, false);
487                 self.sortDevices();
488                 self.saveBluetooth();
489                 if (!!self.selectedDevice() && self.selectedDevice().address === dev.address) {
490                         self.selectedDevice(dev);
491                 }
492         }, function(error) {
493                 console.log ("Could not get device info:", error);
494                 console.log ("removing device:", device.address);
495                 self.removeDevice(device.address);
496                 self.sortDevices();
497                 self.saveBluetooth();
498         });
499 };
500
501 /**
502  * Loads the default local Bluetooth adapter.
503  *
504  * @method loadDefaultAdapter
505  */
506 Bluetooth.prototype.loadDefaultAdapter = function() {
507         "use strict";
508         var self = this;
509         if (typeof (tizen.bluetooth) !== 'undefined' && typeof (tizen.bluetooth.getDefaultAdapter) !== 'undefined') {
510                 try {
511                         var adapter = tizen.bluetooth.getDefaultAdapter();
512                         if (adapter === null) {
513                                 console.log('Error: Bluetooth adapter not found');
514                         } else {
515                                 self.adapter(adapter);
516                         }
517                 } catch (err) {
518                         console.log(err);
519                 }
520         } else {
521                 console.log("Bluetooth API is not available.");
522         }
523 };
524
525 /**
526  * Sets the listener to receivce notifications about changes of Bluetooth adapter.
527  *
528  * @method registerDefaultAdapterChangeListener
529  */
530 Bluetooth.prototype.registerDefaultAdapterChangeListener = function() {
531         "use strict";
532         var self = this;
533         if (!!self.adapter() && typeof (self.adapter().setChangeListener) !== 'undefined') {
534                 try {
535                         self.adapter().setChangeListener({
536                                 onstatechanged : function(powered) {
537                                         console.log("Power state is changed into: " + powered);
538                                         self.loadDefaultAdapter();
539                                         if (!powered) {
540                                                 self.clearDevices();
541                                                 self.scanning(false);
542                                                 self.hideBluetoothLoadingSpinner();
543                                                 hideLoadingSpinner("Scanning");
544                                                 self.saveBluetooth();
545                                         }
546                                 },
547                                 onnamechanged : function(name) {
548                                         console.log("Name is changed to: " + name);
549                                         //self.loadDefaultAdapter();
550                                 },
551                                 onvisibilitychanged : function(visible) {
552                                         console.log("Visibility is changed into: " + visible);
553                                         //self.loadDefaultAdapter();
554                                 }
555                         });
556                 } catch (err) {
557                         console.log(err);
558                 }
559         } else {
560                 console.log("adapter.setChangeListener API not available.");
561         }
562 };
563
564 /**
565  * Shows list view of nearby and known remote Bluetooth devices and allows to trigger rediscovering, open detail view, pair on unpair selected Bluetooth device, turn on or off Bluetooth adapter.
566  *
567  * @method show
568  */
569 Bluetooth.prototype.show = function(successCallback) {
570         "use strict";
571         var self = this;
572         console.log("Bluetooth show called");
573         self.isVisible(true);
574         var subpanelModel = {
575                 textTitle : "SETTINGS",
576                 textSubtitle : "BLUETOOTH",
577                 actionName : "BACK",
578                 action : function() {
579                         self.isVisible(false);
580                         Settings.renderSettingsView();
581                 }
582         };
583
584         var loadBluetoothDevicesUI = function() {
585                 if (!$("#bluetoothDevicesList").length) {
586                         var bluetoothDevicesList = '<div id="bluetoothDevicesList" data-bind="template: { name: \'';
587                         bluetoothDevicesList += templateName;
588                         bluetoothDevicesList += '\', foreach: Settings.Bluetooth.devices }"></div>';
589                         $(bluetoothDevicesList).prependTo($('.' + bluetoothContent));
590
591                         var button = "";
592                         button += '<div class="buttonsArea">';
593                         button += '<div id="bluetoothRefreshButton" class="toggleButton bluetoothRefreshButton" data-bind="click: Settings.Bluetooth.toogleScanDevices, style: { display: Settings.Bluetooth.adapter().powered ? \'block\' : \'none\' }">';
594                         button += '<div class="bgColorThemeTransparent boxShadowInset toggleButtonBackground"></div>';
595                         button += '<div class="fontColorNormal fontSizeMedium fontWeightBold toggleButtonText" data-bind="text: Settings.Bluetooth.scanning() ? \'STOP\' : \'SCAN\'"></div>';
596                         button += '</div>';
597                         button += '</div>';
598                         $(button).appendTo($('.' + bluetoothContent));
599
600                         button = '<div id="wifiPowerButton" class="toggleButton subPanelToggleButton subPanelToggleButtonWide" data-bind="with: Settings.Bluetooth.adapter, click: Settings.Bluetooth.togglePower">';
601                         button += '<div class="bgColorThemeTransparent boxShadowInset toggleButtonBackground"></div>';
602                         button += '<div class="fontColorNormal fontSizeMedium fontWeightBold toggleButtonText" data-bind="text: powered ? \'TURN OFF\' : \'TURN ON\'"></div>';
603                         button += '</div>';
604                         $(button).appendTo($('.tabsTopSubPanel'));
605                         ko.applyBindings(window.Settings);
606
607                         var diff = -1;
608                         if (!!self.lastSync()) {
609                                 diff = new Date().getTime() - self.lastSync();
610                         }
611                         if (!self.scanning() && (diff === -1 || diff > 600000 || !self.devices().length)) {
612                                 self.clearDevices();
613                                 self.scan(true);
614                         }
615                         if (self.scanning()) {
616                                 self.showBluetoothLoadingSpinner();
617                         }
618                 }
619         };
620
621         var bluetoothContent = "bluetoothContent";
622         var templateName = "template-bluetooth";
623         Settings.domElement.tabs("clearContent");
624         Settings.domElement.tabs("changeContentClass", bluetoothContent);
625         Settings.domElement.tabs("subpanelContentTemplateCompile", subpanelModel, function() {
626                 loadTemplate(Settings.SETTINGS_TEMPLATES_PATH, templateName, loadBluetoothDevicesUI);
627         });
628         if (!Settings.domElement.find(".bluetoothPINCode").length) {
629                 var pinCode = "<div class='bluetoothPINCode fontSizeXXSmall fontWeightBold fontColorTheme'>Default bluetooth pincode / passkey: 123456</div>";
630                 $(pinCode).appendTo(Settings.domElement);
631         }
632 };
633
634 /**
635  * Shows small loading spinner in header during active discovery session.
636  *
637  * @method showBluetoothLoadingSpinner
638  */
639 Bluetooth.prototype.showBluetoothLoadingSpinner = function() {
640         "use strict";
641         if ($(".bluetoothContent").length) {
642                 if (!$("#loadingSpinnerBluetooth").length) {
643                         var spinner = '';
644                         spinner += '<div id="loadingSpinnerBluetooth" class="loadingSpinnerBluetooth loading-container loading-container-small">';
645                         spinner += '<div class="loading loading-small"></div>';
646                         spinner += '</div>';
647                         $(spinner).appendTo($(".tabsTopSubPanel"));
648                 }
649                 $("#loadingSpinnerBluetooth").show();
650         }
651 };
652
653 /**
654  * Hides small loading spinner in header.
655  *
656  * @method hideBluetoothLoadingSpinner
657  */
658 Bluetooth.prototype.hideBluetoothLoadingSpinner = function() {
659         "use strict";
660         if ($("#loadingSpinnerBluetooth").length) {
661                 $("#loadingSpinnerBluetooth").hide();
662         }
663 };
664
665 /**
666  * Discovers nearby Bluetooth devices if any, that is, devices within proximity to the local device and gets all the known devices that have information stored in the local Bluetooth adapter.
667  *
668  * @method scan
669  * @param showSpinner? {Boolean} Indicates if full screen loading spinner should be visible during active discovery session.
670  */
671 Bluetooth.prototype.scan = function(showSpinner) {
672         "use strict";
673         var self = this;
674         showSpinner = typeof (showSpinner) === 'undefined' ? true : showSpinner;
675         if (self.scanning()) {
676                 self.showBluetoothLoadingSpinner();
677                 if (showSpinner) {
678                         showLoadingSpinner("Scanning");
679                 }
680                 return;
681         }
682         //self.loadDefaultAdapter();
683         self.stopScan(function(err) {
684                 if (!!self.adapter() && self.adapter().powered) {
685                         console.log("Bluetooth: discoverDevices called");
686                         if (showSpinner) {
687                                 showLoadingSpinner("Scanning");
688                         }
689                         self.showBluetoothLoadingSpinner();
690                         self.scanning(true);
691                         self.lastSync(new Date().getTime());
692
693                         self.adapter().getKnownDevices(function(devices) {
694                                 if (devices.length) {
695                                         for ( var i = 0; i < devices.length; ++i) {
696                                                 console.log("Known device name: " + devices[i].name + ", Address: " + devices[i].address);
697                                                 self.addUpdateDevice(devices[i]);
698                                         }
699                                         self.sortDevices();
700                                         self.saveBluetooth();
701                                         hideLoadingSpinner("Scanning");
702                                 }
703                         }, function(error) {
704                                 console.log("Could not get known devices: ", error);
705                         });
706
707                         var discoveryTimeout = null;
708                         var clearDiscoveryTimeout = function() {
709                                 if (!!discoveryTimeout) {
710                                         clearTimeout(discoveryTimeout);
711                                         discoveryTimeout = null;
712                                 }
713                         };
714                         var errorDiscoveryCallback = function(error) {
715                                 console.log('An error occured while discovering bluetooth devices: ', error);
716                                 self.stopScan(function() {
717                                         clearDiscoveryTimeout();
718                                         self.scanning(false);
719                                         hideLoadingSpinner("Scanning");
720                                         self.hideBluetoothLoadingSpinner();
721                                 }, false);
722                         };
723
724                         // Workaround due to https://bugs.tizen.org/jira/browse/TIVI-2565
725                         discoveryTimeout = setTimeout(function() {
726                                 errorDiscoveryCallback("Bluetooth adapter busy.");
727                         }, 30000);
728
729                         self.adapter().discoverDevices({
730                                 onstarted : function() {
731                                         console.log("Device discovery started.");
732                                 },
733                                 ondevicefound : function(device) {
734                                         console.log("Found device - name: " + device.name + ", Address: " + device.address);
735                                         clearDiscoveryTimeout();
736                                         self.addUpdateDevice(device);
737                                         self.sortDevices();
738                                         self.saveBluetooth();
739                                         hideLoadingSpinner("Scanning");
740                                 },
741                                 ondevicedisappeared : function(address) {
742                                         console.log("Device disappeared: " + address);
743                                         clearDiscoveryTimeout();
744                                         self.removeDevice(address);
745                                         self.sortDevices();
746                                         self.saveBluetooth();
747                                 },
748                                 onfinished : function(devices) {
749                                         console.log("Device discovery finished.");
750                                         clearDiscoveryTimeout();
751                                         for ( var i = 0; i < devices.length; ++i) {
752                                                 console.log("Name: " + devices[i].name + ", Address: " + devices[i].address);
753                                                 self.addUpdateDevice(devices[i]);
754                                         }
755                                         self.sortDevices();
756                                         self.scanning(false);
757                                         hideLoadingSpinner("Scanning");
758                                         self.hideBluetoothLoadingSpinner();
759                                 }
760                         }, function(err) {
761                                 errorDiscoveryCallback(err);
762                         });
763                 }
764         }, false);
765 };
766
767 /**
768  * Stops an active device discovery session.
769  *
770  * @method stopScan
771  * @param callback {Function(error)} Callback function to be invoked when stopping ends.
772  * @param showSpinner? {Boolean} Indicates if full screen spinner should be visible during stopping process.
773  */
774 Bluetooth.prototype.stopScan = function(callback, showSpinner) {
775         "use strict";
776         var self = this;
777         //self.loadDefaultAdapter();
778         if (!!self.adapter() && self.adapter().powered) {
779                 showSpinner = typeof (showSpinner) === 'undefined' ? false : showSpinner;
780                 if (showSpinner) {
781                         showLoadingSpinner("Stopping");
782                 }
783                 self.adapter().stopDiscovery(function() {
784                         console.log("Stop discovery success.");
785                         self.scanning(false);
786                         hideLoadingSpinner("Stopping");
787                         self.hideBluetoothLoadingSpinner();
788                         if (!!callback) {
789                                 callback();
790                         }
791                 }, function(err) {
792                         console.log("An error occured while stopping bluetooth discovery.", err);
793                         hideLoadingSpinner("Stopping");
794                         if (!!callback) {
795                                 callback(err);
796                         }
797                 });
798         } else {
799                 if (!!callback) {
800                         callback();
801                 }
802         }
803 };
804
805 /**
806  * Adds or updates remote Bluetooth device in list of nearby and known remote Bluetooth devices.
807  *
808  * @method addUpdateDevice
809  * @param device {Object} Object representing Bluetooth device to be added or updated.
810  * @param addDevice? {Boolean} Indicates if device should be added if it is not yet in the list of available and known Bluetooth devices.
811  */
812 Bluetooth.prototype.addUpdateDevice = function(device, addDevice) {
813         "use strict";
814         var self = this;
815         // console.log("Device to be added/updated");
816         // console.log(device);
817
818         if (!!device && !!self.devices()) {
819                 var deviceExists = false;
820                 for ( var i = 0; i < self.devices().length; ++i) {
821                         var dev = self.devices()[i];
822                         if (dev.address === device.address) {
823                                 self.devices()[i] = device;
824                                 deviceExists = true;
825                                 break;
826                         }
827                 }
828                 addDevice = typeof (addDevice) === 'undefined' ? true : addDevice;
829                 if (!deviceExists && addDevice) {
830                         self.devices.push(device);
831                 }
832         }
833 };
834
835 /**
836  * Removes remote Bluetooth device from list of nearby and known remote Bluetooth devices.
837  *
838  * @method removeDevice
839  * @param deviceAddress {String} Bluetooth device hardware address to be removed.
840  */
841 Bluetooth.prototype.removeDevice = function(deviceAddress) {
842         "use strict";
843         var self = this;
844         if (!!deviceAddress && deviceAddress !== "" && !!self.devices() && self.devices().length) {
845                 self.devices.remove(function(device) {
846                         return device.address === deviceAddress;
847                 });
848         }
849 };
850
851 /**
852  * Clears list of nearby and known remote Bluetooth devices.
853  *
854  * @method clearDevices
855  */
856 Bluetooth.prototype.clearDevices = function() {
857         "use strict";
858         this.devices.removeAll();
859         this.devices([]);
860 };
861
862 /**
863  * Sorts nearby and known remote Bluetooth devices by attribute representing the bond state of remote device with the local device (paired firts).
864  *
865  * @method sortDevices
866  */
867 Bluetooth.prototype.sortDevices = function() {
868         "use strict";
869         var self = this;
870         if (!!self.devices() && self.devices().length) {
871                 self.devices.sort(function(left, right) {
872                         return left.isBonded === right.isBonded ? 0 : left.isBonded ? -1 : 1;
873                 });
874         }
875 };
876
877 /**
878  * Saves Bluetooth adapter power state and nearby and known remote Bluetooth devices to local offline storage in order to keep same list of devices accross all the applications.
879  *
880  * @method saveBluetooth
881  */
882 Bluetooth.prototype.saveBluetooth = function() {
883         "use strict";
884         var devs = ko.toJS(this.devices().slice(0));
885
886         // clean bluetooth device objects from properties that do not need to be saved
887         for ( var i = 0; i < devs.length; ++i) {
888                 delete devs[i].connectToServiceByUUID;
889                 delete devs[i].uuids;
890                 var deviceClass = devs[i].deviceClass;
891                 delete devs[i].deviceClass;
892                 devs[i].deviceClass = {};
893                 if (!!deviceClass) {
894                         devs[i].deviceClass.major = deviceClass.major;
895                         devs[i].deviceClass.minor = deviceClass.minor;
896                 }
897         }
898         var newBluetoothConf = {
899                 devices : devs,
900                 lastSync : this.lastSync()
901         };
902         var savedBluetoothConf = Configuration.get("bluetooth");
903         var savedBluetoothConfStr = JSON.stringify(savedBluetoothConf);
904         //console.log(savedBluetoothConfStr);
905         var newBluetoothConfStr = JSON.stringify(newBluetoothConf);
906         //console.log(newBluetoothConfStr);
907         //console.log("SAME: ", savedBluetoothConfStr == newBluetoothConfStr ? "YES" : "NO");
908         if (!savedBluetoothConf || newBluetoothConfStr !== savedBluetoothConfStr) {
909                 Configuration.set("bluetooth", newBluetoothConf, false);
910         }
911 };
912
913 /**
914  * Loads Bluetooth device hardware address of phone marked as selected for incoming calls.
915  *
916  * @method loadSelectedRemoteDevice
917  */
918 Bluetooth.prototype.loadSelectedRemoteDevice = function() {
919         "use strict";
920         var self = this;
921         if (typeof (tizen.phone) !== 'undefined' && typeof (tizen.phone.getSelectedRemoteDevice) !== 'undefined') {
922                 try {
923                         tizen.phone.getSelectedRemoteDevice(function(selectedRemoteDev) {
924                                 console.log("selected remote device: ", selectedRemoteDev);
925                                 if (!!selectedRemoteDev && selectedRemoteDev !== "") {
926                                         self.selectedPhone(selectedRemoteDev);
927                                 } else {
928                                         self.selectedPhone(null);
929                                 }
930                         });
931                 } catch (err) {
932                         console.log("An error occured while loading selected remote device ", err);
933                         self.selectedPhone(null);
934                 }
935         }
936 };
937
938 /**
939  * Marks a given remote Bluetooth device as selected for incoming calls.
940  *
941  * @method selectRemoteDevice
942  * @param device {Object} Object representing remote Bluetooth device to be selected.
943  */
944 Bluetooth.prototype.selectRemoteDevice = function(device) {
945         "use strict";
946         var self = this;
947         console.log("selectRemoteDevice called", device);
948         if (!!device && !!device.address && !!device.deviceClass && device.deviceClass.major === tizen.bluetooth.deviceMajor.PHONE && typeof (tizen.phone) !== 'undefined' && typeof (tizen.phone.selectRemoteDevice) !== 'undefined') {
949                 showLoadingSpinner("Selecting");
950                 try {
951                         tizen.phone.selectRemoteDevice(device.address);
952                 } catch (err) {
953                         console.log("An error occured while selecting remote device ", err);
954                 }
955         } else {
956                 console.log("tizen.phone.selectRemoteDevice API not available or supplied device is not valid.");
957         }
958 };
959
960 /**
961  * Unmarks previously selected remote Bluetooth device for incoming calls.
962  *
963  * @method unselectRemoteDevice
964  */
965 Bluetooth.prototype.unselectRemoteDevice = function() {
966         "use strict";
967         var self = this;
968         console.log("unselectRemoteDevice called");
969         if (typeof (tizen.phone) !== 'undefined' && typeof (tizen.phone.unselectRemoteDevice) !== 'undefined') {
970                 try {
971                         tizen.phone.unselectRemoteDevice();
972                 } catch (err) {
973                         console.log("An error occured while unselecting remote device ", err);
974                 }
975         } else {
976                 console.log("tizen.phone.unselectRemoteDevice API not available.");
977         }
978 };
979
980 /**
981  * Sets the listener to receivce notifications when new remote Bluetooth device was marked as selected for incoming calls.
982  * @method registerSelectedRemoteDeviceChangeListener
983  */
984 Bluetooth.prototype.registerSelectedRemoteDeviceChangeListener = function() {
985         "use strict";
986         var self = this;
987
988         if (typeof (tizen.phone) !== 'undefined' && typeof (tizen.phone.addRemoteDeviceSelectedListener) !== 'undefined') {
989                 try {
990                         tizen.phone.addRemoteDeviceSelectedListener(function(result) {
991                                 console.log("addRemoteDeviceSelectedListener: ", result);
992                                 if (!!result && !!result.error) {
993                                         console.log("An error occured while selecting remote device: ", result.error);
994                                         self.selectedPhone(null);
995                                 } else if (!!result && !!result.value && result.value.toString().trim() !== "") {
996                                         self.selectedPhone(result.value.toString().trim());
997                                 } else {
998                                         self.selectedPhone(null);
999                                 }
1000                                 self._restartRefreshDevicesInterval();
1001                                 hideLoadingSpinner("Selecting");
1002                         });
1003                 } catch (err) {
1004                         console.log("An error occured while registering remote device selected listener ", err);
1005                 }
1006         } else {
1007                 console.log("tizen.phone.addRemoteDeviceSelectedListener API not available.");
1008         }
1009 };
1010
1011 /**
1012  * Reloads list of nearby and knwon remote Bluetooth devices and default local Bluetooth adapter.
1013  *
1014  * @method refreshDevices
1015  */
1016 Bluetooth.prototype.refreshDevices = function() {
1017         "use strict";
1018         var self = this;
1019         console.log("refreshDevices called");
1020         var devices = self.devices().slice(0);
1021         self.clearDevices();
1022         //self.loadDefaultAdapter();
1023         if (!!self.adapter() && self.adapter().powered) {
1024                 self.devices(devices);
1025         }
1026
1027         function updateDeviceInfo() {
1028                 for ( var i = 0; i < self.devices().length; ++i) {
1029                         var dev = self.devices()[i];
1030                         if (!!self.selectedDevice() && self.selectedDevice().address === dev.address) {
1031                                 self.selectedDevice(dev);
1032                         }
1033                         if (dev.toString().indexOf("BluetoothDevice") === -1) {
1034                                 self.getDevice(dev);
1035                         }
1036                 }
1037                 self.sortDevices();
1038                 self.saveBluetooth();
1039         }
1040
1041         if (self.devices().length) {
1042                 self.adapter().getKnownDevices(function(devs) {
1043                         if (devs.length) {
1044                                 for ( var i = 0; i < devs.length; ++i) {
1045                                         self.addUpdateDevice(devs[i], false);
1046                                 }
1047                         }
1048                         updateDeviceInfo();
1049                 }, function(error) {
1050                         console.log("Could not get known devices: ", error);
1051                         updateDeviceInfo();
1052                 });
1053         } else {
1054                 updateDeviceInfo();
1055         }
1056 };
1057
1058 Bluetooth.prototype._restartRefreshDevicesInterval = function() {
1059         "use strict";
1060         var self = this;
1061         self._clearRefreshDevicesInterval();
1062         self.refreshDevices();
1063         self._setRefreshDevicesInterval();
1064 };