fix: allow cancelling of bluetooth requests (#37601)
* fix: allow cancelling of bluetooth requests allows cancelling of bluetooth requests when no devices present * docs: update docs to reflect how bluetooth works.
This commit is contained in:
parent
42e7cd9b3f
commit
6a6908c4c8
7 changed files with 56 additions and 31 deletions
|
@ -777,20 +777,24 @@ Returns:
|
|||
* `callback` Function
|
||||
* `deviceId` string
|
||||
|
||||
Emitted when bluetooth device needs to be selected on call to
|
||||
`navigator.bluetooth.requestDevice`. To use `navigator.bluetooth` api
|
||||
`webBluetooth` should be enabled. If `event.preventDefault` is not called,
|
||||
first available device will be selected. `callback` should be called with
|
||||
`deviceId` to be selected, passing empty string to `callback` will
|
||||
cancel the request.
|
||||
Emitted when a bluetooth device needs to be selected when a call to
|
||||
`navigator.bluetooth.requestDevice` is made. `callback` should be called with
|
||||
the `deviceId` of the device to be selected. Passing an empty string to
|
||||
`callback` will cancel the request.
|
||||
|
||||
If no event listener is added for this event, all bluetooth requests will be cancelled.
|
||||
If an event listener is not added for this event, or if `event.preventDefault`
|
||||
is not called when handling this event, the first available device will be
|
||||
automatically selected.
|
||||
|
||||
Due to the nature of bluetooth, scanning for devices when
|
||||
`navigator.bluetooth.requestDevice` is called may take time and will cause
|
||||
`select-bluetooth-device` to fire multiple times until `callback` is called
|
||||
with either a device id or an empty string to cancel the request.
|
||||
|
||||
```javascript title='main.js'
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
let win = null
|
||||
app.commandLine.appendSwitch('enable-experimental-web-platform-features')
|
||||
|
||||
app.whenReady().then(() => {
|
||||
win = new BrowserWindow({ width: 800, height: 600 })
|
||||
|
@ -800,6 +804,9 @@ app.whenReady().then(() => {
|
|||
return device.deviceName === 'test'
|
||||
})
|
||||
if (!result) {
|
||||
// The device wasn't found so we need to either wait longer (eg until the
|
||||
// device is turned on) or cancel the request by calling the callback
|
||||
// with an empty string.
|
||||
callback('')
|
||||
} else {
|
||||
callback(result.deviceId)
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
<h1>Web Bluetooth API</h1>
|
||||
|
||||
<button id="clickme">Test Bluetooth</button>
|
||||
<button id="cancel">Cancel Bluetooth Request</button>
|
||||
|
||||
<p>Currently selected bluetooth device: <strong id="device-name""></strong></p>
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
const {app, BrowserWindow, ipcMain} = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
let bluetoothPinCallback
|
||||
let selectBluetoothCallback
|
||||
|
||||
function createWindow () {
|
||||
const mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
|
@ -12,11 +15,23 @@ function createWindow () {
|
|||
|
||||
mainWindow.webContents.on('select-bluetooth-device', (event, deviceList, callback) => {
|
||||
event.preventDefault()
|
||||
if (deviceList && deviceList.length > 0) {
|
||||
callback(deviceList[0].deviceId)
|
||||
}
|
||||
selectBluetoothCallback = callback
|
||||
const result = deviceList.find((device) => {
|
||||
return device.deviceName === 'test'
|
||||
})
|
||||
if (result) {
|
||||
callback(result.deviceId)
|
||||
} else {
|
||||
// The device wasn't found so we need to either wait longer (eg until the
|
||||
// device is turned on) or until the user cancels the request
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('cancel-bluetooth-request', (event) => {
|
||||
selectBluetoothCallback('')
|
||||
})
|
||||
|
||||
|
||||
// Listen for a message from the renderer to get the response for the Bluetooth pairing.
|
||||
ipcMain.on('bluetooth-pairing-response', (event, response) => {
|
||||
bluetoothPinCallback(response)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const { contextBridge, ipcRenderer } = require('electron')
|
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
cancelBluetoothRequest: (callback) => ipcRenderer.send('cancel-bluetooth-request', callback),
|
||||
bluetoothPairingRequest: (callback) => ipcRenderer.on('bluetooth-pairing-request', callback),
|
||||
bluetoothPairingResponse: (response) => ipcRenderer.send('bluetooth-pairing-response', response)
|
||||
})
|
|
@ -7,6 +7,12 @@ async function testIt() {
|
|||
|
||||
document.getElementById('clickme').addEventListener('click',testIt)
|
||||
|
||||
function cancelRequest() {
|
||||
window.electronAPI.cancelBluetoothRequest()
|
||||
}
|
||||
|
||||
document.getElementById('cancel').addEventListener('click', cancelRequest)
|
||||
|
||||
window.electronAPI.bluetoothPairingRequest((event, details) => {
|
||||
const response = {}
|
||||
|
||||
|
|
|
@ -27,8 +27,6 @@ namespace electron {
|
|||
|
||||
namespace {
|
||||
|
||||
const int kMaxScanRetries = 5;
|
||||
|
||||
void OnDeviceChosen(const content::BluetoothChooser::EventHandler& handler,
|
||||
const std::string& device_id) {
|
||||
if (device_id.empty()) {
|
||||
|
@ -66,29 +64,15 @@ void BluetoothChooser::SetAdapterPresence(AdapterPresence presence) {
|
|||
}
|
||||
|
||||
void BluetoothChooser::ShowDiscoveryState(DiscoveryState state) {
|
||||
bool idle_state = false;
|
||||
switch (state) {
|
||||
case DiscoveryState::FAILED_TO_START:
|
||||
refreshing_ = false;
|
||||
event_handler_.Run(content::BluetoothChooserEvent::CANCELLED, "");
|
||||
break;
|
||||
return;
|
||||
case DiscoveryState::IDLE:
|
||||
refreshing_ = false;
|
||||
if (device_map_.empty()) {
|
||||
auto event = ++num_retries_ > kMaxScanRetries
|
||||
? content::BluetoothChooserEvent::CANCELLED
|
||||
: content::BluetoothChooserEvent::RESCAN;
|
||||
event_handler_.Run(event, "");
|
||||
} else {
|
||||
bool prevent_default = api_web_contents_->Emit(
|
||||
"select-bluetooth-device", GetDeviceList(),
|
||||
base::BindOnce(&OnDeviceChosen, event_handler_));
|
||||
if (!prevent_default) {
|
||||
auto it = device_map_.begin();
|
||||
auto device_id = it->first;
|
||||
event_handler_.Run(content::BluetoothChooserEvent::SELECTED,
|
||||
device_id);
|
||||
}
|
||||
}
|
||||
idle_state = true;
|
||||
break;
|
||||
case DiscoveryState::DISCOVERING:
|
||||
// The first time this state fires is due to a rescan triggering so set a
|
||||
|
@ -101,6 +85,18 @@ void BluetoothChooser::ShowDiscoveryState(DiscoveryState state) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
bool prevent_default =
|
||||
api_web_contents_->Emit("select-bluetooth-device", GetDeviceList(),
|
||||
base::BindOnce(&OnDeviceChosen, event_handler_));
|
||||
if (!prevent_default && idle_state) {
|
||||
if (device_map_.empty()) {
|
||||
event_handler_.Run(content::BluetoothChooserEvent::CANCELLED, "");
|
||||
} else {
|
||||
auto it = device_map_.begin();
|
||||
auto device_id = it->first;
|
||||
event_handler_.Run(content::BluetoothChooserEvent::SELECTED, device_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BluetoothChooser::AddOrUpdateDevice(const std::string& device_id,
|
||||
|
|
|
@ -44,7 +44,6 @@ class BluetoothChooser : public content::BluetoothChooser {
|
|||
std::map<std::string, std::u16string> device_map_;
|
||||
api::WebContents* api_web_contents_;
|
||||
EventHandler event_handler_;
|
||||
int num_retries_ = 0;
|
||||
bool refreshing_ = false;
|
||||
bool rescan_ = false;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue