Commit e8e6d1fc authored by mbrunson's avatar mbrunson Committed by Commit bot

bluetooth: Add adapter page to internals page.

Adds BluetoothAdapter::Observer callbacks to Adapter implementation
for tracking changes in Adapter state including:
  AdapterDiscoverableChanged
  AdapterPoweredChanged
  AdapterPresentChanged

Adds adapter page to display details about the current state of the adapter.
Adds ObjectFieldSet interface component for displaying properties of a JavaScript object.

Screenshot: https://goo.gl/photos/dCbsULiydMbiAtiJ9

BUG=651282
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:closure_compilation

Review-Url: https://codereview.chromium.org/2567983007
Cr-Commit-Position: refs/heads/master@{#443065}
parent db4968cf
...@@ -101,12 +101,14 @@ ...@@ -101,12 +101,14 @@
<include name="IDR_BLUETOOTH_UUID_MOJO_JS" file="${root_gen_dir}\device\bluetooth\public\interfaces\uuid.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" /> <include name="IDR_BLUETOOTH_UUID_MOJO_JS" file="${root_gen_dir}\device\bluetooth\public\interfaces\uuid.mojom.js" use_base_dir="false" type="BINDATA" compress="gzip" />
<include name="IDR_BLUETOOTH_INTERNALS_CSS" file="resources\bluetooth_internals\bluetooth_internals.css" type="BINDATA" compress="gzip" /> <include name="IDR_BLUETOOTH_INTERNALS_CSS" file="resources\bluetooth_internals\bluetooth_internals.css" type="BINDATA" compress="gzip" />
<include name="IDR_BLUETOOTH_INTERNALS_ADAPTER_BROKER_JS" file="resources\bluetooth_internals\adapter_broker.js" type="BINDATA" compress="gzip" /> <include name="IDR_BLUETOOTH_INTERNALS_ADAPTER_BROKER_JS" file="resources\bluetooth_internals\adapter_broker.js" type="BINDATA" compress="gzip" />
<include name="IDR_BLUETOOTH_INTERNALS_ADAPTER_PAGE_JS" file="resources\bluetooth_internals\adapter_page.js" type="BINDATA" compress="gzip" />
<include name="IDR_BLUETOOTH_INTERNALS_DEVICE_COLLECTION_JS" file="resources\bluetooth_internals\device_collection.js" type="BINDATA" compress="gzip" /> <include name="IDR_BLUETOOTH_INTERNALS_DEVICE_COLLECTION_JS" file="resources\bluetooth_internals\device_collection.js" type="BINDATA" compress="gzip" />
<include name="IDR_BLUETOOTH_INTERNALS_DEVICE_TABLE_JS" file="resources\bluetooth_internals\device_table.js" type="BINDATA" compress="gzip" /> <include name="IDR_BLUETOOTH_INTERNALS_DEVICE_TABLE_JS" file="resources\bluetooth_internals\device_table.js" type="BINDATA" compress="gzip" />
<include name="IDR_BLUETOOTH_INTERNALS_DEVICES_PAGE_JS" file="resources\bluetooth_internals\devices_page.js" type="BINDATA" compress="gzip" /> <include name="IDR_BLUETOOTH_INTERNALS_DEVICES_PAGE_JS" file="resources\bluetooth_internals\devices_page.js" type="BINDATA" compress="gzip" />
<include name="IDR_BLUETOOTH_INTERNALS_HTML" file="resources\bluetooth_internals\bluetooth_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" /> <include name="IDR_BLUETOOTH_INTERNALS_HTML" file="resources\bluetooth_internals\bluetooth_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" />
<include name="IDR_BLUETOOTH_INTERNALS_INTERFACES_JS" file="resources\bluetooth_internals\interfaces.js" type="BINDATA" compress="gzip" /> <include name="IDR_BLUETOOTH_INTERNALS_INTERFACES_JS" file="resources\bluetooth_internals\interfaces.js" type="BINDATA" compress="gzip" />
<include name="IDR_BLUETOOTH_INTERNALS_JS" file="resources\bluetooth_internals\bluetooth_internals.js" type="BINDATA" compress="gzip" /> <include name="IDR_BLUETOOTH_INTERNALS_JS" file="resources\bluetooth_internals\bluetooth_internals.js" type="BINDATA" compress="gzip" />
<include name="IDR_BLUETOOTH_INTERNALS_OBJECT_FIELDSET_JS" file="resources\bluetooth_internals\object_fieldset.js" type="BINDATA" compress="gzip" />
<include name="IDR_BLUETOOTH_INTERNALS_SIDEBAR_JS" file="resources\bluetooth_internals\sidebar.js" type="BINDATA" compress="gzip" /> <include name="IDR_BLUETOOTH_INTERNALS_SIDEBAR_JS" file="resources\bluetooth_internals\sidebar.js" type="BINDATA" compress="gzip" />
<include name="IDR_BLUETOOTH_INTERNALS_SNACKBAR_JS" file="resources\bluetooth_internals\snackbar.js" type="BINDATA" compress="gzip" /> <include name="IDR_BLUETOOTH_INTERNALS_SNACKBAR_JS" file="resources\bluetooth_internals\snackbar.js" type="BINDATA" compress="gzip" />
<include name="IDR_BOOKMARKS_MANIFEST" file="resources\bookmark_manager\manifest.json" type="BINDATA" /> <include name="IDR_BOOKMARKS_MANIFEST" file="resources\bookmark_manager\manifest.json" type="BINDATA" />
......
...@@ -19,7 +19,10 @@ cr.define('adapter_broker', function() { ...@@ -19,7 +19,10 @@ cr.define('adapter_broker', function() {
* @enum {string} * @enum {string}
*/ */
var AdapterProperty = { var AdapterProperty = {
DISCOVERABLE: 'discoverable',
DISCOVERING: 'discovering', DISCOVERING: 'discovering',
POWERED: 'powered',
PRESENT: 'present',
}; };
/** /**
...@@ -122,7 +125,49 @@ cr.define('adapter_broker', function() { ...@@ -122,7 +125,49 @@ cr.define('adapter_broker', function() {
AdapterClient.prototype = { AdapterClient.prototype = {
/** /**
* Fires adapterchanged event. * Fires adapterchanged event with "present" property.
* @param {boolean} present
*/
presentChanged: function(present) {
var event = new CustomEvent('adapterchanged', {
detail: {
property: AdapterProperty.PRESENT,
value: present,
}
});
this.adapterBroker_.dispatchEvent(event);
},
/**
* Fires adapterchanged event with "powered" property changed.
* @param {boolean} powered
*/
poweredChanged: function(powered) {
var event = new CustomEvent('adapterchanged', {
detail: {
property: AdapterProperty.POWERED,
value: powered,
}
});
this.adapterBroker_.dispatchEvent(event);
},
/**
* Fires adapterchanged event with "discoverable" property changed.
* @param {boolean} discoverable
*/
discoverableChanged: function(discoverable) {
var event = new CustomEvent('adapterchanged', {
detail: {
property: AdapterProperty.DISCOVERABLE,
value: discoverable,
}
});
this.adapterBroker_.dispatchEvent(event);
},
/**
* Fires adapterchanged event with "discovering" property changed.
* @param {boolean} discovering * @param {boolean} discovering
*/ */
discoveringChanged: function(discovering) { discoveringChanged: function(discovering) {
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* Javascript for AdapterPage, served from chrome://bluetooth-internals/.
*/
cr.define('adapter_page', function() {
/** @const */ var Page = cr.ui.pageManager.Page;
var PROPERTY_NAMES = {
address: 'Address',
name: 'Name',
initialized: 'Initialized',
present: 'Present',
powered: 'Powered',
discoverable: 'Discoverable',
discovering: 'Discovering',
};
/**
* Page that contains an ObjectFieldSet that displays the latest AdapterInfo.
* @constructor
* @extends {cr.ui.pageManager.Page}
*/
function AdapterPage() {
Page.call(this, 'adapter', 'Adapter', 'adapter');
this.adapterFieldSet = new object_fieldset.ObjectFieldSet();
this.adapterFieldSet.setPropertyDisplayNames(PROPERTY_NAMES);
this.pageDiv.appendChild(this.adapterFieldSet);
this.refreshBtn_ = $('adapter-refresh-btn');
this.refreshBtn_.addEventListener('click', function() {
this.refreshBtn_.disabled = true;
this.pageDiv.dispatchEvent(new CustomEvent('refreshpressed'));
}.bind(this));
}
AdapterPage.prototype = {
__proto__: Page.prototype,
/**
* Sets the information to display in fieldset.
* @param {!interfaces.BluetoothAdapter.AdapterInfo} info
*/
setAdapterInfo: function(info) {
this.adapterFieldSet.setObject(info);
this.refreshBtn_.disabled = false;
},
/**
* Redraws the fieldset displaying the adapter info.
*/
redraw: function() {
this.adapterFieldSet.redraw();
this.refreshBtn_.disabled = false;
},
};
return {
AdapterPage: AdapterPage,
};
});
...@@ -21,6 +21,18 @@ h1 { ...@@ -21,6 +21,18 @@ h1 {
color: rgb(92, 97, 102); color: rgb(92, 97, 102);
} }
.toggle-status {
background-image: url(../../../../ui/webui/resources/images/cancel_red.svg);
background-repeat: no-repeat;
min-height: 24px;
min-width: 24px;
}
.toggle-status.checked {
background-image:
url(../../../../ui/webui/resources/images/check_circle_green.svg);
}
/* Page container */ /* Page container */
#page-container { #page-container {
-webkit-margin-start: var(--sidebar-width); -webkit-margin-start: var(--sidebar-width);
...@@ -297,4 +309,27 @@ table .removed { ...@@ -297,4 +309,27 @@ table .removed {
opacity: 1; opacity: 1;
transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0);
visibility: visible; visibility: visible;
}
/* Adapter Page */
@media screen and (min-width: 601px) {
#adapter {
display: flex;
}
}
/* Object Fieldset */
.object-fieldset .status {
align-items: center;
display: flex;
margin-bottom: 0.8em;
}
.object-fieldset .status div:first-child {
-webkit-margin-end: 1em;
white-space: nowrap;
}
.object-fieldset .status:last-child {
margin-bottom: 0;
} }
\ No newline at end of file
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
<script src="snackbar.js"></script> <script src="snackbar.js"></script>
<script src="interfaces.js"></script> <script src="interfaces.js"></script>
<script src="adapter_broker.js"></script> <script src="adapter_broker.js"></script>
<script src="object_fieldset.js"></script>
<script src="adapter_page.js"></script>
<script src="device_collection.js"></script> <script src="device_collection.js"></script>
<script src="device_table.js"></script> <script src="device_table.js"></script>
<script src="devices_page.js"></script> <script src="devices_page.js"></script>
...@@ -35,6 +37,11 @@ ...@@ -35,6 +37,11 @@
<button id="menu-btn" class="custom-appearance"></button> <button id="menu-btn" class="custom-appearance"></button>
<h1 class="page-title"></h1> <h1 class="page-title"></h1>
</header> </header>
<section id="adapter" hidden>
<div class="header-extras">
<button id="adapter-refresh-btn">Refresh</button>
</div>
</section>
<section id="devices" hidden> <section id="devices" hidden>
<div class="header-extras"> <div class="header-extras">
<button id="scan-btn">Start Scan</button> <button id="scan-btn">Start Scan</button>
...@@ -50,7 +57,10 @@ ...@@ -50,7 +57,10 @@
</header> </header>
<nav> <nav>
<ul role="tablist"> <ul role="tablist">
<li class="selected" data-page-name="devices"> <li class="selected" data-page-name="adapter">
<button class="custom-appearance">Adapter</button>
</li>
<li data-page-name="devices">
<button class="custom-appearance">Devices</button> <button class="custom-appearance">Devices</button>
</li> </li>
</ul> </ul>
......
...@@ -13,6 +13,7 @@ var devices = null; ...@@ -13,6 +13,7 @@ var devices = null;
var sidebarObj = null; var sidebarObj = null;
cr.define('bluetooth_internals', function() { cr.define('bluetooth_internals', function() {
/** @const */ var AdapterPage = adapter_page.AdapterPage;
/** @const */ var DevicesPage = devices_page.DevicesPage; /** @const */ var DevicesPage = devices_page.DevicesPage;
/** @const */ var PageManager = cr.ui.pageManager.PageManager; /** @const */ var PageManager = cr.ui.pageManager.PageManager;
/** @const */ var Snackbar = snackbar.Snackbar; /** @const */ var Snackbar = snackbar.Snackbar;
...@@ -47,6 +48,8 @@ cr.define('bluetooth_internals', function() { ...@@ -47,6 +48,8 @@ cr.define('bluetooth_internals', function() {
/** @type {!device_collection.DeviceCollection} */ /** @type {!device_collection.DeviceCollection} */
devices = new device_collection.DeviceCollection([]); devices = new device_collection.DeviceCollection([]);
/** @type {adapter_page.AdapterPage} */
var adapterPage = null;
/** @type {devices_page.DevicesPage} */ /** @type {devices_page.DevicesPage} */
var devicesPage = null; var devicesPage = null;
...@@ -121,9 +124,11 @@ cr.define('bluetooth_internals', function() { ...@@ -121,9 +124,11 @@ cr.define('bluetooth_internals', function() {
} }
function setupAdapterSystem(response) { function setupAdapterSystem(response) {
console.log('adapter', response.info);
adapterBroker.addEventListener('adapterchanged', function(event) { adapterBroker.addEventListener('adapterchanged', function(event) {
adapterPage.adapterFieldSet.value[event.detail.property] =
event.detail.value;
adapterPage.redraw();
if (event.detail.property == adapter_broker.AdapterProperty.DISCOVERING && if (event.detail.property == adapter_broker.AdapterProperty.DISCOVERING &&
!event.detail.value && !userRequestedScanStop && discoverySession) { !event.detail.value && !userRequestedScanStop && discoverySession) {
updateStoppedDiscoverySession(); updateStoppedDiscoverySession();
...@@ -131,6 +136,14 @@ cr.define('bluetooth_internals', function() { ...@@ -131,6 +136,14 @@ cr.define('bluetooth_internals', function() {
'Discovery session ended unexpectedly', SnackbarType.WARNING); 'Discovery session ended unexpectedly', SnackbarType.WARNING);
} }
}); });
adapterPage.setAdapterInfo(response.info);
adapterPage.pageDiv.addEventListener('refreshpressed', function() {
adapterBroker.getInfo().then(function(response) {
adapterPage.setAdapterInfo(response.info);
});
});
} }
function setupDeviceSystem(response) { function setupDeviceSystem(response) {
...@@ -197,6 +210,8 @@ cr.define('bluetooth_internals', function() { ...@@ -197,6 +210,8 @@ cr.define('bluetooth_internals', function() {
devicesPage = new DevicesPage(); devicesPage = new DevicesPage();
PageManager.register(devicesPage); PageManager.register(devicesPage);
adapterPage = new AdapterPage();
PageManager.register(adapterPage);
// Set up hash-based navigation. // Set up hash-based navigation.
window.addEventListener('hashchange', function() { window.addEventListener('hashchange', function() {
...@@ -204,7 +219,7 @@ cr.define('bluetooth_internals', function() { ...@@ -204,7 +219,7 @@ cr.define('bluetooth_internals', function() {
}); });
if (!window.location.hash) { if (!window.location.hash) {
PageManager.showPageByName(devicesPage.name); PageManager.showPageByName(adapterPage.name);
return; return;
} }
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* Javascript for ObjectFieldSet, a UI element for displaying the properties
* of a given Javascript object. These properties are displayed in a fieldset
* as a series of rows for each key-value pair.
* Served from chrome://bluetooth-internals/.
*/
cr.define('object_fieldset', function() {
/**
* A fieldset that lists the properties of a given object. These properties
* are displayed as a series of rows for each key-value pair.
* Only the object's own properties are displayed. Boolean values are
* displayed using images: a green check for 'true', and a red cancel 'x' for
* 'false'. All other types are converted to their string representation for
* display.
* @constructor
* @extends {HTMLFieldSetElement}
*/
var ObjectFieldSet = cr.ui.define('fieldset');
ObjectFieldSet.prototype = {
__proto__: HTMLFieldSetElement.prototype,
/**
* Decorates the element as an ObjectFieldset.
*/
decorate: function() {
this.classList.add('object-fieldset');
/** @type {?Object} */
this.value = null;
/** @private {?Object<string, string>} */
this.nameMap_ = null;
},
/**
* Sets the object data to be displayed in the fieldset.
* @param {!Object} value
*/
setObject: function(value) {
this.value = value;
this.redraw();
},
/**
* Sets the object used to map property names to display names. If a display
* name is not provided, the default property name will be used.
* @param {!Object<string, string>} nameMap
*/
setPropertyDisplayNames: function(nameMap) {
this.nameMap_ = nameMap;
},
/**
* Deletes and recreates the table structure with current object data.
*/
redraw: function() {
this.innerHTML = '';
Object.keys(this.value).forEach(function(propName) {
var name = this.nameMap_[propName] || propName;
var value = this.value[propName];
var newField = document.createElement('div');
newField.classList.add('status');
var nameDiv = document.createElement('div');
nameDiv.textContent = name + ':';
newField.appendChild(nameDiv);
var valueDiv = document.createElement('div');
valueDiv.dataset.field = propName;
if (typeof(value) === 'boolean') {
valueDiv.classList.add('toggle-status');
valueDiv.classList.toggle('checked', value);
} else {
valueDiv.textContent = String(value);
}
newField.appendChild(valueDiv);
this.appendChild(newField);
}, this);
},
};
return {
ObjectFieldSet: ObjectFieldSet,
};
});
...@@ -18,6 +18,8 @@ BluetoothInternalsUI::BluetoothInternalsUI(content::WebUI* web_ui) ...@@ -18,6 +18,8 @@ BluetoothInternalsUI::BluetoothInternalsUI(content::WebUI* web_ui)
// Add required resources. // Add required resources.
html_source->AddResourcePath("adapter_broker.js", html_source->AddResourcePath("adapter_broker.js",
IDR_BLUETOOTH_INTERNALS_ADAPTER_BROKER_JS); IDR_BLUETOOTH_INTERNALS_ADAPTER_BROKER_JS);
html_source->AddResourcePath("adapter_page.js",
IDR_BLUETOOTH_INTERNALS_ADAPTER_PAGE_JS);
html_source->AddResourcePath("bluetooth_internals.css", html_source->AddResourcePath("bluetooth_internals.css",
IDR_BLUETOOTH_INTERNALS_CSS); IDR_BLUETOOTH_INTERNALS_CSS);
html_source->AddResourcePath("bluetooth_internals.js", html_source->AddResourcePath("bluetooth_internals.js",
...@@ -30,6 +32,8 @@ BluetoothInternalsUI::BluetoothInternalsUI(content::WebUI* web_ui) ...@@ -30,6 +32,8 @@ BluetoothInternalsUI::BluetoothInternalsUI(content::WebUI* web_ui)
IDR_BLUETOOTH_INTERNALS_DEVICES_PAGE_JS); IDR_BLUETOOTH_INTERNALS_DEVICES_PAGE_JS);
html_source->AddResourcePath("interfaces.js", html_source->AddResourcePath("interfaces.js",
IDR_BLUETOOTH_INTERNALS_INTERFACES_JS); IDR_BLUETOOTH_INTERNALS_INTERFACES_JS);
html_source->AddResourcePath("object_fieldset.js",
IDR_BLUETOOTH_INTERNALS_OBJECT_FIELDSET_JS);
html_source->AddResourcePath("sidebar.js", html_source->AddResourcePath("sidebar.js",
IDR_BLUETOOTH_INTERNALS_SIDEBAR_JS); IDR_BLUETOOTH_INTERNALS_SIDEBAR_JS);
html_source->AddResourcePath("snackbar.js", html_source->AddResourcePath("snackbar.js",
......
...@@ -203,10 +203,14 @@ BluetoothInternalsTest.prototype = { ...@@ -203,10 +203,14 @@ BluetoothInternalsTest.prototype = {
}; };
TEST_F('BluetoothInternalsTest', 'Startup_BluetoothInternals', function() { TEST_F('BluetoothInternalsTest', 'Startup_BluetoothInternals', function() {
/** @const */ var PageManager = cr.ui.pageManager.PageManager;
var adapterFactory = null; var adapterFactory = null;
var adapterFieldSet = null;
var deviceTable = null; var deviceTable = null;
var sidebarNode = null; var sidebarNode = null;
var fakeAdapterInfo = this.fakeAdapterInfo;
var fakeDeviceInfo1 = this.fakeDeviceInfo1; var fakeDeviceInfo1 = this.fakeDeviceInfo1;
var fakeDeviceInfo2 = this.fakeDeviceInfo2; var fakeDeviceInfo2 = this.fakeDeviceInfo2;
var fakeDeviceInfo3 = this.fakeDeviceInfo3; var fakeDeviceInfo3 = this.fakeDeviceInfo3;
...@@ -231,6 +235,7 @@ TEST_F('BluetoothInternalsTest', 'Startup_BluetoothInternals', function() { ...@@ -231,6 +235,7 @@ TEST_F('BluetoothInternalsTest', 'Startup_BluetoothInternals', function() {
}); });
setup(function() { setup(function() {
adapterFieldSet = document.querySelector('#adapter fieldset');
deviceTable = document.querySelector('#devices table'); deviceTable = document.querySelector('#devices table');
sidebarNode = document.querySelector('#sidebar'); sidebarNode = document.querySelector('#sidebar');
devices.splice(0, devices.length); devices.splice(0, devices.length);
...@@ -242,6 +247,7 @@ TEST_F('BluetoothInternalsTest', 'Startup_BluetoothInternals', function() { ...@@ -242,6 +247,7 @@ TEST_F('BluetoothInternalsTest', 'Startup_BluetoothInternals', function() {
adapterFactory.reset(); adapterFactory.reset();
sidebarObj.close(); sidebarObj.close();
snackbar.Snackbar.dismiss(true); snackbar.Snackbar.dismiss(true);
PageManager.registeredPages['adapter'].setAdapterInfo(fakeAdapterInfo());
}); });
/** /**
...@@ -411,7 +417,7 @@ TEST_F('BluetoothInternalsTest', 'Startup_BluetoothInternals', function() { ...@@ -411,7 +417,7 @@ TEST_F('BluetoothInternalsTest', 'Startup_BluetoothInternals', function() {
var sidebarItems = Array.from( var sidebarItems = Array.from(
sidebarNode.querySelectorAll('.sidebar-content li')); sidebarNode.querySelectorAll('.sidebar-content li'));
['devices'].forEach(function(pageName) { ['adapter', 'devices'].forEach(function(pageName) {
expectTrue(sidebarItems.some(function(item) { expectTrue(sidebarItems.some(function(item) {
return item.dataset.pageName === pageName; return item.dataset.pageName === pageName;
})); }));
...@@ -545,8 +551,56 @@ TEST_F('BluetoothInternalsTest', 'Startup_BluetoothInternals', function() { ...@@ -545,8 +551,56 @@ TEST_F('BluetoothInternalsTest', 'Startup_BluetoothInternals', function() {
expectFalse(!!snackbar.Snackbar.current_); expectFalse(!!snackbar.Snackbar.current_);
}).then(finishSnackbarTest); }).then(finishSnackbarTest);
}); });
});
/* AdapterPage Tests */
function checkAdapterFieldSet(adapterInfo) {
for (var propName in adapterInfo) {
var valueCell = adapterFieldSet.querySelector(
'[data-field="' + propName + '"]');
var value = adapterInfo[propName];
if (typeof(value) === 'boolean') {
expectEquals(value, valueCell.classList.contains('checked'));
} else if (typeof(value) === 'string') {
expectEquals(value, valueCell.textContent);
} else {
assert('boolean or string type expected but got ' + typeof(value));
}
}
}
test('AdapterPage_DefaultState', function() {
checkAdapterFieldSet(adapterFieldSet.value);
});
test('AdapterPage_AdapterChanged', function() {
var adapterInfo = adapterFieldSet.value;
adapterInfo.present = !adapterInfo.present;
adapterBroker.adapterClient_.presentChanged(adapterInfo.present);
checkAdapterFieldSet(adapterInfo);
adapterInfo.discovering = !adapterInfo.discovering;
adapterBroker.adapterClient_.discoveringChanged(adapterInfo.discovering);
checkAdapterFieldSet(adapterInfo);
});
test('AdapterPage_AdapterChanged_RepeatTwice', function() {
var adapterInfo = adapterFieldSet.value;
adapterInfo.present = !adapterInfo.present;
adapterBroker.adapterClient_.presentChanged(adapterInfo.present);
checkAdapterFieldSet(adapterInfo);
adapterBroker.adapterClient_.presentChanged(adapterInfo.present);
checkAdapterFieldSet(adapterInfo);
adapterInfo.discovering = !adapterInfo.discovering;
adapterBroker.adapterClient_.discoveringChanged(adapterInfo.discovering);
checkAdapterFieldSet(adapterInfo);
adapterBroker.adapterClient_.discoveringChanged(adapterInfo.discovering);
checkAdapterFieldSet(adapterInfo);
});
});
// Run all registered tests. // Run all registered tests.
mocha.run(); mocha.run();
......
...@@ -79,6 +79,24 @@ void Adapter::StartDiscoverySession( ...@@ -79,6 +79,24 @@ void Adapter::StartDiscoverySession(
weak_ptr_factory_.GetWeakPtr(), callback)); weak_ptr_factory_.GetWeakPtr(), callback));
} }
void Adapter::AdapterPresentChanged(device::BluetoothAdapter* adapter,
bool present) {
if (client_)
client_->PresentChanged(present);
}
void Adapter::AdapterPoweredChanged(device::BluetoothAdapter* adapter,
bool powered) {
if (client_)
client_->PoweredChanged(powered);
}
void Adapter::AdapterDiscoverableChanged(device::BluetoothAdapter* adapter,
bool discoverable) {
if (client_)
client_->DiscoverableChanged(discoverable);
}
void Adapter::AdapterDiscoveringChanged(device::BluetoothAdapter* adapter, void Adapter::AdapterDiscoveringChanged(device::BluetoothAdapter* adapter,
bool discovering) { bool discovering) {
if (client_) if (client_)
......
...@@ -37,6 +37,12 @@ class Adapter : public mojom::Adapter, ...@@ -37,6 +37,12 @@ class Adapter : public mojom::Adapter,
const StartDiscoverySessionCallback& callback) override; const StartDiscoverySessionCallback& callback) override;
// device::BluetoothAdapter::Observer overrides: // device::BluetoothAdapter::Observer overrides:
void AdapterPresentChanged(device::BluetoothAdapter* adapter,
bool present) override;
void AdapterPoweredChanged(device::BluetoothAdapter* adapter,
bool powered) override;
void AdapterDiscoverableChanged(device::BluetoothAdapter* adapter,
bool discoverable) override;
void AdapterDiscoveringChanged(device::BluetoothAdapter* adapter, void AdapterDiscoveringChanged(device::BluetoothAdapter* adapter,
bool discovering) override; bool discovering) override;
void DeviceAdded(device::BluetoothAdapter* adapter, void DeviceAdded(device::BluetoothAdapter* adapter,
......
...@@ -44,4 +44,4 @@ class DiscoverySession : public mojom::DiscoverySession { ...@@ -44,4 +44,4 @@ class DiscoverySession : public mojom::DiscoverySession {
} // namespace bluetooth } // namespace bluetooth
#endif #endif
\ No newline at end of file
...@@ -82,6 +82,15 @@ interface Adapter { ...@@ -82,6 +82,15 @@ interface Adapter {
}; };
interface AdapterClient { interface AdapterClient {
// Called when the presence of the adapter changes.
PresentChanged(bool present);
// Called when the radio power state of the adapter changes.
PoweredChanged(bool powered);
// Called when the discoverability state of the adapter changes.
DiscoverableChanged(bool discoverable);
// Called when the discovering state of the adapter changes. // Called when the discovering state of the adapter changes.
DiscoveringChanged(bool discovering); DiscoveringChanged(bool discovering);
......
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24" fill="#DB4437">
<path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24" fill="#0F9D58">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
</svg>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment