diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java index bf96dea..b9dca9d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java
@@ -691,17 +691,11 @@ private MediaMetadataCompat createMetadata() { MediaMetadataCompat.Builder metadataBuilder = new MediaMetadataCompat.Builder(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, - mMediaNotificationInfo.metadata.getTitle()); - metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, - mMediaNotificationInfo.origin); - } else { - metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, - mMediaNotificationInfo.metadata.getTitle()); - metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, - mMediaNotificationInfo.origin); - } + metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, + mMediaNotificationInfo.metadata.getTitle()); + metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, + mMediaNotificationInfo.origin); + if (!TextUtils.isEmpty(mMediaNotificationInfo.metadata.getArtist())) { metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, mMediaNotificationInfo.metadata.getArtist());
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp index 2ed9ea5..d10d5ca 100644 --- a/chrome/app/settings_strings.grdp +++ b/chrome/app/settings_strings.grdp
@@ -2744,6 +2744,23 @@ <message name="IDS_SETTINGS_STORAGE_DELETE_ALL_BUTTON_TITLE" desc="In storage manager confirmation dialog, label for the button to delete offline files."> Delete files </message> + + <!-- Power --> + <message name="IDS_SETTINGS_POWER_SOURCE_LABEL" desc="The label for the power source dropdown in the Power overlay."> + Power source + </message> + <message name="IDS_SETTINGS_POWER_SOURCE_BATTERY" desc="The text referring to the battery as the power source."> + Battery + </message> + <message name="IDS_SETTINGS_POWER_SOURCE_AC_ADAPTER" desc="The text referring to a dedicated charger like an AC adapter as the power source."> + AC adapter + </message> + <message name="IDS_SETTINGS_POWER_SOURCE_LOW_POWER_CHARGER" desc="The text referring to a low-power charger like a USB charger as the power source."> + Low-power charger + </message> + <message name="IDS_SETTINGS_POWER_SOURCE_CALCULATING" desc="The description in the 'Power' overlay when the power status is being determined."> + Checking... + </message> </if> <!-- System Page -->
diff --git a/chrome/browser/resources/settings/device_page/device_page.html b/chrome/browser/resources/settings/device_page/device_page.html index feb35dc..e12ef3b6 100644 --- a/chrome/browser/resources/settings/device_page/device_page.html +++ b/chrome/browser/resources/settings/device_page/device_page.html
@@ -1,4 +1,5 @@ <link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/html/md_select_css.html"> <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> @@ -19,7 +20,7 @@ <dom-module id="settings-device-page"> <template> - <style include="settings-shared"></style> + <style include="settings-shared md-select"></style> <settings-animated-pages id="pages" section="device"> <neon-animatable id="main" route-path="default"> <div id="pointersRow" class="settings-box first" @@ -57,6 +58,35 @@ <div class="middle">$i18n{storageTitle}</div> <button class="subpage-arrow" is="paper-icon-button-light"></button> </div> + <template is="dom-if" if="[[enablePowerSettings_]]"> + <div id="powerRow" class="settings-box two-line"> + <iron-icon icon="[[batteryIcon_]]"></iron-icon> + <div class="middle"> + <div>[[powerLabel_]]</div> + <div class="secondary">[[batteryStatus_.statusText]]</div> + </div> + <div class="md-select-wrapper" + hidden$="[[!showPowerDropdown_]]"> + <select id="powerSource" class="md-select" + on-change="onPowerSourceChange_"> + <option value="" + selected$="[[isEqual_('', selectedPowerSourceId_)]]"> + $i18n{powerSourceBattery} + </option> + <template is="dom-repeat" items="[[powerSources_]]"> + <option value="[[item.id]]" + selected$="[[isEqual_(item.id, selectedPowerSourceId_)]]"> + [[item.description]] + </option> + </template> + </select> + <span class="md-select-underline"></span> + </div> + <div hidden$="[[showPowerDropdown_]]"> + [[powerSourceName_]] + </div> + </div> + </template> </neon-animatable> <template is="dom-if" route-path="/pointer-overlay"> <settings-subpage
diff --git a/chrome/browser/resources/settings/device_page/device_page.js b/chrome/browser/resources/settings/device_page/device_page.js index 18f39843..5ec0105a 100644 --- a/chrome/browser/resources/settings/device_page/device_page.js +++ b/chrome/browser/resources/settings/device_page/device_page.js
@@ -39,6 +39,62 @@ }, readOnly: true, }, + + /** + * Whether power status and settings should be fetched and displayed. + * @private + */ + enablePowerSettings_: { + type: Boolean, + value: function() { + return loadTimeData.getBoolean('enablePowerSettings'); + }, + readOnly: true, + }, + + /** @private {string} ID of the selected power source, or ''. */ + selectedPowerSourceId_: String, + + /** @private {!settings.BatteryStatus|undefined} */ + batteryStatus_: Object, + + /** @private {boolean} Whether a low-power (USB) charger is being used. */ + lowPowerCharger_: Boolean, + + /** + * List of available dual-role power sources, if enablePowerSettings_ is on. + * @private {!Array<!settings.PowerSource>|undefined} + */ + powerSources_: Array, + + /** @private */ + batteryIcon_: { + type: String, + computed: 'computeBatteryIcon_(batteryStatus_, lowPowerCharger_)', + value: 'settings:battery-unknown', + }, + + /** @private */ + powerLabel_: { + type: String, + computed: 'computePowerLabel_(powerSources_, batteryStatus_.calculating)', + }, + + /** @private */ + showPowerDropdown_: { + type: Boolean, + computed: 'computeShowPowerDropdown_(powerSources_)', + value: false, + }, + + /** + * The name of the dedicated charging device being used, if present. + * @private {string} + */ + powerSourceName_: { + type: String, + computed: 'computePowerSourceName_(powerSources_, lowPowerCharger_)', + }, }, observers: [ @@ -46,16 +102,20 @@ ], /** @override */ - ready: function() { - settings.DevicePageBrowserProxyImpl.getInstance().initializePointers(); - }, - - /** @override */ attached: function() { this.addWebUIListener( 'has-mouse-changed', this.set.bind(this, 'hasMouse_')); this.addWebUIListener( 'has-touchpad-changed', this.set.bind(this, 'hasTouchpad_')); + settings.DevicePageBrowserProxyImpl.getInstance().initializePointers(); + + if (this.enablePowerSettings_) { + this.addWebUIListener( + 'battery-status-changed', this.set.bind(this, 'batteryStatus_')); + this.addWebUIListener( + 'power-sources-changed', this.powerSourcesChanged_.bind(this)); + settings.DevicePageBrowserProxyImpl.getInstance().updatePowerStatus(); + } }, /** @@ -85,6 +145,89 @@ }, /** + * @param {*} lhs + * @param {*} rhs + * @return {boolean} + */ + isEqual_: function(lhs, rhs) { + return lhs === rhs; + }, + + /** + * @param {!settings.BatteryStatus} batteryStatus + * @param {boolean} lowPowerCharger + * @return {string} + */ + computeBatteryIcon_: function(batteryStatus, lowPowerCharger) { + var iconPrefix = 'settings:battery-'; + + if (batteryStatus.calculating) + return iconPrefix + 'unknown'; + + if (lowPowerCharger) + return iconPrefix + 'unreliable'; + + if (!batteryStatus.charging && batteryStatus.percent < 5) + return iconPrefix + 'alert'; + + // Compute the rest of the icon: iconPrefix + '[charging-]<percent>'. + if (batteryStatus.charging) + iconPrefix += 'charging-'; + + // Show the highest level icon that doesn't go over the actual percentage. + if (batteryStatus.percent < 30) + return iconPrefix + '20'; + if (batteryStatus.percent < 50) + return iconPrefix + '30'; + if (batteryStatus.percent < 60) + return iconPrefix + '50'; + if (batteryStatus.percent < 80) + return iconPrefix + '60'; + if (batteryStatus.percent < 90) + return iconPrefix + '80'; + if (batteryStatus.percent < 100) + return iconPrefix + '90'; + return iconPrefix + 'full'; + }, + + /** + * @param {!Array<!settings.PowerSource>|undefined} powerSources + * @param {boolean} calculating + * @return {string} The primary label for the power row. + * @private + */ + computePowerLabel_: function(powerSources, calculating) { + return this.i18n(calculating ? 'calculatingPower' : + powerSources.length ? 'powerSourceLabel' : 'powerSourceBattery'); + }, + + /** + * @param {!Array<!settings.PowerSource>} powerSources + * @return {boolean} True if at least one power source is attached and all of + * them are dual-role (no dedicated chargers). + * @private + */ + computeShowPowerDropdown_: function(powerSources) { + return powerSources.length > 0 && powerSources.every(function(source) { + return source.type == settings.PowerDeviceType.DUAL_ROLE_USB; + }); + }, + + /** + * @param {!Array<!settings.PowerSource>} powerSources + * @param {boolean} lowPowerCharger + * @return {string} Description of the power source. + * @private + */ + computePowerSourceName_(powerSources, lowPowerCharger) { + if (lowPowerCharger) + return this.i18n('powerSourceLowPowerCharger'); + if (powerSources.length) + return this.i18n('powerSourceAcAdapter'); + return ''; + }, + + /** * Handler for tapping the mouse and touchpad settings menu item. * @private */ @@ -124,6 +267,11 @@ settings.navigateTo(settings.Route.STORAGE); }, + onPowerSourceChange_: function() { + settings.DevicePageBrowserProxyImpl.getInstance().setPowerSource( + this.$$('#powerSource').value); + }, + /** @protected */ currentRouteChanged: function() { this.checkPointerSubpage_(); @@ -140,6 +288,19 @@ }, /** + * @param {!Array<settings.PowerSource>} sources External power sources. + * @param {string} selectedId The ID of the currently used power source. + * @param {boolean} lowPowerCharger Whether the currently used power source + * is a low-powered USB charger. + * @private + */ + powerSourcesChanged_: function(sources, selectedId, lowPowerCharger) { + this.powerSources_ = sources; + this.selectedPowerSourceId_ = selectedId; + this.lowPowerCharger_ = lowPowerCharger; + }, + + /** * Leaves the pointer subpage if all pointing devices are detached. * @private */
diff --git a/chrome/browser/resources/settings/device_page/device_page_browser_proxy.js b/chrome/browser/resources/settings/device_page/device_page_browser_proxy.js index 4a3f62c2a..0f7ed4db 100644 --- a/chrome/browser/resources/settings/device_page/device_page_browser_proxy.js +++ b/chrome/browser/resources/settings/device_page/device_page_browser_proxy.js
@@ -3,6 +3,36 @@ // found in the LICENSE file. /** @fileoverview A helper object used for testing the Device page. */ +cr.exportPath('settings'); + +/** + * Mirrors DeviceType from ash/common/system/chromeos/power/power_status.h. + * @enum {number} + */ +settings.PowerDeviceType = { + DEDICATED_CHARGER: 0, + DUAL_ROLE_USB: 1, +}; + +/** + * @typedef {{ + * id: string, + * type: settings.PowerDeviceType, + * description: string + * }} + */ +settings.PowerSource; + +/** + * @typedef {{ + * charging: boolean, + * calculating: boolean, + * percent: number, + * statusText: string, + * }} + */ +settings.BatteryStatus; + cr.define('settings', function() { /** @interface */ function DevicePageBrowserProxy() {} @@ -23,6 +53,16 @@ /** Shows the Ash keyboard shortcuts overlay. */ showKeyboardShortcutsOverlay: function() {}, + + /** Requests a power status update. */ + updatePowerStatus: function() {}, + + /** + * Sets the ID of the power source to use. + * @param {string} powerSourceId ID of the power source. '' denotes the + * battery (no external power source). + */ + setPowerSource: function(powerSourceId) {}, }; /** @@ -55,6 +95,16 @@ showKeyboardShortcutsOverlay: function() { chrome.send('showKeyboardShortcutsOverlay'); }, + + /** @override */ + updatePowerStatus: function() { + chrome.send('updatePowerStatus'); + }, + + /** @override */ + setPowerSource: function(powerSourceId) { + chrome.send('setPowerSource', [powerSourceId]); + }, }; return {
diff --git a/chrome/browser/resources/settings/icons.html b/chrome/browser/resources/settings/icons.html index 5041e948..e88824d9 100644 --- a/chrome/browser/resources/settings/icons.html +++ b/chrome/browser/resources/settings/icons.html
@@ -18,6 +18,12 @@ <path fill="none" d="M1 1h22v22H1z"></path> </g> + <!-- Battery shell from Polymer plus unreliable badge from Ash. --> + <g id="battery-unreliable"> + <path d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v15.33C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V5.33C17 4.6 16.4 4 15.67 4z"></path> + <path fill="#fff" d="M10.755 11.99c-.99.01-1.25.5-1.25.5v-.985S9.775 11 10.76 11c.985 0 1.5.975 2.525.975S14.5 11.5 14.5 11.5v.99s-.19.525-1.22.525-1.535-1.04-2.53-1.025z"></path> + </g> + <!-- These icons are copied from Polymer's iron-icons and kept in sorted order. See http://goo.gl/Y1OdAq for instructions on adding additional icons. @@ -31,7 +37,22 @@ <g id="arrow-back"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"></path></g> <g id="assignment"><path d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm2 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z"></path></g> <if expr="chromeos"> + <g id="battery-20"><path d="M7 17v3.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V17H7z"></path><path fill-opacity=".3" d="M17 5.33C17 4.6 16.4 4 15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V17h10V5.33z"></path></g> + <g id="battery-30"><path fill-opacity=".3" d="M17 5.33C17 4.6 16.4 4 15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V15h10V5.33z"></path><path d="M7 15v5.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V15H7z"></path></g> + <g id="battery-50"><path fill-opacity=".3" d="M17 5.33C17 4.6 16.4 4 15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V13h10V5.33z"></path><path d="M7 13v7.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V13H7z"></path></g> + <g id="battery-60"><path fill-opacity=".3" d="M17 5.33C17 4.6 16.4 4 15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V11h10V5.33z"></path><path d="M7 11v9.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V11H7z"></path></g> + <g id="battery-80"><path fill-opacity=".3" d="M17 5.33C17 4.6 16.4 4 15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V9h10V5.33z"></path><path d="M7 9v11.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V9H7z"></path></g> + <g id="battery-90"><path fill-opacity=".3" d="M17 5.33C17 4.6 16.4 4 15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V8h10V5.33z"></path><path d="M7 8v12.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V8H7z"></path></g> + <g id="battery-alert"><path d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v15.33C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V5.33C17 4.6 16.4 4 15.67 4zM13 18h-2v-2h2v2zm0-4h-2V9h2v5z"></path></g> + <g id="battery-charging-20"><path d="M11 20v-3H7v3.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V17h-4.4L11 20z"></path><path fill-opacity=".3" d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V17h4v-2.5H9L13 7v5.5h2L12.6 17H17V5.33C17 4.6 16.4 4 15.67 4z"></path></g> + <g id="battery-charging-30"><path fill-opacity=".3" d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v9.17h2L13 7v5.5h2l-1.07 2H17V5.33C17 4.6 16.4 4 15.67 4z"></path><path d="M11 20v-5.5H7v6.17C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V14.5h-3.07L11 20z"></path></g> + <g id="battery-charging-50"><path d="M14.47 13.5L11 20v-5.5H9l.53-1H7v7.17C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V13.5h-2.53z"></path><path fill-opacity=".3" d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v8.17h2.53L13 7v5.5h2l-.53 1H17V5.33C17 4.6 16.4 4 15.67 4z"></path></g> + <g id="battery-charging-60"><path fill-opacity=".3" d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V11h3.87L13 7v4h4V5.33C17 4.6 16.4 4 15.67 4z"></path><path d="M13 12.5h2L11 20v-5.5H9l1.87-3.5H7v9.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V11h-4v1.5z"></path></g> + <g id="battery-charging-80"><path fill-opacity=".3" d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V9h4.93L13 7v2h4V5.33C17 4.6 16.4 4 15.67 4z"></path><path d="M13 12.5h2L11 20v-5.5H9L11.93 9H7v11.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V9h-4v3.5z"></path></g> + <g id="battery-charging-90"><path fill-opacity=".3" d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33V8h5.47L13 7v1h4V5.33C17 4.6 16.4 4 15.67 4z"></path><path d="M13 12.5h2L11 20v-5.5H9L12.47 8H7v12.67C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V8h-4v4.5z"></path></g> <g id="battery-charging-full"><path d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v15.33C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V5.33C17 4.6 16.4 4 15.67 4zM11 20v-5.5H9L13 7v5.5h2L11 20z"></path></g> + <g id="battery-full"><path d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v15.33C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V5.33C17 4.6 16.4 4 15.67 4z"></path></g> + <g id="battery-unknown"><path d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v15.33C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V5.33C17 4.6 16.4 4 15.67 4zm-2.72 13.95h-1.9v-1.9h1.9v1.9zm1.35-5.26s-.38.42-.67.71c-.48.48-.83 1.15-.83 1.6h-1.6c0-.83.46-1.52.93-2l.93-.94c.27-.27.44-.65.44-1.06 0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5H9c0-1.66 1.34-3 3-3s3 1.34 3 3c0 .66-.27 1.26-.7 1.69z"></path></g> <g id="bluetooth"><path d="M17.71 7.71L12 2h-1v7.59L6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 11 14.41V22h1l5.71-5.71-4.3-4.29 4.3-4.29zM13 5.83l1.88 1.88L13 9.59V5.83zm1.88 10.46L13 18.17v-3.76l1.88 1.88z"></path></g> <g id="bluetooth-connected"><path d="M7 12l-2-2-2 2 2 2 2-2zm10.71-4.29L12 2h-1v7.59L6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 11 14.41V22h1l5.71-5.71-4.3-4.29 4.3-4.29zM13 5.83l1.88 1.88L13 9.59V5.83zm1.88 10.46L13 18.17v-3.76l1.88 1.88zM19 10l-2 2 2 2 2-2-2-2z"></path></g> <g id="bluetooth-disabled"><path d="M13 5.83l1.88 1.88-1.6 1.6 1.41 1.41 3.02-3.02L12 2h-1v5.03l2 2v-3.2zM5.41 4L4 5.41 10.59 12 5 17.59 6.41 19 11 14.41V22h1l4.29-4.29 2.3 2.29L20 18.59 5.41 4zM13 18.17v-3.76l1.88 1.88L13 18.17z"></path></g>
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index db7302c..7488e79 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -1094,6 +1094,8 @@ "webui/settings/chromeos/device_keyboard_handler.h", "webui/settings/chromeos/device_pointer_handler.cc", "webui/settings/chromeos/device_pointer_handler.h", + "webui/settings/chromeos/device_power_handler.cc", + "webui/settings/chromeos/device_power_handler.h", "webui/settings/chromeos/device_storage_handler.cc", "webui/settings/chromeos/device_storage_handler.h", "webui/settings/chromeos/easy_unlock_settings_handler.cc",
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_power_handler.cc b/chrome/browser/ui/webui/settings/chromeos/device_power_handler.cc new file mode 100644 index 0000000..4d4340d --- /dev/null +++ b/chrome/browser/ui/webui/settings/chromeos/device_power_handler.cc
@@ -0,0 +1,140 @@ +// 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. + +#include "chrome/browser/ui/webui/settings/chromeos/device_power_handler.h" + +#include <memory> +#include <utility> + +#include "ash/resources/grit/ash_resources.h" +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/grit/generated_resources.h" +#include "content/public/browser/web_ui.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/l10n/time_format.h" +#include "ui/base/webui/web_ui_util.h" + +namespace chromeos { +namespace settings { +namespace { + +base::string16 GetBatteryTimeText(base::TimeDelta time_left) { + int hour = 0; + int min = 0; + ash::PowerStatus::SplitTimeIntoHoursAndMinutes(time_left, &hour, &min); + + base::string16 time_text; + if (hour == 0 || min == 0) { + // Display only one unit ("2 hours" or "10 minutes"). + return ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION, + ui::TimeFormat::LENGTH_LONG, time_left); + } + + return ui::TimeFormat::Detailed(ui::TimeFormat::FORMAT_DURATION, + ui::TimeFormat::LENGTH_LONG, + -1, // force hour and minute output + time_left); +} + +} // namespace + +PowerHandler::PowerHandler() { + power_status_ = ash::PowerStatus::Get(); +} + +PowerHandler::~PowerHandler() {} + +void PowerHandler::RegisterMessages() { + web_ui()->RegisterMessageCallback( + "updatePowerStatus", base::Bind(&PowerHandler::HandleUpdatePowerStatus, + base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "setPowerSource", + base::Bind(&PowerHandler::HandleSetPowerSource, base::Unretained(this))); +} + +void PowerHandler::OnJavascriptAllowed() { + power_status_->AddObserver(this); +} + +void PowerHandler::OnJavascriptDisallowed() { + power_status_->RemoveObserver(this); +} + +void PowerHandler::OnPowerStatusChanged() { + SendBatteryStatus(); + SendPowerSources(); +} + +void PowerHandler::HandleUpdatePowerStatus(const base::ListValue* args) { + AllowJavascript(); + power_status_->RequestStatusUpdate(); +} + +void PowerHandler::HandleSetPowerSource(const base::ListValue* args) { + AllowJavascript(); + + std::string id; + CHECK(args->GetString(0, &id)); + power_status_->SetPowerSource(id); +} + +void PowerHandler::SendBatteryStatus() { + bool charging = power_status_->IsBatteryCharging(); + bool calculating = power_status_->IsBatteryTimeBeingCalculated(); + int percent = power_status_->GetRoundedBatteryPercent(); + base::TimeDelta time_left; + bool show_time = false; + + if (!calculating) { + time_left = charging ? power_status_->GetBatteryTimeToFull() + : power_status_->GetBatteryTimeToEmpty(); + show_time = ash::PowerStatus::ShouldDisplayBatteryTime(time_left); + } + + base::string16 status_text; + if (show_time) { + status_text = l10n_util::GetStringFUTF16( + charging ? IDS_OPTIONS_BATTERY_STATUS_CHARGING + : IDS_OPTIONS_BATTERY_STATUS, + base::IntToString16(percent), GetBatteryTimeText(time_left)); + } else { + status_text = l10n_util::GetStringFUTF16(IDS_OPTIONS_BATTERY_STATUS_SHORT, + base::IntToString16(percent)); + } + + base::DictionaryValue battery_dict; + battery_dict.SetBoolean("charging", charging); + battery_dict.SetBoolean("calculating", calculating); + battery_dict.SetInteger("percent", percent); + battery_dict.SetString("statusText", status_text); + + CallJavascriptFunction("cr.webUIListenerCallback", + base::StringValue("battery-status-changed"), + battery_dict); +} + +void PowerHandler::SendPowerSources() { + base::ListValue sources_list; + for (const auto& source : power_status_->GetPowerSources()) { + std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); + dict->SetString("id", source.id); + dict->SetInteger("type", source.type); + dict->SetString("description", + l10n_util::GetStringUTF16(source.description_id)); + sources_list.Append(std::move(dict)); + } + + CallJavascriptFunction( + "cr.webUIListenerCallback", base::StringValue("power-sources-changed"), + sources_list, base::StringValue(power_status_->GetCurrentPowerSourceID()), + base::FundamentalValue(power_status_->IsUsbChargerConnected())); +} + +} // namespace settings +} // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_power_handler.h b/chrome/browser/ui/webui/settings/chromeos/device_power_handler.h new file mode 100644 index 0000000..09e6b07 --- /dev/null +++ b/chrome/browser/ui/webui/settings/chromeos/device_power_handler.h
@@ -0,0 +1,56 @@ +// 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. + +#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DEVICE_POWER_HANDLER_H_ +#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DEVICE_POWER_HANDLER_H_ + +#include "ash/common/system/chromeos/power/power_status.h" +#include "base/macros.h" +#include "base/strings/string16.h" +#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h" + +namespace base { +class ListValue; +} + +namespace chromeos { +namespace settings { + +// Chrome OS battery status and power settings handler. +class PowerHandler : public ::settings::SettingsPageUIHandler, + public ash::PowerStatus::Observer { + public: + PowerHandler(); + ~PowerHandler() override; + + // SettingsPageUIHandler implementation. + void RegisterMessages() override; + void OnJavascriptAllowed() override; + void OnJavascriptDisallowed() override; + + // ash::PowerStatus::Observer implementation. + void OnPowerStatusChanged() override; + + private: + // Handler to request updating the power status. + void HandleUpdatePowerStatus(const base::ListValue* args); + + // Handler to change the power source. + void HandleSetPowerSource(const base::ListValue* args); + + // Updates the UI with the current battery status. + void SendBatteryStatus(); + + // Updates the UI with a list of available dual-role power sources. + void SendPowerSources(); + + ash::PowerStatus* power_status_; + + DISALLOW_COPY_AND_ASSIGN(PowerHandler); +}; + +} // namespace settings +} // namespace chromeos + +#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DEVICE_POWER_HANDLER_H_
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc index 074aba8..e184acb4 100644 --- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc +++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -32,7 +32,6 @@ #include "content/public/browser/web_ui_data_source.h" #include "ui/base/l10n/l10n_util.h" - #if defined(OS_CHROMEOS) #include "ash/common/system/chromeos/devicetype_utils.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" @@ -653,6 +652,15 @@ AddLocalizedStringsBulk(html_source, storage_strings, arraysize(storage_strings)); + LocalizedString power_strings[] = { + {"powerSourceLabel", IDS_SETTINGS_POWER_SOURCE_LABEL}, + {"powerSourceBattery", IDS_SETTINGS_POWER_SOURCE_BATTERY}, + {"powerSourceAcAdapter", IDS_SETTINGS_POWER_SOURCE_AC_ADAPTER}, + {"powerSourceLowPowerCharger", + IDS_SETTINGS_POWER_SOURCE_LOW_POWER_CHARGER}, + {"calculatingPower", IDS_SETTINGS_POWER_SOURCE_CALCULATING}}; + AddLocalizedStringsBulk(html_source, power_strings, arraysize(power_strings)); + html_source->AddString("naturalScrollLearnMoreLink", base::ASCIIToUTF16(chrome::kNaturalScrollHelpURL)); }
diff --git a/chrome/browser/ui/webui/settings/md_settings_ui.cc b/chrome/browser/ui/webui/settings/md_settings_ui.cc index d378f57..910ca3b 100644 --- a/chrome/browser/ui/webui/settings/md_settings_ui.cc +++ b/chrome/browser/ui/webui/settings/md_settings_ui.cc
@@ -44,8 +44,10 @@ #if defined(OS_CHROMEOS) #include "ash/common/system/chromeos/palette/palette_utils.h" +#include "ash/common/system/chromeos/power/power_status.h" #include "chrome/browser/chromeos/arc/arc_session_manager.h" #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h" +#include "chrome/browser/ui/ash/ash_util.h" #include "chrome/browser/ui/webui/settings/chromeos/accessibility_handler.h" #include "chrome/browser/ui/webui/settings/chromeos/android_apps_handler.h" #include "chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h" @@ -53,9 +55,11 @@ #include "chrome/browser/ui/webui/settings/chromeos/date_time_handler.h" #include "chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.h" #include "chrome/browser/ui/webui/settings/chromeos/device_pointer_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/device_power_handler.h" #include "chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h" #include "chrome/browser/ui/webui/settings/chromeos/easy_unlock_settings_handler.h" #include "chrome/browser/ui/webui/settings/chromeos/internet_handler.h" +#include "chrome/common/chrome_switches.h" #else // !defined(OS_CHROMEOS) #include "chrome/browser/ui/webui/settings/settings_default_browser_handler.h" #include "chrome/browser/ui/webui/settings/settings_manage_profile_handler.h" @@ -153,6 +157,18 @@ "androidAppsAllowed", arc::ArcSessionManager::IsAllowedForProfile(profile) && !arc::ArcSessionManager::IsOptInVerificationDisabled()); + + // TODO(mash): Support Chrome power settings in Mash. crbug.com/644348 + bool enable_power_settings = + !chrome::IsRunningInMash() && + (switches::PowerOverlayEnabled() || + (ash::PowerStatus::Get()->IsBatteryPresent() && + ash::PowerStatus::Get()->SupportsDualRoleDevices())); + html_source->AddBoolean("enablePowerSettings", enable_power_settings); + if (enable_power_settings) { + AddSettingsPageUIHandler( + base::MakeUnique<chromeos::settings::PowerHandler>()); + } #endif AddSettingsPageUIHandler(
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js index 254e8314..4a1f66e 100644 --- a/chrome/test/data/webui/settings/cr_settings_browsertest.js +++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -719,6 +719,10 @@ TEST_F('CrSettingsDevicePageTest', 'PointersTest', function() { mocha.grep(assert(device_page_tests.TestNames.Pointers)).run(); }); + +TEST_F('CrSettingsDevicePageTest', 'PowerTest', function() { + mocha.grep(assert(device_page_tests.TestNames.Power)).run(); +}); GEN('#endif'); /**
diff --git a/chrome/test/data/webui/settings/device_page_tests.js b/chrome/test/data/webui/settings/device_page_tests.js index 477d274..224583cd 100644 --- a/chrome/test/data/webui/settings/device_page_tests.js +++ b/chrome/test/data/webui/settings/device_page_tests.js
@@ -9,6 +9,7 @@ Display: 'display', Keyboard: 'keyboard', Pointers: 'pointers', + Power: 'power', }; /** @@ -17,6 +18,7 @@ */ function TestDevicePageBrowserProxy() { this.keyboardShortcutsOverlayShown_ = 0; + this.updatePowerStatusCalled_ = 0; } TestDevicePageBrowserProxy.prototype = { @@ -42,6 +44,16 @@ showKeyboardShortcutsOverlay: function() { this.keyboardShortcutsOverlayShown_++; }, + + /** @override */ + updatePowerStatus: function() { + this.updatePowerStatusCalled_++; + }, + + /** @override */ + setPowerSource: function(powerSourceId) { + this.powerSourceId_ = powerSourceId; + }, }; function getFakePrefs() { @@ -525,6 +537,168 @@ expectTrue(displayPage.isMirrored_(displayPage.displays)); }); }); + + suite(assert(TestNames.Power), function() { + /** + * Sets power sources using a deep copy of |sources|. + * @param {Array<settings.PowerSource>} sources + * @param {string} powerSourceId + * @param {bool} isLowPowerCharger + */ + function setPowerSources(sources, powerSourceId, isLowPowerCharger) { + var sourcesCopy = sources.map(function(source) { + return Object.assign({}, source); + }); + cr.webUIListenerCallback('power-sources-changed', + sourcesCopy, powerSourceId, isLowPowerCharger); + } + + suite('no power settings', function() { + test('power row hidden', function() { + assertEquals(null, devicePage.$$('#powerRow')); + assertEquals(0, + settings.DevicePageBrowserProxyImpl.getInstance() + .updatePowerStatusCalled_); + }); + }); + + suite('power settings', function() { + var powerRow; + var powerSourceWrapper; + var powerSourceSelect; + + suiteSetup(function() { + // Always show power settings. + loadTimeData.overrideValues({ + enablePowerSettings: true, + }); + }); + + setup(function() { + powerRow = assert(devicePage.$$('#powerRow')); + powerSourceWrapper = + assert(powerRow.querySelector('.md-select-wrapper')); + powerSourceSelect = assert(devicePage.$$('#powerSource')); + assertEquals(1, + settings.DevicePageBrowserProxyImpl.getInstance() + .updatePowerStatusCalled_); + }); + + test('battery status', function() { + var icon = powerRow.querySelector('iron-icon'); + assertEquals('settings:battery-unknown', icon.icon); + + // Start at 50%. + var batteryStatus = { + charging: false, + calculating: false, + percent: 50, + statusText: '5 hours left', + }; + cr.webUIListenerCallback( + 'battery-status-changed', Object.assign({}, batteryStatus)); + setPowerSources([], '', false); + assertEquals(icon.icon, 'settings:battery-50'); + + // Update to charging. + var powerSource = { + id: '1', + type: settings.PowerDeviceType.DEDICATED_CHARGER, + description: 'AC adapter', + }; + batteryStatus.charging = true; + batteryStatus.percent = 65; + cr.webUIListenerCallback( + 'battery-status-changed', Object.assign({}, batteryStatus)); + setPowerSources([powerSource], powerSource.id, false); + assertEquals(icon.icon, 'settings:battery-charging-60'); + + // Update with a low-power charger. + setPowerSources([powerSource], powerSource.id, true); + assertEquals(icon.icon, 'settings:battery-unreliable'); + + // Update with no charger and a critical battery level. + batteryStatus.charging = false; + batteryStatus.percent = 2; + cr.webUIListenerCallback( + 'battery-status-changed', Object.assign({}, batteryStatus)); + setPowerSources([], '', false); + assertEquals(icon.icon, 'settings:battery-alert'); + }); + + test('power sources', function() { + var batteryStatus = { + charging: false, + calculating: false, + percent: 50, + statusText: '5 hours left', + }; + cr.webUIListenerCallback( + 'battery-status-changed', Object.assign({}, batteryStatus)); + setPowerSources([], '', false); + Polymer.dom.flush(); + + // Power sources dropdown is hidden. + assertTrue(powerSourceWrapper.hidden); + + // Attach a dual-role USB device. + var powerSource = { + id: '2', + type: settings.PowerDeviceType.DUAL_ROLE_USB, + description: 'USB-C device', + }; + setPowerSources([powerSource], '', false); + Polymer.dom.flush(); + + // "Battery" should be selected. + assertFalse(powerSourceWrapper.hidden); + assertEquals('', powerSourceSelect.value); + + // Select the power source. + setPowerSources([powerSource], powerSource.id, true); + Polymer.dom.flush(); + assertFalse(powerSourceWrapper.hidden); + assertEquals(powerSource.id, powerSourceSelect.value); + + // Send another power source; the first should still be selected. + var otherPowerSource = Object.assign({}, powerSource); + otherPowerSource.id = '3'; + setPowerSources( + [otherPowerSource, powerSource], powerSource.id, true); + Polymer.dom.flush(); + assertFalse(powerSourceWrapper.hidden); + assertEquals(powerSource.id, powerSourceSelect.value); + }); + + test('choose power source', function() { + var batteryStatus = { + charging: false, + calculating: false, + percent: 50, + statusText: '5 hours left', + }; + cr.webUIListenerCallback( + 'battery-status-changed', Object.assign({}, batteryStatus)); + + // Attach a dual-role USB device. + var powerSource = { + id: '3', + type: settings.PowerDeviceType.DUAL_ROLE_USB, + description: 'USB-C device', + }; + setPowerSources([powerSource], '', false); + Polymer.dom.flush(); + + // Select the device. + powerSourceSelect.value = powerSourceSelect.children[1].value; + powerSourceSelect.dispatchEvent(new CustomEvent('change')); + Polymer.dom.flush(); + expectEquals( + powerSource.id, + settings.DevicePageBrowserProxyImpl.getInstance().powerSourceId_); + }); + }); + }); }); return {