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
|
* `callback` Function
|
||||||
* `deviceId` string
|
* `deviceId` string
|
||||||
|
|
||||||
Emitted when bluetooth device needs to be selected on call to
|
Emitted when a bluetooth device needs to be selected when a call to
|
||||||
`navigator.bluetooth.requestDevice`. To use `navigator.bluetooth` api
|
`navigator.bluetooth.requestDevice` is made. `callback` should be called with
|
||||||
`webBluetooth` should be enabled. If `event.preventDefault` is not called,
|
the `deviceId` of the device to be selected. Passing an empty string to
|
||||||
first available device will be selected. `callback` should be called with
|
`callback` will cancel the request.
|
||||||
`deviceId` to be selected, passing 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'
|
```javascript title='main.js'
|
||||||
const { app, BrowserWindow } = require('electron')
|
const { app, BrowserWindow } = require('electron')
|
||||||
|
|
||||||
let win = null
|
let win = null
|
||||||
app.commandLine.appendSwitch('enable-experimental-web-platform-features')
|
|
||||||
|
|
||||||
app.whenReady().then(() => {
|
app.whenReady().then(() => {
|
||||||
win = new BrowserWindow({ width: 800, height: 600 })
|
win = new BrowserWindow({ width: 800, height: 600 })
|
||||||
|
@ -800,6 +804,9 @@ app.whenReady().then(() => {
|
||||||
return device.deviceName === 'test'
|
return device.deviceName === 'test'
|
||||||
})
|
})
|
||||||
if (!result) {
|
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('')
|
callback('')
|
||||||
} else {
|
} else {
|
||||||
callback(result.deviceId)
|
callback(result.deviceId)
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
<h1>Web Bluetooth API</h1>
|
<h1>Web Bluetooth API</h1>
|
||||||
|
|
||||||
<button id="clickme">Test Bluetooth</button>
|
<button id="clickme">Test Bluetooth</button>
|
||||||
|
<button id="cancel">Cancel Bluetooth Request</button>
|
||||||
|
|
||||||
<p>Currently selected bluetooth device: <strong id="device-name""></strong></p>
|
<p>Currently selected bluetooth device: <strong id="device-name""></strong></p>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
const {app, BrowserWindow, ipcMain} = require('electron')
|
const {app, BrowserWindow, ipcMain} = require('electron')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
|
||||||
|
let bluetoothPinCallback
|
||||||
|
let selectBluetoothCallback
|
||||||
|
|
||||||
function createWindow () {
|
function createWindow () {
|
||||||
const mainWindow = new BrowserWindow({
|
const mainWindow = new BrowserWindow({
|
||||||
width: 800,
|
width: 800,
|
||||||
|
@ -12,11 +15,23 @@ function createWindow () {
|
||||||
|
|
||||||
mainWindow.webContents.on('select-bluetooth-device', (event, deviceList, callback) => {
|
mainWindow.webContents.on('select-bluetooth-device', (event, deviceList, callback) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
if (deviceList && deviceList.length > 0) {
|
selectBluetoothCallback = callback
|
||||||
callback(deviceList[0].deviceId)
|
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.
|
// Listen for a message from the renderer to get the response for the Bluetooth pairing.
|
||||||
ipcMain.on('bluetooth-pairing-response', (event, response) => {
|
ipcMain.on('bluetooth-pairing-response', (event, response) => {
|
||||||
bluetoothPinCallback(response)
|
bluetoothPinCallback(response)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const { contextBridge, ipcRenderer } = require('electron')
|
const { contextBridge, ipcRenderer } = require('electron')
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld('electronAPI', {
|
contextBridge.exposeInMainWorld('electronAPI', {
|
||||||
|
cancelBluetoothRequest: (callback) => ipcRenderer.send('cancel-bluetooth-request', callback),
|
||||||
bluetoothPairingRequest: (callback) => ipcRenderer.on('bluetooth-pairing-request', callback),
|
bluetoothPairingRequest: (callback) => ipcRenderer.on('bluetooth-pairing-request', callback),
|
||||||
bluetoothPairingResponse: (response) => ipcRenderer.send('bluetooth-pairing-response', response)
|
bluetoothPairingResponse: (response) => ipcRenderer.send('bluetooth-pairing-response', response)
|
||||||
})
|
})
|
|
@ -7,6 +7,12 @@ async function testIt() {
|
||||||
|
|
||||||
document.getElementById('clickme').addEventListener('click',testIt)
|
document.getElementById('clickme').addEventListener('click',testIt)
|
||||||
|
|
||||||
|
function cancelRequest() {
|
||||||
|
window.electronAPI.cancelBluetoothRequest()
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('cancel').addEventListener('click', cancelRequest)
|
||||||
|
|
||||||
window.electronAPI.bluetoothPairingRequest((event, details) => {
|
window.electronAPI.bluetoothPairingRequest((event, details) => {
|
||||||
const response = {}
|
const response = {}
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,6 @@ namespace electron {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
const int kMaxScanRetries = 5;
|
|
||||||
|
|
||||||
void OnDeviceChosen(const content::BluetoothChooser::EventHandler& handler,
|
void OnDeviceChosen(const content::BluetoothChooser::EventHandler& handler,
|
||||||
const std::string& device_id) {
|
const std::string& device_id) {
|
||||||
if (device_id.empty()) {
|
if (device_id.empty()) {
|
||||||
|
@ -66,29 +64,15 @@ void BluetoothChooser::SetAdapterPresence(AdapterPresence presence) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void BluetoothChooser::ShowDiscoveryState(DiscoveryState state) {
|
void BluetoothChooser::ShowDiscoveryState(DiscoveryState state) {
|
||||||
|
bool idle_state = false;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case DiscoveryState::FAILED_TO_START:
|
case DiscoveryState::FAILED_TO_START:
|
||||||
refreshing_ = false;
|
refreshing_ = false;
|
||||||
event_handler_.Run(content::BluetoothChooserEvent::CANCELLED, "");
|
event_handler_.Run(content::BluetoothChooserEvent::CANCELLED, "");
|
||||||
break;
|
return;
|
||||||
case DiscoveryState::IDLE:
|
case DiscoveryState::IDLE:
|
||||||
refreshing_ = false;
|
refreshing_ = false;
|
||||||
if (device_map_.empty()) {
|
idle_state = true;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case DiscoveryState::DISCOVERING:
|
case DiscoveryState::DISCOVERING:
|
||||||
// The first time this state fires is due to a rescan triggering so set a
|
// 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;
|
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,
|
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_;
|
std::map<std::string, std::u16string> device_map_;
|
||||||
api::WebContents* api_web_contents_;
|
api::WebContents* api_web_contents_;
|
||||||
EventHandler event_handler_;
|
EventHandler event_handler_;
|
||||||
int num_retries_ = 0;
|
|
||||||
bool refreshing_ = false;
|
bool refreshing_ = false;
|
||||||
bool rescan_ = false;
|
bool rescan_ = false;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue