Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
322 lines
7.4 KiB
JavaScript
322 lines
7.4 KiB
JavaScript
/* exported BluetoothHelper */
|
|
|
|
'use strict';
|
|
|
|
var BluetoothHelper = function() {
|
|
var profiles = {
|
|
'HFP': 0x111E,
|
|
'A2DP': 0x110D
|
|
};
|
|
|
|
var _bluetooth = window.navigator.mozBluetooth;
|
|
var _isReady = false;
|
|
var _callbacks = [];
|
|
|
|
var _adapter = null;
|
|
var _oldAdapter = null; // The varaible is accessed for v1 only.
|
|
var _v2 = true;
|
|
|
|
var _ready = function(callback) {
|
|
if (!callback || !_bluetooth) {
|
|
return;
|
|
}
|
|
|
|
if (_isReady) {
|
|
callback();
|
|
} else {
|
|
_callbacks.push(callback);
|
|
}
|
|
};
|
|
|
|
var _handleRequest = function(request, callback, errorcb) {
|
|
request.onsuccess = function() {
|
|
if (callback) {
|
|
callback(request.result);
|
|
}
|
|
};
|
|
|
|
request.onerror = function() {
|
|
console.log('Error handling bluetooth request');
|
|
if (errorcb) {
|
|
errorcb();
|
|
}
|
|
};
|
|
};
|
|
|
|
// run callbacks when adapter is ready
|
|
var _processCallbacks = function() {
|
|
if (_adapter) {
|
|
_isReady = true;
|
|
|
|
_callbacks.forEach(function(callback) {
|
|
callback();
|
|
});
|
|
// clean up the _callback queue
|
|
_callbacks = [];
|
|
} else {
|
|
// We can do nothing without default adapter.
|
|
console.log('BluetoothHelper(): connot get default adapter yet');
|
|
}
|
|
};
|
|
|
|
// API v2 get adapter via bluetooth
|
|
var _fetchAdapterV2 = function() {
|
|
// need time to get bluetooth adapter at first run
|
|
_bluetooth.onattributechanged = function onManagerAttributeChanged(evt) {
|
|
for (var i in evt.attrs) {
|
|
switch (evt.attrs[i]) {
|
|
case 'defaultAdapter':
|
|
console.log('defaultAdapter changed. address:',
|
|
_bluetooth.defaultAdapter.address);
|
|
_adapter = _bluetooth.defaultAdapter;
|
|
_processCallbacks();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
_adapter = _bluetooth.defaultAdapter;
|
|
if (_adapter) {
|
|
_processCallbacks();
|
|
}
|
|
};
|
|
|
|
// API v1 get adapter via bluetooth
|
|
var _fetchAdapter = function() {
|
|
var req = _bluetooth.getDefaultAdapter();
|
|
if (req) {
|
|
req.onsuccess = function() {
|
|
if (_adapter) {
|
|
_oldAdapter = _adapter;
|
|
}
|
|
|
|
_isReady = true;
|
|
_adapter = req.result;
|
|
|
|
// Put the callback function of onpairedstatuschanged to the new adapter
|
|
// because the new adapter won't remember those callback function which
|
|
// is registered before. In other word, we get a new adpater after
|
|
// turned on/off Bluetooth. The new adapter have no registered callback.
|
|
if (_oldAdapter && _oldAdapter.onpairedstatuschanged) {
|
|
_adapter.onpairedstatuschanged = _oldAdapter.onpairedstatuschanged;
|
|
}
|
|
|
|
_callbacks.forEach(function(callback) {
|
|
callback();
|
|
});
|
|
// clean up the _callback queue
|
|
_callbacks = [];
|
|
};
|
|
|
|
req.onerror = function() {
|
|
// We can do nothing without default adapter.
|
|
console.log('BluetoothHelper(): connot get default adapter!!!');
|
|
};
|
|
}
|
|
};
|
|
|
|
var _getAdapter = function() {
|
|
if (_v2) {
|
|
_fetchAdapterV2();
|
|
} else {
|
|
_fetchAdapter();
|
|
}
|
|
};
|
|
|
|
var _resetAdapter = function() {
|
|
if (_adapter) {
|
|
_oldAdapter = _adapter;
|
|
}
|
|
// clean up state and adapter
|
|
_isReady = false;
|
|
_adapter = null;
|
|
};
|
|
|
|
// init
|
|
if (_bluetooth) {
|
|
// detect API version
|
|
if (typeof(_bluetooth.onattributechanged) === 'undefined') {
|
|
_v2 = false;
|
|
}
|
|
|
|
if (_v2) {
|
|
_bluetooth.onadapteradded = function onAdapterAdded(evt) {
|
|
_getAdapter();
|
|
};
|
|
} else {
|
|
_bluetooth.addEventListener('enabled', _getAdapter);
|
|
_bluetooth.addEventListener('adapteradded', _getAdapter);
|
|
_bluetooth.addEventListener('disabled', _resetAdapter);
|
|
}
|
|
_getAdapter();
|
|
}
|
|
|
|
return {
|
|
profiles: profiles,
|
|
|
|
answerWaitingCall: function() {
|
|
_ready(function() {
|
|
_adapter.answerWaitingCall();
|
|
});
|
|
},
|
|
|
|
ignoreWaitingCall: function() {
|
|
_ready(function() {
|
|
_adapter.ignoreWaitingCall();
|
|
});
|
|
},
|
|
|
|
toggleCalls: function() {
|
|
_ready(function() {
|
|
_adapter.toggleCalls();
|
|
});
|
|
},
|
|
|
|
getConnectedDevicesByProfile: function(profileID, cb) {
|
|
_ready(function() {
|
|
_handleRequest(_adapter.getConnectedDevices(profileID), cb);
|
|
});
|
|
},
|
|
|
|
connectSco: function(cb) {
|
|
_ready(function() {
|
|
_handleRequest(_adapter.connectSco(), cb);
|
|
});
|
|
},
|
|
|
|
disconnectSco: function(cb) {
|
|
_ready(function() {
|
|
_handleRequest(_adapter.disconnectSco(), cb);
|
|
});
|
|
},
|
|
|
|
getPairedDevices: function(cb) {
|
|
_ready(function() {
|
|
_handleRequest(_adapter.getPairedDevices(), cb);
|
|
});
|
|
},
|
|
|
|
getAddress: function(cb) {
|
|
if (_v2) {
|
|
console.log('getAddress function is deprecated');
|
|
return;
|
|
}
|
|
|
|
_ready(function() {
|
|
var address = _adapter.address;
|
|
cb(address);
|
|
});
|
|
},
|
|
|
|
setPairingConfirmation: function(address, confirmed) {
|
|
if (_v2) {
|
|
console.log('setPairingConfirmation API is deprecated');
|
|
return;
|
|
}
|
|
|
|
_ready(function() {
|
|
_adapter.setPairingConfirmation(address, confirmed);
|
|
});
|
|
},
|
|
|
|
setPinCode: function(address, pincode) {
|
|
if (_v2) {
|
|
console.log('setPairingConfirmation API is deprecated');
|
|
return;
|
|
}
|
|
|
|
_ready(function() {
|
|
_adapter.setPinCode(address, pincode);
|
|
});
|
|
},
|
|
|
|
setPasskey: function(address, key) {
|
|
if (_v2) {
|
|
console.log('setPairingConfirmation API is deprecated');
|
|
return;
|
|
}
|
|
|
|
_ready(function() {
|
|
_adapter.setPasskey(address, key);
|
|
});
|
|
},
|
|
|
|
isScoConnected: function(cb, errorcb) {
|
|
_ready(function() {
|
|
_handleRequest(_adapter.isScoConnected(), cb, errorcb);
|
|
});
|
|
},
|
|
|
|
sendMediaMetaData: function(metadata, cb, errorcb) {
|
|
_ready(function() {
|
|
_handleRequest(_adapter.sendMediaMetaData(metadata), cb, errorcb);
|
|
});
|
|
},
|
|
|
|
sendMediaPlayStatus: function(metadata, cb, errorcb) {
|
|
_ready(function() {
|
|
_handleRequest(_adapter.sendMediaPlayStatus(metadata), cb, errorcb);
|
|
});
|
|
},
|
|
|
|
set onhfpstatuschanged(callback) {
|
|
_ready(function() {
|
|
_adapter.onhfpstatuschanged = callback;
|
|
});
|
|
},
|
|
|
|
set onscostatuschanged(callback) {
|
|
_ready(function() {
|
|
_adapter.onscostatuschanged = callback;
|
|
});
|
|
},
|
|
|
|
set ona2dpstatuschanged(callback) {
|
|
_ready(function() {
|
|
_adapter.ona2dpstatuschanged = callback;
|
|
});
|
|
},
|
|
|
|
set onrequestmediaplaystatus(callback) {
|
|
_ready(function() {
|
|
_adapter.onrequestmediaplaystatus = callback;
|
|
});
|
|
},
|
|
|
|
set onpairedstatuschanged(callback) {
|
|
if (_v2) {
|
|
console.log('onpairedstatuschanged API is deprecated');
|
|
return;
|
|
}
|
|
|
|
_ready(function() {
|
|
_adapter.onpairedstatuschanged = callback;
|
|
});
|
|
},
|
|
|
|
v2: _v2, // expose API version for app reference
|
|
|
|
// bypass the enable/disable state if works in APIv1
|
|
enable: function() {
|
|
if (_v2) {
|
|
_ready(function() {
|
|
_adapter.enable();
|
|
});
|
|
} else {
|
|
console.log('enable is not support in v1 API!');
|
|
}
|
|
},
|
|
|
|
disable: function() {
|
|
if (_v2) {
|
|
_ready(function() {
|
|
_adapter.disable();
|
|
});
|
|
} else {
|
|
console.log('disable is not support in v1 API!');
|
|
}
|
|
}
|
|
};
|
|
};
|