Bug 1488506 - Add button to enable/disable USB debugging in Connect page;r=ladybenko,daisuke
authorJulian Descottes <jdescottes@mozilla.com>
Wed, 17 Oct 2018 08:47:30 +0000
changeset 490301 97207813052bd7a33c671422198b5d1e43adab49
parent 490300 3e2d47ca7a2a199ab58bf0b7605882a205e6cece
child 490302 cac4aff89a39a7df33305397268510e8c7d2d840
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewersladybenko, daisuke
bugs1488506
milestone64.0a1
Bug 1488506 - Add button to enable/disable USB debugging in Connect page;r=ladybenko,daisuke Differential Revision: https://phabricator.services.mozilla.com/D8556
devtools/client/aboutdebugging-new/aboutdebugging.css
devtools/client/aboutdebugging-new/src/actions/ui.js
devtools/client/aboutdebugging-new/src/components/App.js
devtools/client/aboutdebugging-new/src/components/connect/ConnectPage.css
devtools/client/aboutdebugging-new/src/components/connect/ConnectPage.js
devtools/client/aboutdebugging-new/src/components/connect/moz.build
devtools/client/aboutdebugging-new/src/constants.js
devtools/client/aboutdebugging-new/tmp-locale/en-US/aboutdebugging.notftl
--- a/devtools/client/aboutdebugging-new/aboutdebugging.css
+++ b/devtools/client/aboutdebugging-new/aboutdebugging.css
@@ -1,16 +1,17 @@
 /* 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/. */
 
 @import "chrome://global/skin/in-content/common.css";
 @import "resource://devtools/client/themes/variables.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/App.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/RuntimeInfo.css";
+@import "resource://devtools/client/aboutdebugging-new/src/components/connect/ConnectPage.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/connect/ConnectSteps.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/connect/NetworkLocationsForm.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/connect/NetworkLocationsList.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetList.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetPane.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/ExtensionDetail.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/WorkerDetail.css";
--- a/devtools/client/aboutdebugging-new/src/actions/ui.js
+++ b/devtools/client/aboutdebugging-new/src/actions/ui.js
@@ -1,23 +1,30 @@
 /* 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_INSTALL_START,
+  ADB_ADDON_INSTALL_SUCCESS,
+  ADB_ADDON_INSTALL_FAILURE,
+  ADB_ADDON_UNINSTALL_START,
+  ADB_ADDON_UNINSTALL_SUCCESS,
+  ADB_ADDON_UNINSTALL_FAILURE,
   ADB_ADDON_STATUS_UPDATED,
   DEBUG_TARGET_COLLAPSIBILITY_UPDATED,
   NETWORK_LOCATIONS_UPDATED,
   PAGE_SELECTED,
   PAGES,
 } = require("../constants");
 
 const NetworkLocationsModule = require("../modules/network-locations");
+const { adbAddon } = require("devtools/shared/adb/adb-addon");
 
 const Actions = require("./index");
 
 // XXX: Isolating the code here, because it feels wrong to rely solely on the page "not"
 // being CONNECT to decide what to do. Should we have a page "type" on top of page "id"?
 function _isRuntimePage(page) {
   return page && page !== PAGES.CONNECT;
 }
@@ -64,16 +71,44 @@ function removeNetworkLocation(location)
 function updateAdbAddonStatus(adbAddonStatus) {
   return { type: ADB_ADDON_STATUS_UPDATED, adbAddonStatus };
 }
 
 function updateNetworkLocations(locations) {
   return { type: NETWORK_LOCATIONS_UPDATED, locations };
 }
 
+function installAdbAddon() {
+  return async (dispatch, getState) => {
+    dispatch({ type: ADB_ADDON_INSTALL_START });
+
+    try {
+      await adbAddon.install();
+      dispatch({ type: ADB_ADDON_INSTALL_SUCCESS });
+    } catch (e) {
+      dispatch({ type: ADB_ADDON_INSTALL_FAILURE, error: e.message });
+    }
+  };
+}
+
+function uninstallAdbAddon() {
+  return async (dispatch, getState) => {
+    dispatch({ type: ADB_ADDON_UNINSTALL_START });
+
+    try {
+      await adbAddon.uninstall();
+      dispatch({ type: ADB_ADDON_UNINSTALL_SUCCESS });
+    } catch (e) {
+      dispatch({ type: ADB_ADDON_UNINSTALL_FAILURE, error: e.message });
+    }
+  };
+}
+
 module.exports = {
   addNetworkLocation,
+  installAdbAddon,
   removeNetworkLocation,
   selectPage,
+  uninstallAdbAddon,
   updateAdbAddonStatus,
   updateDebugTargetCollapsibility,
   updateNetworkLocations,
 };
--- a/devtools/client/aboutdebugging-new/src/components/App.js
+++ b/devtools/client/aboutdebugging-new/src/components/App.js
@@ -31,26 +31,35 @@ class App extends PureComponent {
       networkLocations: PropTypes.arrayOf(PropTypes.string).isRequired,
       networkRuntimes: PropTypes.arrayOf(Types.runtime).isRequired,
       selectedPage: PropTypes.string,
       usbRuntimes: PropTypes.arrayOf(Types.runtime).isRequired,
     };
   }
 
   getSelectedPageComponent() {
-    const { dispatch, networkLocations, selectedPage } = this.props;
+    const {
+      adbAddonStatus,
+      dispatch,
+      networkLocations,
+      selectedPage
+    } = this.props;
 
     if (!selectedPage) {
       // No page selected.
       return null;
     }
 
     switch (selectedPage) {
       case PAGES.CONNECT:
-        return ConnectPage({ dispatch, networkLocations });
+        return ConnectPage({
+          adbAddonStatus,
+          dispatch,
+          networkLocations
+        });
       default:
         // All pages except for the CONNECT page are RUNTIME pages.
         return RuntimePage({ dispatch });
     }
   }
 
   render() {
     const {
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/connect/ConnectPage.css
@@ -0,0 +1,7 @@
+/* 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/. */
+
+.connect-page__usb__toggle-button {
+  margin-top: calc(var(--base-distance) * 4);
+}
--- a/devtools/client/aboutdebugging-new/src/components/connect/ConnectPage.js
+++ b/devtools/client/aboutdebugging-new/src/components/connect/ConnectPage.js
@@ -6,28 +6,37 @@
 
 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 {
+  USB_STATES,
+} = require("../../constants");
+
+const Actions = require("../../actions/index");
+
+loader.lazyRequireGetter(this, "ADB_ADDON_STATES", "devtools/shared/adb/adb-addon", true);
+
 const ConnectSection = createFactory(require("./ConnectSection"));
 const ConnectSteps = createFactory(require("./ConnectSteps"));
 const NetworkLocationsForm = createFactory(require("./NetworkLocationsForm"));
 const NetworkLocationsList = createFactory(require("./NetworkLocationsList"));
 
 const USB_ICON_SRC = "chrome://devtools/skin/images/aboutdebugging-connect-icon.svg";
 const WIFI_ICON_SRC = "chrome://devtools/skin/images/aboutdebugging-connect-icon.svg";
 const GLOBE_ICON_SRC = "chrome://devtools/skin/images/aboutdebugging-globe-icon.svg";
 
 class ConnectPage extends PureComponent {
   static get propTypes() {
     return {
+      adbAddonStatus: PropTypes.string,
       dispatch: PropTypes.func.isRequired,
       // Provided by wrapping the component with FluentReact.withLocalization.
       getString: PropTypes.func.isRequired,
       networkLocations: PropTypes.arrayOf(PropTypes.string).isRequired,
     };
   }
 
   renderWifi() {
@@ -49,35 +58,98 @@ class ConnectPage extends PureComponent 
             getString("about-debugging-connect-wifi-step-open-options"),
             getString("about-debugging-connect-wifi-step-enable-debug"),
           ]
         })
       )
     );
   }
 
+  onToggleUSBClick() {
+    const { adbAddonStatus } = this.props;
+    const isAddonInstalled = adbAddonStatus === ADB_ADDON_STATES.INSTALLED;
+    if (isAddonInstalled) {
+      this.props.dispatch(Actions.uninstallAdbAddon());
+    } else {
+      this.props.dispatch(Actions.installAdbAddon());
+    }
+  }
+
+  getUsbStatus() {
+    switch (this.props.adbAddonStatus) {
+      case ADB_ADDON_STATES.INSTALLED:
+        return USB_STATES.ENABLED_USB;
+      case ADB_ADDON_STATES.UNINSTALLED:
+        return USB_STATES.DISABLED_USB;
+      default:
+        return USB_STATES.UPDATING_USB;
+    }
+  }
+
+  renderUsbToggleButton() {
+    const usbStatus = this.getUsbStatus();
+
+    const localizedStates = {
+      [USB_STATES.ENABLED_USB]: "about-debugging-connect-usb-disable-button",
+      [USB_STATES.DISABLED_USB]: "about-debugging-connect-usb-enable-button",
+      [USB_STATES.UPDATING_USB]: "about-debugging-connect-usb-updating-button",
+    };
+    const localizedState = localizedStates[usbStatus];
+
+    // Disable the button while the USB status is updating.
+    const disabled = usbStatus === USB_STATES.UPDATING_USB;
+
+    return Localized(
+      {
+        id: localizedState
+      },
+      dom.button(
+        {
+          className: "std-button connect-page__usb__toggle-button",
+          disabled,
+          onClick: () => this.onToggleUSBClick(),
+        },
+        localizedState
+      )
+    );
+  }
+
   renderUsb() {
-    const { getString } = this.props;
+    const { adbAddonStatus, getString } = this.props;
+    const isAddonInstalled = adbAddonStatus === ADB_ADDON_STATES.INSTALLED;
     return Localized(
       {
         id: "about-debugging-connect-usb",
         attrs: { title: true }
       },
       ConnectSection(
         {
           icon: USB_ICON_SRC,
           title: "Via USB",
         },
-        ConnectSteps({
-          steps: [
-            getString("about-debugging-connect-usb-step-enable-dev-menu"),
-            getString("about-debugging-connect-usb-step-enable-debug"),
-            getString("about-debugging-connect-usb-step-plug-device"),
-          ]
-        })
+        (isAddonInstalled ?
+          ConnectSteps({
+            steps: [
+              getString("about-debugging-connect-usb-step-enable-dev-menu"),
+              getString("about-debugging-connect-usb-step-enable-debug"),
+              getString("about-debugging-connect-usb-step-plug-device"),
+            ]
+          }) :
+          Localized(
+            {
+              id: "about-debugging-connect-usb-disabled",
+            },
+            dom.aside(
+              {},
+              "Enabling this will download and add the required Android USB debugging " +
+              "components to Firefox."
+            )
+          )
+        ),
+        this.renderUsbToggleButton()
       )
     );
   }
 
   renderNetwork() {
     const { dispatch, networkLocations } = this.props;
     return Localized(
       {
--- a/devtools/client/aboutdebugging-new/src/components/connect/moz.build
+++ b/devtools/client/aboutdebugging-new/src/components/connect/moz.build
@@ -1,13 +1,14 @@
 # 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/.
 
 DevToolsModules(
+    'ConnectPage.css',
     'ConnectPage.js',
     'ConnectSection.js',
     'ConnectSteps.css',
     'ConnectSteps.js',
     'NetworkLocationsForm.css',
     'NetworkLocationsForm.js',
     'NetworkLocationsList.css',
     'NetworkLocationsList.js',
--- a/devtools/client/aboutdebugging-new/src/constants.js
+++ b/devtools/client/aboutdebugging-new/src/constants.js
@@ -1,15 +1,21 @@
 /* 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_INSTALL_START: "ADB_ADDON_INSTALL_START",
+  ADB_ADDON_INSTALL_SUCCESS: "ADB_ADDON_INSTALL_SUCCESS",
+  ADB_ADDON_INSTALL_FAILURE: "ADB_ADDON_INSTALL_FAILURE",
+  ADB_ADDON_UNINSTALL_START: "ADB_ADDON_UNINSTALL_START",
+  ADB_ADDON_UNINSTALL_SUCCESS: "ADB_ADDON_UNINSTALL_SUCCESS",
+  ADB_ADDON_UNINSTALL_FAILURE: "ADB_ADDON_UNINSTALL_FAILURE",
   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",
@@ -65,17 +71,24 @@ const SERVICE_WORKER_FETCH_STATES = {
 };
 
 const SERVICE_WORKER_STATUSES = {
   RUNNING: "RUNNING",
   REGISTERING: "REGISTERING",
   STOPPED: "STOPPED",
 };
 
+const USB_STATES = {
+  DISABLED_USB: "DISABLED_USB",
+  ENABLED_USB: "ENABLED_USB",
+  UPDATING_USB: "UPDATING_USB",
+};
+
 // flatten constants
 module.exports = Object.assign({}, {
   DEBUG_TARGETS,
   DEBUG_TARGET_PANE,
   PAGES,
   RUNTIMES,
   SERVICE_WORKER_FETCH_STATES,
   SERVICE_WORKER_STATUSES,
+  USB_STATES,
 }, actionTypes);
--- a/devtools/client/aboutdebugging-new/tmp-locale/en-US/aboutdebugging.notftl
+++ b/devtools/client/aboutdebugging-new/tmp-locale/en-US/aboutdebugging.notftl
@@ -54,16 +54,21 @@ about-debugging-connect-wifi-step-open-o
 
 # WiFi section step by step guide
 about-debugging-connect-wifi-step-enable-debug = Enable Remote Debugging via WiFi in the Developer Tools section
 
 # USB section of the Connect page
 about-debugging-connect-usb
   .title = Via USB
 
+about-debugging-connect-usb-disabled = Enabling this will download and add the required Android USB debugging components to Firefox.
+about-debugging-connect-usb-enable-button = Enable USB Devices
+about-debugging-connect-usb-disable-button = Disable USB Devices
+about-debugging-connect-usb-updating-button = Updating…
+
 # USB section step by step guide
 about-debugging-connect-usb-step-enable-dev-menu = Enable Developer menu on your Android device
 
 # USB section step by step guide
 about-debugging-connect-usb-step-enable-debug = Enable USB Debugging on the Android Developer Menu
 
 # USB section step by step guide
 about-debugging-connect-usb-step-plug-device = Connect the USB Device to your computer