Bug 1494549 - Show USB devices status in sidebar;r=ladybenko
☠☠ backed out by 1c6fa266a661 ☠ ☠
authorJulian Descottes <jdescottes@mozilla.com>
Mon, 15 Oct 2018 15:31:14 +0000
changeset 489780 0d7e2a4ad36090bb5599afd91fe749fd85b1fa36
parent 489779 d5cc1ab5bb162464e4be7593e6a71d330faf3b51
child 489781 3e89e661031ccb8f6f1889c5b6c1687db03e3eaa
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewersladybenko
bugs1494549
milestone64.0a1
Bug 1494549 - Show USB devices status in sidebar;r=ladybenko Maybe we want to land the simplest solution for now and discuss quickly how to style the message to reduce confusion in a follow up? Differential Revision: https://phabricator.services.mozilla.com/D8334
devtools/client/aboutdebugging-new/aboutdebugging.js
devtools/client/aboutdebugging-new/src/actions/ui.js
devtools/client/aboutdebugging-new/src/components/App.js
devtools/client/aboutdebugging-new/src/components/sidebar/Sidebar.css
devtools/client/aboutdebugging-new/src/components/sidebar/Sidebar.js
devtools/client/aboutdebugging-new/src/constants.js
devtools/client/aboutdebugging-new/src/reducers/ui-state.js
devtools/client/aboutdebugging-new/tmp-locale/en-US/aboutdebugging.notftl
--- a/devtools/client/aboutdebugging-new/aboutdebugging.js
+++ b/devtools/client/aboutdebugging-new/aboutdebugging.js
@@ -29,28 +29,31 @@ const {
 const {
   addUSBRuntimesObserver,
   disableUSBRuntimes,
   enableUSBRuntimes,
   getUSBRuntimes,
   removeUSBRuntimesObserver,
 } = require("./src/modules/usb-runtimes");
 
+loader.lazyRequireGetter(this, "adbAddon", "devtools/shared/adb/adb-addon", true);
+
 const App = createFactory(require("./src/components/App"));
 
 const { PAGES, RUNTIMES } = require("./src/constants");
 
 const AboutDebugging = {
   async init() {
     if (!Services.prefs.getBoolPref("devtools.enabled", true)) {
       // If DevTools are disabled, navigate to about:devtools.
       window.location = "about:devtools?reason=AboutDebugging";
       return;
     }
 
+    this.onAdbAddonUpdated = this.onAdbAddonUpdated.bind(this);
     this.onNetworkLocationsUpdated = this.onNetworkLocationsUpdated.bind(this);
     this.onUSBRuntimesUpdated = this.onUSBRuntimesUpdated.bind(this);
 
     this.store = configureStore();
     this.actions = bindActionCreators(actions, this.store.dispatch);
 
     const messageContexts = await this.createMessageContexts();
 
@@ -60,16 +63,19 @@ const AboutDebugging = {
     );
 
     this.actions.selectPage(PAGES.THIS_FIREFOX, RUNTIMES.THIS_FIREFOX);
     this.actions.updateNetworkLocations(getNetworkLocations());
 
     addNetworkLocationsObserver(this.onNetworkLocationsUpdated);
     addUSBRuntimesObserver(this.onUSBRuntimesUpdated);
     await enableUSBRuntimes();
+
+    adbAddon.on("update", this.onAdbAddonUpdated);
+    this.onAdbAddonUpdated();
   },
 
   async createMessageContexts() {
     // XXX Until the strings for the updated about:debugging stabilize, we
     // locate them outside the regular directory for locale resources so that
     // they don't get picked up by localization tools.
     if (!L10nRegistry.sources.has("aboutdebugging")) {
       const temporarySource = new FileSource(
@@ -87,16 +93,20 @@ const AboutDebugging = {
     const contexts = [];
     for await (const context of generator) {
       contexts.push(context);
     }
 
     return contexts;
   },
 
+  onAdbAddonUpdated() {
+    this.actions.updateAdbAddonStatus(adbAddon.status);
+  },
+
   onNetworkLocationsUpdated() {
     this.actions.updateNetworkLocations(getNetworkLocations());
   },
 
   onUSBRuntimesUpdated() {
     this.actions.updateUSBRuntimes(getUSBRuntimes());
   },
 
@@ -108,16 +118,17 @@ const AboutDebugging = {
     const currentRuntimeId = state.runtimes.selectedRuntimeId;
     if (currentRuntimeId) {
       await this.actions.unwatchRuntime(currentRuntimeId);
     }
 
     removeNetworkLocationsObserver(this.onNetworkLocationsUpdated);
     removeUSBRuntimesObserver(this.onUSBRuntimesUpdated);
     disableUSBRuntimes();
+    adbAddon.off("update", this.onAdbAddonUpdated);
     setDebugTargetCollapsibilities(state.ui.debugTargetCollapsibilities);
     unmountComponentAtNode(this.mount);
   },
 
   get mount() {
     return document.getElementById("mount");
   },
 };
--- a/devtools/client/aboutdebugging-new/src/actions/ui.js
+++ b/devtools/client/aboutdebugging-new/src/actions/ui.js
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {
+  ADB_ADDON_STATUS_UPDATED,
   DEBUG_TARGET_COLLAPSIBILITY_UPDATED,
   NETWORK_LOCATIONS_UPDATED,
   PAGE_SELECTED,
   PAGES,
 } = require("../constants");
 
 const NetworkLocationsModule = require("../modules/network-locations");
 
@@ -55,19 +56,24 @@ function addNetworkLocation(location) {
 }
 
 function removeNetworkLocation(location) {
   return (dispatch, getState) => {
     NetworkLocationsModule.removeNetworkLocation(location);
   };
 }
 
+function updateAdbAddonStatus(adbAddonStatus) {
+  return { type: ADB_ADDON_STATUS_UPDATED, adbAddonStatus };
+}
+
 function updateNetworkLocations(locations) {
   return { type: NETWORK_LOCATIONS_UPDATED, locations };
 }
 
 module.exports = {
   addNetworkLocation,
   removeNetworkLocation,
   selectPage,
+  updateAdbAddonStatus,
   updateDebugTargetCollapsibility,
   updateNetworkLocations,
 };
--- a/devtools/client/aboutdebugging-new/src/components/App.js
+++ b/devtools/client/aboutdebugging-new/src/components/App.js
@@ -16,16 +16,17 @@ const { PAGES } = require("../constants"
 
 const ConnectPage = createFactory(require("./connect/ConnectPage"));
 const RuntimePage = createFactory(require("./RuntimePage"));
 const Sidebar = createFactory(require("./sidebar/Sidebar"));
 
 class App extends PureComponent {
   static get propTypes() {
     return {
+      adbAddonStatus: PropTypes.string,
       // The "dispatch" helper is forwarded to the App component via connect.
       // From that point, components are responsible for forwarding the dispatch
       // property to all components who need to dispatch actions.
       dispatch: PropTypes.func.isRequired,
       messageContexts: PropTypes.arrayOf(PropTypes.object).isRequired,
       networkLocations: PropTypes.arrayOf(PropTypes.string).isRequired,
       runtimes: PropTypes.object.isRequired,
       selectedPage: PropTypes.string,
@@ -46,38 +47,48 @@ class App extends PureComponent {
       default:
         // All pages except for the CONNECT page are RUNTIME pages.
         return RuntimePage({ dispatch });
     }
   }
 
   render() {
     const {
+      adbAddonStatus,
       dispatch,
       messageContexts,
       runtimes,
       selectedPage,
     } = this.props;
 
     return LocalizationProvider(
       { messages: messageContexts },
       dom.div(
         { className: "app" },
-        Sidebar({ className: "app__sidebar", dispatch, runtimes, selectedPage }),
+        Sidebar(
+          {
+            adbAddonStatus,
+            className: "app__sidebar",
+            dispatch,
+            runtimes,
+            selectedPage
+          }
+        ),
         dom.main(
           { className: "app__content" },
           this.getSelectedPageComponent()
         )
       )
     );
   }
 }
 
 const mapStateToProps = state => {
   return {
+    adbAddonStatus: state.ui.adbAddonStatus,
     runtimes: state.runtimes,
     networkLocations: state.ui.networkLocations,
     selectedPage: state.ui.selectedPage,
   };
 };
 
 const mapDispatchToProps = dispatch => ({
   dispatch,
--- a/devtools/client/aboutdebugging-new/src/components/sidebar/Sidebar.css
+++ b/devtools/client/aboutdebugging-new/src/components/sidebar/Sidebar.css
@@ -1,11 +1,11 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-.sidebar__devices__no-devices-message {
+.sidebar__devices__message {
   color: var(--grey-40);
   display: inline-block;
   padding: 12px 0;
   text-align: center;
   width: 100%;
 }
\ No newline at end of file
--- a/devtools/client/aboutdebugging-new/src/components/sidebar/Sidebar.js
+++ b/devtools/client/aboutdebugging-new/src/components/sidebar/Sidebar.js
@@ -7,43 +7,61 @@
 const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 const FluentReact = require("devtools/client/shared/vendor/fluent-react");
 const Localized = createFactory(FluentReact.Localized);
 
 const { PAGES, RUNTIMES } = require("../../constants");
+loader.lazyRequireGetter(this, "ADB_ADDON_STATES", "devtools/shared/adb/adb-addon", true);
 
 const DeviceSidebarItemAction = createFactory(require("./DeviceSidebarItemAction"));
 const SidebarItem = createFactory(require("./SidebarItem"));
 const FIREFOX_ICON = "chrome://devtools/skin/images/aboutdebugging-firefox-logo.svg";
 const CONNECT_ICON = "chrome://devtools/skin/images/aboutdebugging-connect-icon.svg";
 const GLOBE_ICON = "chrome://devtools/skin/images/aboutdebugging-globe-icon.svg";
 const USB_ICON = "chrome://devtools/skin/images/aboutdebugging-connect-icon.svg";
 
 class Sidebar extends PureComponent {
   static get propTypes() {
     return {
+      adbAddonStatus: PropTypes.string,
       className: PropTypes.string,
       dispatch: PropTypes.func.isRequired,
       runtimes: PropTypes.object.isRequired,
       selectedPage: PropTypes.string,
     };
   }
 
+  renderAdbAddonStatus() {
+    const isAddonInstalled = this.props.adbAddonStatus === ADB_ADDON_STATES.INSTALLED;
+    const localizationId = isAddonInstalled ? "about-debugging-sidebar-usb-enabled" :
+                                              "about-debugging-sidebar-usb-disabled";
+    return Localized(
+      {
+        id: localizationId
+      }, dom.aside(
+        {
+          className: "sidebar__devices__message"
+        },
+        localizationId
+      )
+    );
+  }
+
   renderDevices() {
     const { runtimes } = this.props;
     if (!runtimes.networkRuntimes.length && !runtimes.usbRuntimes.length) {
       return Localized(
         {
           id: "about-debugging-sidebar-no-devices"
-        }, dom.span(
+        }, dom.aside(
           {
-            className: "sidebar__devices__no-devices-message js-sidebar-no-devices"
+            className: "sidebar__devices__message js-sidebar-no-devices"
           },
           "No devices discovered"
         )
       );
     }
 
     return [
       ...this.renderSidebarItems(GLOBE_ICON, runtimes.networkRuntimes),
@@ -106,15 +124,16 @@ class Sidebar extends PureComponent {
             dispatch,
             icon: CONNECT_ICON,
             isSelected: PAGES.CONNECT === selectedPage,
             name: "Connect",
             selectable: true,
           })
         ),
         dom.hr(),
+        this.renderAdbAddonStatus(),
         this.renderDevices()
       )
     );
   }
 }
 
 module.exports = Sidebar;
--- a/devtools/client/aboutdebugging-new/src/constants.js
+++ b/devtools/client/aboutdebugging-new/src/constants.js
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const actionTypes = {
+  ADB_ADDON_STATUS_UPDATED: "ADB_ADDON_STATUS_UPDATED",
   CONNECT_RUNTIME_FAILURE: "CONNECT_RUNTIME_FAILURE",
   CONNECT_RUNTIME_START: "CONNECT_RUNTIME_START",
   CONNECT_RUNTIME_SUCCESS: "CONNECT_RUNTIME_SUCCESS",
   DEBUG_TARGET_COLLAPSIBILITY_UPDATED: "DEBUG_TARGET_COLLAPSIBILITY_UPDATED",
   DISCONNECT_RUNTIME_FAILURE: "DISCONNECT_RUNTIME_FAILURE",
   DISCONNECT_RUNTIME_START: "DISCONNECT_RUNTIME_START",
   DISCONNECT_RUNTIME_SUCCESS: "DISCONNECT_RUNTIME_SUCCESS",
   NETWORK_LOCATIONS_UPDATED: "NETWORK_LOCATIONS_UPDATED",
--- a/devtools/client/aboutdebugging-new/src/reducers/ui-state.js
+++ b/devtools/client/aboutdebugging-new/src/reducers/ui-state.js
@@ -1,30 +1,37 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {
+  ADB_ADDON_STATUS_UPDATED,
   DEBUG_TARGET_COLLAPSIBILITY_UPDATED,
   NETWORK_LOCATIONS_UPDATED,
   PAGE_SELECTED,
 } = require("../constants");
 
 function UiState(locations = [], debugTargetCollapsibilities = {}) {
   return {
+    adbAddonStatus: null,
     debugTargetCollapsibilities,
     networkLocations: locations,
     selectedPage: null,
   };
 }
 
 function uiReducer(state = UiState(), action) {
   switch (action.type) {
+    case ADB_ADDON_STATUS_UPDATED: {
+      const { adbAddonStatus } = action;
+      return Object.assign({}, state, { adbAddonStatus });
+    }
+
     case DEBUG_TARGET_COLLAPSIBILITY_UPDATED: {
       const { isCollapsed, key } = action;
       const debugTargetCollapsibilities = new Map(state.debugTargetCollapsibilities);
       debugTargetCollapsibilities.set(key, isCollapsed);
       return Object.assign({}, state, { debugTargetCollapsibilities });
     }
 
     case NETWORK_LOCATIONS_UPDATED: {
--- a/devtools/client/aboutdebugging-new/tmp-locale/en-US/aboutdebugging.notftl
+++ b/devtools/client/aboutdebugging-new/tmp-locale/en-US/aboutdebugging.notftl
@@ -8,18 +8,22 @@
 # Sidebar heading for selecting the currently running instance of Firefox
 about-debugging-sidebar-this-firefox =
   .name = This Firefox
 
 # Sidebar heading for connecting to some remote source
 about-debugging-sidebar-connect =
   .name = Connect
 
-# Text displayed in the about:debugging sidebar when no device was found.
-about-debugging-sidebar-no-devices = No devices discovered
+# Text displayed in the about:debugging sidebar when USB devices discovery is enabled.
+about-debugging-sidebar-usb-enabled = USB devices enabled
+
+# Text displayed in the about:debugging sidebar when USB devices discovery is disabled
+# (for instance because the mandatory ADB extension is not installed).
+about-debugging-sidebar-usb-disabled = USB devices disabled
 
 # Text displayed in buttons found in sidebar items representing remote runtimes.
 # Clicking on the button will attempt to connect to the runtime.
 about-debugging-sidebar-item-connect-button = Connect
 
 # Temporary text displayed in sidebar items representing remote runtimes after
 # successfully connecting to them. Temporary UI, do not localize.
 about-debugging-sidebar-item-connected-label = Connected