VulpesOS/gaia/apps/bluetooth/js/views/device_picker_panel.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

413 lines
13 KiB
JavaScript
Executable file

/*
* DevicePickerPanel is responsible for:
* - Render paired/found devices list in this panel.
* - Emit 'devicePicked' event with device address while a user click on a
* paired device.
* - Pair with the picked device while a user click on a found device.
* Emit 'devicePicked' event with device address until the picked device
* is pared.
* - Emit 'cancelSelection' event while a user click close button on the
* header for leaving.
*
* @module DevicePickerPanel
*/
define(function(require) {
'use strict';
var BtContext = require('modules/bluetooth/bluetooth_context');
var BtTemplateFactory = require('views/bt_template_factory');
var ListView = require('modules/mvvm/list_view');
var _debug = false;
var debug = function() {};
if (_debug) {
debug = function btdpp_debug(msg) {
console.log('--> [DevicePickerPanel]: ' + msg);
};
}
var DevicePickerPanel = {
/**
* A instance to maintain paired devices list view.
*
* @access private
* @memberOf DevicePickerPanel
* @type {Object}
*/
_pairedDevicesListView: null,
/**
* A instance to maintain found devices list view.
*
* @access private
* @memberOf DevicePickerPanel
* @type {Object}
*/
_foundDevicesListView: null,
/**
* Construct elements.
*
* @access private
* @memberOf DevicePickerPanel
* @type {Object}
*/
_elements: {
view: {
viewDevicePicker: document.querySelector('.devices-list-view')
},
header: {
headerActionClose: document.querySelector('.devices-list-header')
},
paired: {
pairedDevicesHeader: document.querySelector('.bluetooth-paired-title'),
pairedDevicesList: document.querySelector('.bluetooth-paired-devices')
},
found: {
foundDevicesHeader: document.querySelector('.bluetooth-found-title'),
foundDevicesList: document.querySelector('.bluetooth-devices')
},
search: {
searchingItem: document.querySelector('.bluetooth-searching'),
searchItem: document.querySelector('.bluetooth-search'),
searchButton: document.querySelector('.search-device')
}
},
/**
* The object maintains listeners' callback per property name.
* Each listener would be called as following definition.
* 'devicePicked' - be called when device is picked and paired successfully.
* 'cancelSelection': - be called when user click on close button on header.
*
* @memberOf DevicePickerPanel
* @type {Object}
*/
_listeners: {
'devicePicked': [],
'cancelSelection': []
},
/**
* Default adapter of Bluetooth.
*
* @access private
* @memberOf DevicePickerPanel
* @type {Object} BluetoothAdapter
*/
_defaultAdapter: null,
/**
* The function init the paired/found devices list and search button.
*
* @access private
* @memberOf DevicePickerPanel
*/
_init: function btdpp__init() {
// Bind click event for header.
this._elements.header.headerActionClose.addEventListener('action',
this._onHeaderActionCloseClick.bind(this));
// Bind click event for search button.
this._elements.search.searchButton.addEventListener('click',
this._onSearchButtonClick.bind(this));
// Bind click event for paired device item.
var pairedDeviceTemplate =
BtTemplateFactory('paired', this._onPairedDeviceItemClick.bind(this));
// Create paired devices list view.
this._pairedDevicesListView =
ListView(this._elements.paired.pairedDevicesList,
BtContext.getPairedDevices(),
pairedDeviceTemplate);
// Bind click event for found device item.
var foundDeviceTemplate =
BtTemplateFactory('remote', this._onFoundDeviceItemClick.bind(this));
// Create found devices list view.
this._foundDevicesListView =
ListView(this._elements.found.foundDevicesList,
BtContext.getRemoteDevices(),
foundDeviceTemplate);
// Init items state.
this._initItemsState();
// search devices automatically while the panel is inited.
BtContext.startDiscovery().then(() => {
debug('_init(): startDiscovery successfully');
}, (reason) => {
debug('_init(): startDiscovery failed, ' +
'reason = ' + reason);
});
},
/**
* The function init these elements which are displayed in this panel.
*
* @access private
* @memberOf DevicePickerPanel
*/
_initItemsState: function btdpp__initItemsState() {
// paired devices header
BtContext.observe('hasPairedDevice',
this._updatePairedDevicesHeader.bind(this));
this._updatePairedDevicesHeader(BtContext.hasPairedDevice);
// paired devices list
BtContext.observe('state', this._updatePairedDevicesList.bind(this));
this._updatePairedDevicesList(BtContext.state);
// found devices list
BtContext.observe('state', this._updateFoundDevicesList.bind(this));
this._updateFoundDevicesList(BtContext.state);
this._pairedDevicesListView.enabled = true;
this._foundDevicesListView.enabled = true;
// search
BtContext.observe('state', this._updateSearchItem.bind(this));
this._updateSearchItem(BtContext.state);
BtContext.observe('discovering', this._updateSearchingItem.bind(this));
this._updateSearchingItem(BtContext.discovering);
},
/**
* Start discovery devices while the search button is clicked.
*
* @access private
* @memberOf DevicePickerPanel
*/
_onHeaderActionCloseClick: function btdpp__onHeaderActionCloseClick() {
debug('_onHeaderActionCloseClick():');
// Emit 'cancelSelection' event to outer modules.
var event = {
type: 'cancelSelection'
};
this._emitEvent(event);
},
/**
* Start discovery devices while the search button is clicked.
*
* @access private
* @memberOf DevicePickerPanel
*/
_onSearchButtonClick: function btdpp__onSearchButtonClick() {
debug('_onSearchButtonClick():');
BtContext.startDiscovery().then(() => {
debug('_onSearchButtonClick(): startDiscovery successfully');
}, (reason) => {
debug('_onSearchButtonClick(): startDiscovery failed, ' +
'reason = ' + reason);
});
},
/**
* While the paired device is clicked, emit event to outer modules with
* the device item.
*
* @access private
* @memberOf DevicePickerPanel
*/
_onPairedDeviceItemClick:
function btdpp__onPairedDeviceItemClick(deviceItem) {
debug('_onPairedDeviceItemClick(): deviceItem.address = ' +
deviceItem.address);
debug('_onPairedDeviceItemClick(): deviceItem.paired = ' +
deviceItem.paired);
// Emit 'devicePicked' event to outer modules with the device item.
var event = {
type: 'devicePicked',
detail: {
address: deviceItem.address
}
};
this._emitEvent(event);
},
/**
* Pair with the clicked device. While the device is paired successfully,
* emit event to outer modules with the device item.
*
* @access private
* @memberOf DevicePickerPanel
*/
_onFoundDeviceItemClick:
function btdpp__onFoundDeviceItemClick(deviceItem) {
debug('_onFoundDeviceItemClick(): deviceItem.address = ' +
deviceItem.address);
// Update device pairing status first.
deviceItem.paired = 'pairing';
// Pair with the remote device.
BtContext.pair(deviceItem.address).then(() => {
debug('_onFoundDeviceItemClick(): pair successfully');
// Emit 'devicePicked' event to outer modules with the
// paired device item.
var event = {
type: 'devicePicked',
detail: {
address: deviceItem.address
}
};
this._emitEvent(event);
}, (reason) => {
debug('_onFoundDeviceItemClick(): pair failed, ' +
'reason = ' + reason);
// Reset the paired status back to false,
// since the 'pairing' status is given in Gaia side.
deviceItem.paired = false;
// Show alert message while pair device failed.
this._alertPairFailedErrorMessage(reason);
});
},
/**
* Show an alert dialog since the device is failed in pairing process.
*
* @access private
* @memberOf DevicePickerPanel
*/
_alertPairFailedErrorMessage:
function btdpp__alertPairFailedErrorMessage(errorReason) {
var _ = navigator.mozL10n.get;
var msg = _('error-pair-title'); // This is a default message.
if (errorReason === 'Repeated Attempts') {
msg = msg + '\n' + _('error-pair-toofast');
} else if (errorReason === 'Authentication Failed') {
msg = msg + '\n' + _('error-pair-pincode');
}
window.alert(msg);
},
/**
* A function to show/hide paired devices header.
*
* @access private
* @memberOf DevicePickerPanel
*/
_updatePairedDevicesHeader:
function btdpp__updatePairedDevicesHeader(hasPairedDevice) {
debug('_updatePairedDevicesHeader(): ' +
'callback from observe "hasPairedDevice" = ' + hasPairedDevice);
this._elements.paired.pairedDevicesHeader.hidden =
!(hasPairedDevice && (BtContext.state === 'enabled'));
},
/**
* A function to show/hide paired devices list.
*
* @access private
* @memberOf DevicePickerPanel
*/
_updatePairedDevicesList: function btdpp__updatePairedDevicesList(state) {
debug('_updatePairedDevicesList(): ' +
'callback from observe "state" = ' + state);
this._elements.paired.pairedDevicesHeader.hidden =
!((state === 'enabled') && (BtContext.hasPairedDevice));
this._elements.paired.pairedDevicesList.hidden = (state !== 'enabled');
},
/**
* A function to show/hide found devices list.
*
* @access private
* @memberOf DevicePickerPanel
*/
_updateFoundDevicesList: function btdpp__updateFoundDevicesList(state) {
debug('_updateFoundDevicesList(): ' +
'callback from observe "state" = ' + state);
this._elements.found.foundDevicesHeader.hidden = (state !== 'enabled');
this._elements.found.foundDevicesList.hidden = (state !== 'enabled');
},
/**
* A function to show/hide search item.
*
* @access private
* @memberOf DevicePickerPanel
*/
_updateSearchItem: function btdpp__updateSearchItem(state) {
debug('_updateSearchItem(): ' +
'callback from observe "state" = ' + state);
this._elements.search.searchItem.hidden = (state !== 'enabled');
},
/**
* A function to show/hide searching description item.
*
* @access private
* @memberOf DevicePickerPanel
*/
_updateSearchingItem: function btdpp__updateSearchingItem(discovering) {
debug('_updateSearchingItem(): ' +
'callback from observe "discovering" = ' + discovering);
this._elements.search.searchingItem.hidden = !discovering;
this._elements.search.searchButton.disabled = discovering;
},
/**
* A function to emit event to each registered listener by the input type.
*
* @memberOf DevicePickerPanel
* @param {Object} options
* @param {String} options.type - type of event name
* @param {Object} options.detail - the object pass to the listener
*/
_emitEvent: function btdpp__emitEvent(options) {
this._listeners[options.type].forEach(function(listener) {
listener(options);
});
},
/**
* The method will provide event listener for outer modules to regist.
*
* @access public
* @memberOf DevicePickerPanel
* @param {String} eventName
* @param {Function} callback
*/
addEventListener: function btdpp_addEventListener(eventName, callback) {
if (callback && (this._listeners.hasOwnProperty(eventName))) {
this._listeners[eventName].push(callback);
}
},
/**
* The method will provide event listener for outer modules to un-regist.
*
* @access public
* @memberOf DevicePickerPanel
* @param {String} eventName
* @param {Function} callback
*/
removeEventListener:
function btdpp_removeEventListener(eventName, callback) {
if (callback && (this._listeners.hasOwnProperty(eventName))) {
var index = this._listeners[eventName].indexOf(callback);
if (index >= 0) {
this._listeners[eventName].splice(index, 1);
}
}
},
/**
* The setter will show/hide the device picker view for outer modules.
*
* @access public
* @memberOf DevicePickerPanel
* @param {Boolean} enabled
*/
set visible(enabled) {
// display device picker view
this._elements.view.viewDevicePicker.hidden = !enabled;
}
};
DevicePickerPanel._init();
return DevicePickerPanel;
});