Updated Modello Common libraries
[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         if (!self.adapter()) {
485                 console.log ("Device is null");
486                 console.log ("removing device:", device.address);
487                 self.removeDevice(device.address);
488                 self.sortDevices();
489                 self.saveBluetooth();
490         }
491
492         self.adapter().getDevice(device.address, function(dev) {
493                 // console.log("getDevice ", dev);
494                 self.addUpdateDevice(dev, false);
495                 self.sortDevices();
496                 self.saveBluetooth();
497                 if (!!self.selectedDevice() && self.selectedDevice().address === dev.address) {
498                         self.selectedDevice(dev);
499                 }
500         }, function(error) {
501                 console.log ("Could not get device info:", error);
502                 console.log ("removing device:", device.address);
503                 self.removeDevice(device.address);
504                 self.sortDevices();
505                 self.saveBluetooth();
506         });
507 };
508
509 /**
510  * Loads the default local Bluetooth adapter.
511  *
512  * @method loadDefaultAdapter
513  */
514 Bluetooth.prototype.loadDefaultAdapter = function() {
515         "use strict";
516         var self = this;
517         if (typeof (tizen.bluetooth) !== 'undefined' && typeof (tizen.bluetooth.getDefaultAdapter) !== 'undefined') {
518                 try {
519                         var adapter = tizen.bluetooth.getDefaultAdapter();
520                         if (adapter === null) {
521                                 console.log('Error: Bluetooth adapter not found');
522                         } else {
523                                 self.adapter(adapter);
524                         }
525                 } catch (err) {
526                         console.log(err);
527                 }
528         } else {
529                 console.log("Bluetooth API is not available.");
530         }
531 };
532
533 /**
534  * Sets the listener to receivce notifications about changes of Bluetooth adapter.
535  *
536  * @method registerDefaultAdapterChangeListener
537  */
538 Bluetooth.prototype.registerDefaultAdapterChangeListener = function() {
539         "use strict";
540         var self = this;
541         if (!!self.adapter() && typeof (self.adapter().setChangeListener) !== 'undefined') {
542                 try {
543                         self.adapter().setChangeListener({
544                                 onstatechanged : function(powered) {
545                                         console.log("Power state is changed into: " + powered);
546                                         self.loadDefaultAdapter();
547                                         if (!powered) {
548                                                 self.clearDevices();
549                                                 self.scanning(false);
550                                                 self.hideBluetoothLoadingSpinner();
551                                                 hideLoadingSpinner("Scanning");
552                                                 self.saveBluetooth();
553                                         }
554                                 },
555                                 onnamechanged : function(name) {
556                                         console.log("Name is changed to: " + name);
557                                         //self.loadDefaultAdapter();
558                                 },
559                                 onvisibilitychanged : function(visible) {
560                                         console.log("Visibility is changed into: " + visible);
561                                         //self.loadDefaultAdapter();
562                                 }
563                         });
564                 } catch (err) {
565                         console.log(err);
566                 }
567         } else {
568                 console.log("adapter.setChangeListener API not available.");
569         }
570 };
571
572 /**
573  * 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.
574  *
575  * @method show
576  */
577 Bluetooth.prototype.show = function(successCallback) {
578         "use strict";
579         var self = this;
580         console.log("Bluetooth show called");
581         self.isVisible(true);
582         var subpanelModel = {
583                 textTitle : "SETTINGS",
584                 textSubtitle : "BLUETOOTH",
585                 actionName : "BACK",
586                 action : function() {
587                         self.isVisible(false);
588                         Settings.renderSettingsView();
589                 }
590         };
591
592         var loadBluetoothDevicesUI = function() {
593                 if (!$("#bluetoothDevicesList").length) {
594                         var bluetoothDevicesList = '<div id="bluetoothDevicesList" data-bind="template: { name: \'';
595                         bluetoothDevicesList += templateName;
596                         bluetoothDevicesList += '\', foreach: Settings.Bluetooth.devices }"></div>';
597                         $(bluetoothDevicesList).prependTo($('.' + bluetoothContent));
598
599                         var button = "";
600                         button += '<div class="buttonsArea">';
601                         button += '<div id="bluetoothRefreshButton" class="toggleButton bluetoothRefreshButton" data-bind="click: Settings.Bluetooth.toogleScanDevices, style: { display: Settings.Bluetooth.adapter().powered ? \'block\' : \'none\' }">';
602                         button += '<div class="bgColorThemeTransparent boxShadowInset toggleButtonBackground"></div>';
603                         button += '<div class="fontColorNormal fontSizeMedium fontWeightBold toggleButtonText" data-bind="text: Settings.Bluetooth.scanning() ? \'STOP\' : \'SCAN\'"></div>';
604                         button += '</div>';
605                         button += '</div>';
606                         $(button).appendTo($('.' + bluetoothContent));
607
608                         button = '<div id="wifiPowerButton" class="toggleButton subPanelToggleButton subPanelToggleButtonWide" data-bind="with: Settings.Bluetooth.adapter, click: Settings.Bluetooth.togglePower">';
609                         button += '<div class="bgColorThemeTransparent boxShadowInset toggleButtonBackground"></div>';
610                         button += '<div class="fontColorNormal fontSizeMedium fontWeightBold toggleButtonText" data-bind="text: powered ? \'TURN OFF\' : \'TURN ON\'"></div>';
611                         button += '</div>';
612                         $(button).appendTo($('.tabsTopSubPanel'));
613                         ko.applyBindings(window.Settings);
614
615                         var diff = -1;
616                         if (!!self.lastSync()) {
617                                 diff = new Date().getTime() - self.lastSync();
618                         }
619                         if (!self.scanning() && (diff === -1 || diff > 600000 || !self.devices().length)) {
620                                 self.clearDevices();
621                                 self.scan(true);
622                         }
623                         if (self.scanning()) {
624                                 self.showBluetoothLoadingSpinner();
625                         }
626                 }
627         };
628
629         var bluetoothContent = "bluetoothContent";
630         var templateName = "template-bluetooth";
631         Settings.domElement.tabs("clearContent");
632         Settings.domElement.tabs("changeContentClass", bluetoothContent);
633         Settings.domElement.tabs("subpanelContentTemplateCompile", subpanelModel, function() {
634                 loadTemplate(Settings.SETTINGS_TEMPLATES_PATH, templateName, loadBluetoothDevicesUI);
635         });
636         if (!Settings.domElement.find(".bluetoothPINCode").length) {
637                 var pinCode = "<div class='bluetoothPINCode fontSizeXXSmall fontWeightBold fontColorTheme'>Default bluetooth pincode / passkey: 123456</div>";
638                 $(pinCode).appendTo(Settings.domElement);
639         }
640 };
641
642 /**
643  * Shows small loading spinner in header during active discovery session.
644  *
645  * @method showBluetoothLoadingSpinner
646  */
647 Bluetooth.prototype.showBluetoothLoadingSpinner = function() {
648         "use strict";
649         if ($(".bluetoothContent").length) {
650                 if (!$("#loadingSpinnerBluetooth").length) {
651                         var spinner = '';
652                         spinner += '<div id="loadingSpinnerBluetooth" class="loadingSpinnerBluetooth loading-container loading-container-small">';
653                         spinner += '<div class="loading loading-small"></div>';
654                         spinner += '</div>';
655                         $(spinner).appendTo($(".tabsTopSubPanel"));
656                 }
657                 $("#loadingSpinnerBluetooth").show();
658         }
659 };
660
661 /**
662  * Hides small loading spinner in header.
663  *
664  * @method hideBluetoothLoadingSpinner
665  */
666 Bluetooth.prototype.hideBluetoothLoadingSpinner = function() {
667         "use strict";
668         if ($("#loadingSpinnerBluetooth").length) {
669                 $("#loadingSpinnerBluetooth").hide();
670         }
671 };
672
673 /**
674  * 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.
675  *
676  * @method scan
677  * @param showSpinner? {Boolean} Indicates if full screen loading spinner should be visible during active discovery session.
678  */
679 Bluetooth.prototype.scan = function(showSpinner) {
680         "use strict";
681         var self = this;
682         showSpinner = typeof (showSpinner) === 'undefined' ? true : showSpinner;
683         if (self.scanning()) {
684                 self.showBluetoothLoadingSpinner();
685                 if (showSpinner) {
686                         showLoadingSpinner("Scanning");
687                 }
688                 return;
689         }
690         //self.loadDefaultAdapter();
691         self.stopScan(function(err) {
692                 if (!!self.adapter() && self.adapter().powered) {
693                         console.log("Bluetooth: discoverDevices called");
694                         if (showSpinner) {
695                                 showLoadingSpinner("Scanning");
696                         }
697                         self.showBluetoothLoadingSpinner();
698                         self.scanning(true);
699                         self.lastSync(new Date().getTime());
700
701                         self.adapter().getKnownDevices(function(devices) {
702                                 if (devices.length) {
703                                         for ( var i = 0; i < devices.length; ++i) {
704                                                 console.log("Known device name: " + devices[i].name + ", Address: " + devices[i].address);
705                                                 self.addUpdateDevice(devices[i]);
706                                         }
707                                         self.sortDevices();
708                                         self.saveBluetooth();
709                                         hideLoadingSpinner("Scanning");
710                                 }
711                         }, function(error) {
712                                 console.log("Could not get known devices: ", error);
713                         });
714
715                         var discoveryTimeout = null;
716                         var clearDiscoveryTimeout = function() {
717                                 if (!!discoveryTimeout) {
718                                         clearTimeout(discoveryTimeout);
719                                         discoveryTimeout = null;
720                                 }
721                         };
722                         var errorDiscoveryCallback = function(error) {
723                                 console.log('An error occured while discovering bluetooth devices: ', error);
724                                 self.stopScan(function() {
725                                         clearDiscoveryTimeout();
726                                         self.scanning(false);
727                                         hideLoadingSpinner("Scanning");
728                                         self.hideBluetoothLoadingSpinner();
729                                 }, false);
730                         };
731
732                         // Workaround due to https://bugs.tizen.org/jira/browse/TIVI-2565
733                         discoveryTimeout = setTimeout(function() {
734                                 errorDiscoveryCallback("Bluetooth adapter busy.");
735                         }, 30000);
736
737                         self.adapter().discoverDevices({
738                                 onstarted : function() {
739                                         console.log("Device discovery started.");
740                                 },
741                                 ondevicefound : function(device) {
742                                         console.log("Found device - name: " + device.name + ", Address: " + device.address);
743                                         clearDiscoveryTimeout();
744                                         self.addUpdateDevice(device);
745                                         self.sortDevices();
746                                         self.saveBluetooth();
747                                         hideLoadingSpinner("Scanning");
748                                 },
749                                 ondevicedisappeared : function(address) {
750                                         console.log("Device disappeared: " + address);
751                                         clearDiscoveryTimeout();
752                                         self.removeDevice(address);
753                                         self.sortDevices();
754                                         self.saveBluetooth();
755                                 },
756                                 onfinished : function(devices) {
757                                         console.log("Device discovery finished.");
758                                         clearDiscoveryTimeout();
759                                         for ( var i = 0; i < devices.length; ++i) {
760                                                 console.log("Name: " + devices[i].name + ", Address: " + devices[i].address);
761                                                 self.addUpdateDevice(devices[i]);
762                                         }
763                                         self.sortDevices();
764                                         self.scanning(false);
765                                         hideLoadingSpinner("Scanning");
766                                         self.hideBluetoothLoadingSpinner();
767                                 }
768                         }, function(err) {
769                                 errorDiscoveryCallback(err);
770                         });
771                 }
772         }, false);
773 };
774
775 /**
776  * Stops an active device discovery session.
777  *
778  * @method stopScan
779  * @param callback {Function(error)} Callback function to be invoked when stopping ends.
780  * @param showSpinner? {Boolean} Indicates if full screen spinner should be visible during stopping process.
781  */
782 Bluetooth.prototype.stopScan = function(callback, showSpinner) {
783         "use strict";
784         var self = this;
785         //self.loadDefaultAdapter();
786         if (!!self.adapter() && self.adapter().powered) {
787                 showSpinner = typeof (showSpinner) === 'undefined' ? false : showSpinner;
788                 if (showSpinner) {
789                         showLoadingSpinner("Stopping");
790                 }
791                 self.adapter().stopDiscovery(function() {
792                         console.log("Stop discovery success.");
793                         self.scanning(false);
794                         hideLoadingSpinner("Stopping");
795                         self.hideBluetoothLoadingSpinner();
796                         if (!!callback) {
797                                 callback();
798                         }
799                 }, function(err) {
800                         console.log("An error occured while stopping bluetooth discovery.", err);
801                         hideLoadingSpinner("Stopping");
802                         if (!!callback) {
803                                 callback(err);
804                         }
805                 });
806         } else {
807                 if (!!callback) {
808                         callback();
809                 }
810         }
811 };
812
813 /**
814  * Adds or updates remote Bluetooth device in list of nearby and known remote Bluetooth devices.
815  *
816  * @method addUpdateDevice
817  * @param device {Object} Object representing Bluetooth device to be added or updated.
818  * @param addDevice? {Boolean} Indicates if device should be added if it is not yet in the list of available and known Bluetooth devices.
819  */
820 Bluetooth.prototype.addUpdateDevice = function(device, addDevice) {
821         "use strict";
822         var self = this;
823         // console.log("Device to be added/updated");
824         // console.log(device);
825
826         if (!!device && !!self.devices()) {
827                 var deviceExists = false;
828                 for ( var i = 0; i < self.devices().length; ++i) {
829                         var dev = self.devices()[i];
830                         if (dev.address === device.address) {
831                                 self.devices()[i] = device;
832                                 deviceExists = true;
833                                 break;
834                         }
835                 }
836                 addDevice = typeof (addDevice) === 'undefined' ? true : addDevice;
837                 if (!deviceExists && addDevice) {
838                         self.devices.push(device);
839                 }
840         }
841 };
842
843 /**
844  * Removes remote Bluetooth device from list of nearby and known remote Bluetooth devices.
845  *
846  * @method removeDevice
847  * @param deviceAddress {String} Bluetooth device hardware address to be removed.
848  */
849 Bluetooth.prototype.removeDevice = function(deviceAddress) {
850         "use strict";
851         var self = this;
852         if (!!deviceAddress && deviceAddress !== "" && !!self.devices() && self.devices().length) {
853                 self.devices.remove(function(device) {
854                         return device.address === deviceAddress;
855                 });
856         }
857 };
858
859 /**
860  * Clears list of nearby and known remote Bluetooth devices.
861  *
862  * @method clearDevices
863  */
864 Bluetooth.prototype.clearDevices = function() {
865         "use strict";
866         this.devices.removeAll();
867         this.devices([]);
868 };
869
870 /**
871  * Sorts nearby and known remote Bluetooth devices by attribute representing the bond state of remote device with the local device (paired firts).
872  *
873  * @method sortDevices
874  */
875 Bluetooth.prototype.sortDevices = function() {
876         "use strict";
877         var self = this;
878         if (!!self.devices() && self.devices().length) {
879                 self.devices.sort(function(left, right) {
880                         return left.isBonded === right.isBonded ? 0 : left.isBonded ? -1 : 1;
881                 });
882         }
883 };
884
885 /**
886  * 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.
887  *
888  * @method saveBluetooth
889  */
890 Bluetooth.prototype.saveBluetooth = function() {
891         "use strict";
892         var devs = ko.toJS(this.devices().slice(0));
893
894         // clean bluetooth device objects from properties that do not need to be saved
895         for ( var i = 0; i < devs.length; ++i) {
896                 delete devs[i].connectToServiceByUUID;
897                 delete devs[i].uuids;
898                 var deviceClass = devs[i].deviceClass;
899                 delete devs[i].deviceClass;
900                 devs[i].deviceClass = {};
901                 if (!!deviceClass) {
902                         devs[i].deviceClass.major = deviceClass.major;
903                         devs[i].deviceClass.minor = deviceClass.minor;
904                 }
905         }
906         var newBluetoothConf = {
907                 devices : devs,
908                 lastSync : this.lastSync()
909         };
910         var savedBluetoothConf = Configuration.get("bluetooth");
911         var savedBluetoothConfStr = JSON.stringify(savedBluetoothConf);
912         //console.log(savedBluetoothConfStr);
913         var newBluetoothConfStr = JSON.stringify(newBluetoothConf);
914         //console.log(newBluetoothConfStr);
915         //console.log("SAME: ", savedBluetoothConfStr == newBluetoothConfStr ? "YES" : "NO");
916         if (!savedBluetoothConf || newBluetoothConfStr !== savedBluetoothConfStr) {
917                 Configuration.set("bluetooth", newBluetoothConf, false);
918         }
919 };
920
921 /**
922  * Loads Bluetooth device hardware address of phone marked as selected for incoming calls.
923  *
924  * @method loadSelectedRemoteDevice
925  */
926 Bluetooth.prototype.loadSelectedRemoteDevice = function() {
927         "use strict";
928         var self = this;
929         if (typeof (tizen.phone) !== 'undefined' && typeof (tizen.phone.getSelectedRemoteDevice) !== 'undefined') {
930                 try {
931                         tizen.phone.getSelectedRemoteDevice(function(selectedRemoteDev) {
932                                 console.log("selected remote device: ", selectedRemoteDev);
933                                 if (!!selectedRemoteDev && selectedRemoteDev !== "") {
934                                         self.selectedPhone(selectedRemoteDev);
935                                 } else {
936                                         self.selectedPhone(null);
937                                 }
938                         });
939                 } catch (err) {
940                         console.log("An error occured while loading selected remote device ", err);
941                         self.selectedPhone(null);
942                 }
943         }
944 };
945
946 /**
947  * Marks a given remote Bluetooth device as selected for incoming calls.
948  *
949  * @method selectRemoteDevice
950  * @param device {Object} Object representing remote Bluetooth device to be selected.
951  */
952 Bluetooth.prototype.selectRemoteDevice = function(device) {
953         "use strict";
954         var self = this;
955         console.log("selectRemoteDevice called", device);
956         if (!!device && !!device.address && !!device.deviceClass && device.deviceClass.major === tizen.bluetooth.deviceMajor.PHONE && typeof (tizen.phone) !== 'undefined' && typeof (tizen.phone.selectRemoteDevice) !== 'undefined') {
957                 showLoadingSpinner("Selecting");
958                 try {
959                         tizen.phone.selectRemoteDevice(device.address);
960                 } catch (err) {
961                         console.log("An error occured while selecting remote device ", err);
962                 }
963         } else {
964                 console.log("tizen.phone.selectRemoteDevice API not available or supplied device is not valid.");
965         }
966 };
967
968 /**
969  * Unmarks previously selected remote Bluetooth device for incoming calls.
970  *
971  * @method unselectRemoteDevice
972  */
973 Bluetooth.prototype.unselectRemoteDevice = function() {
974         "use strict";
975         var self = this;
976         console.log("unselectRemoteDevice called");
977         if (typeof (tizen.phone) !== 'undefined' && typeof (tizen.phone.unselectRemoteDevice) !== 'undefined') {
978                 try {
979                         tizen.phone.unselectRemoteDevice();
980                 } catch (err) {
981                         console.log("An error occured while unselecting remote device ", err);
982                 }
983         } else {
984                 console.log("tizen.phone.unselectRemoteDevice API not available.");
985         }
986 };
987
988 /**
989  * Sets the listener to receivce notifications when new remote Bluetooth device was marked as selected for incoming calls.
990  * @method registerSelectedRemoteDeviceChangeListener
991  */
992 Bluetooth.prototype.registerSelectedRemoteDeviceChangeListener = function() {
993         "use strict";
994         var self = this;
995
996         if (typeof (tizen.phone) !== 'undefined' && typeof (tizen.phone.addRemoteDeviceSelectedListener) !== 'undefined') {
997                 try {
998                         tizen.phone.addRemoteDeviceSelectedListener(function(result) {
999                                 console.log("addRemoteDeviceSelectedListener: ", result);
1000                                 if (!!result && !!result.error) {
1001                                         console.log("An error occured while selecting remote device: ", result.error);
1002                                         self.selectedPhone(null);
1003                                 } else if (!!result && !!result.value && result.value.toString().trim() !== "") {
1004                                         self.selectedPhone(result.value.toString().trim());
1005                                 } else {
1006                                         self.selectedPhone(null);
1007                                 }
1008                                 self._restartRefreshDevicesInterval();
1009                                 hideLoadingSpinner("Selecting");
1010                         });
1011                 } catch (err) {
1012                         console.log("An error occured while registering remote device selected listener ", err);
1013                 }
1014         } else {
1015                 console.log("tizen.phone.addRemoteDeviceSelectedListener API not available.");
1016         }
1017 };
1018
1019 /**
1020  * Reloads list of nearby and knwon remote Bluetooth devices and default local Bluetooth adapter.
1021  *
1022  * @method refreshDevices
1023  */
1024 Bluetooth.prototype.refreshDevices = function() {
1025         "use strict";
1026         var self = this;
1027         console.log("refreshDevices called");
1028         var devices = self.devices().slice(0);
1029         self.clearDevices();
1030         //self.loadDefaultAdapter();
1031         if (!!self.adapter() && self.adapter().powered) {
1032                 self.devices(devices);
1033         }
1034
1035         function updateDeviceInfo() {
1036                 for ( var i = 0; i < self.devices().length; ++i) {
1037                         var dev = self.devices()[i];
1038                         if (!!self.selectedDevice() && self.selectedDevice().address === dev.address) {
1039                                 self.selectedDevice(dev);
1040                         }
1041                         if (dev.toString().indexOf("BluetoothDevice") === -1) {
1042                                 self.getDevice(dev);
1043                         }
1044                 }
1045                 self.sortDevices();
1046                 self.saveBluetooth();
1047         }
1048
1049         if (self.devices().length) {
1050                 self.adapter().getKnownDevices(function(devs) {
1051                         if (devs.length) {
1052                                 for ( var i = 0; i < devs.length; ++i) {
1053                                         self.addUpdateDevice(devs[i], false);
1054                                 }
1055                         }
1056                         updateDeviceInfo();
1057                 }, function(error) {
1058                         console.log("Could not get known devices: ", error);
1059                         updateDeviceInfo();
1060                 });
1061         } else {
1062                 updateDeviceInfo();
1063         }
1064 };
1065
1066 Bluetooth.prototype._restartRefreshDevicesInterval = function() {
1067         "use strict";
1068         var self = this;
1069         self._clearRefreshDevicesInterval();
1070         self.refreshDevices();
1071         self._setRefreshDevicesInterval();
1072 };