Bug 917942 - Create a JS interface to Sync configuration. r=margaret
authorRichard Newman <rnewman@mozilla.com>
Tue, 18 Feb 2014 19:20:27 -0800
changeset 169932 2af0db58021bf6fbc0633fea08f46fe81fd11afc
parent 169931 750e90d5ee9aad4d53bf76272f52dd88a2564b90
child 169933 393c539741f2fc982824f566094ef85857359ca7
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersmargaret
bugs917942
milestone30.0a1
Bug 917942 - Create a JS interface to Sync configuration. r=margaret
mobile/android/base/BrowserApp.java
mobile/android/base/tests/robocop.ini
mobile/android/base/tests/testAccounts.java
mobile/android/base/tests/testAccounts.js
mobile/android/modules/Accounts.jsm
mobile/android/modules/moz.build
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -8,31 +8,34 @@ package org.mozilla.gecko;
 import org.mozilla.gecko.animation.PropertyAnimator;
 import org.mozilla.gecko.animation.ViewHelper;
 import org.mozilla.gecko.db.BrowserContract.Combined;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.favicons.Favicons;
 import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
 import org.mozilla.gecko.favicons.LoadFaviconTask;
 import org.mozilla.gecko.favicons.decoders.IconDirectoryEntry;
+import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity;
+import org.mozilla.gecko.fxa.FirefoxAccounts;
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.gfx.GeckoLayerClient;
 import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
 import org.mozilla.gecko.gfx.LayerMarginsAnimator;
 import org.mozilla.gecko.health.BrowserHealthRecorder;
 import org.mozilla.gecko.health.BrowserHealthReporter;
 import org.mozilla.gecko.health.HealthRecorder;
 import org.mozilla.gecko.health.SessionInformation;
 import org.mozilla.gecko.home.BrowserSearch;
 import org.mozilla.gecko.home.HomePager;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 import org.mozilla.gecko.home.SearchEngine;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.prompts.Prompt;
+import org.mozilla.gecko.sync.setup.SyncAccounts;
 import org.mozilla.gecko.toolbar.AutocompleteHandler;
 import org.mozilla.gecko.toolbar.BrowserToolbar;
 import org.mozilla.gecko.util.Clipboard;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.util.GamepadUtils;
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.MenuUtils;
 import org.mozilla.gecko.util.StringUtils;
@@ -540,16 +543,18 @@ abstract public class BrowserApp extends
         registerEventListener("Feedback:OpenPlayStore");
         registerEventListener("Feedback:MaybeLater");
         registerEventListener("Telemetry:Gather");
         registerEventListener("Settings:Show");
         registerEventListener("Updater:Launch");
         registerEventListener("Menu:Add");
         registerEventListener("Menu:Remove");
         registerEventListener("Menu:Update");
+        registerEventListener("Accounts:Create");
+        registerEventListener("Accounts:Exist");
 
         Distribution.init(this);
         JavaAddonManager.getInstance().init(getApplicationContext());
         mSharedPreferencesHelper = new SharedPreferencesHelper(getApplicationContext());
         mOrderedBroadcastHelper = new OrderedBroadcastHelper(getApplicationContext());
         mBrowserHealthReporter = new BrowserHealthReporter();
 
         if (AppConstants.MOZ_ANDROID_BEAM && Build.VERSION.SDK_INT >= 14) {
@@ -857,16 +862,18 @@ abstract public class BrowserApp extends
         unregisterEventListener("Feedback:OpenPlayStore");
         unregisterEventListener("Feedback:MaybeLater");
         unregisterEventListener("Telemetry:Gather");
         unregisterEventListener("Settings:Show");
         unregisterEventListener("Updater:Launch");
         unregisterEventListener("Menu:Add");
         unregisterEventListener("Menu:Remove");
         unregisterEventListener("Menu:Update");
+        unregisterEventListener("Accounts:Create");
+        unregisterEventListener("Accounts:Exist");
 
         if (AppConstants.MOZ_ANDROID_BEAM && Build.VERSION.SDK_INT >= 14) {
             NfcAdapter nfc = NfcAdapter.getDefaultAdapter(this);
             if (nfc != null) {
                 // null this out even though the docs say it's not needed,
                 // because the source code looks like it will only do this
                 // automatically on API 14+
                 nfc.setNdefPushMessageCallback(null, this);
@@ -1241,16 +1248,40 @@ abstract public class BrowserApp extends
             } else if (event.equals("Updater:Launch")) {
                 handleUpdaterLaunch();
             } else if (event.equals("Prompt:ShowTop")) {
                 // Bring this activity to front so the prompt is visible..
                 Intent bringToFrontIntent = new Intent();
                 bringToFrontIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.BROWSER_INTENT_CLASS);
                 bringToFrontIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
                 startActivity(bringToFrontIntent);
+            } else if (event.equals("Accounts:Create")) {
+                // Do exactly the same thing as if you tapped 'Sync'
+                // in Settings.
+                final Intent intent = new Intent(getContext(), FxAccountGetStartedActivity.class);
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                getContext().startActivity(intent);
+            } else if (event.equals("Accounts:Exist")) {
+                final String kind = message.getString("kind");
+                final JSONObject response = new JSONObject();
+
+                if ("any".equals(kind)) {
+                    response.put("exists", SyncAccounts.syncAccountsExist(getContext()) ||
+                                           FirefoxAccounts.firefoxAccountsExist(getContext()));
+                    EventDispatcher.sendResponse(message, response);
+                } else if ("fxa".equals(kind)) {
+                    response.put("exists", FirefoxAccounts.firefoxAccountsExist(getContext()));
+                    EventDispatcher.sendResponse(message, response);
+                } else if ("sync11".equals(kind)) {
+                    response.put("exists", SyncAccounts.syncAccountsExist(getContext()));
+                    EventDispatcher.sendResponse(message, response);
+                } else {
+                    response.put("error", "Unknown kind");
+                    EventDispatcher.sendError(message, response);
+                }
             } else {
                 super.handleMessage(event, message);
             }
         } catch (Exception e) {
             Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
         }
     }
 
--- a/mobile/android/base/tests/robocop.ini
+++ b/mobile/android/base/tests/robocop.ini
@@ -72,16 +72,17 @@ skip-if = processor == "x86"
 [testSystemPages]
 # disabled on x86 only; bug 907383
 skip-if = processor == "x86"
 # [testThumbnails] # see bug 813107
 [testTitleBar]
 # [testVkbOverlap] # see bug 907274
 
 # Using JavascriptTest
+[testAccounts]
 [testBrowserDiscovery]
 [testDeviceSearchEngine]
 [testJNI]
 # [testMozPay] # see bug 945675
 [testOrderedBroadcast]
 [testSharedPreferences]
 [testSimpleDiscovery]
 [testUITelemetry]
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tests/testAccounts.java
@@ -0,0 +1,28 @@
+/* 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/. */
+
+package org.mozilla.gecko.tests;
+
+import org.mozilla.gecko.*;
+import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity;
+
+public class testAccounts extends JavascriptTest {
+    public testAccounts() {
+        super("testAccounts.js");
+    }
+
+    @Override
+    public void testJavascript() throws Exception {
+        super.testJavascript();
+
+        // Rather than waiting for the JS call to message
+        // Java and wait for the Activity to launch, we just
+        // don't test these.
+        /*
+        android.app.Activity activity = mSolo.getCurrentActivity();
+        System.out.println("Current activity: " + activity);
+        mAsserter.ok(activity instanceof FxAccountGetStartedActivity, "checking activity", "setup activity launched");
+        */
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tests/testAccounts.js
@@ -0,0 +1,24 @@
+// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
+/* 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/. */
+
+Components.utils.import("resource://gre/modules/Accounts.jsm");
+
+add_task(function test_Accounts() {
+  let syncExists = yield Accounts.syncAccountsExist();
+  dump("Sync account exists? " + syncExists + "\n");
+  let firefoxExists = yield Accounts.firefoxAccountsExist();
+  dump("Firefox account exists? " + firefoxExists + "\n");
+  let anyExists = yield Accounts.anySyncAccountsExist();
+  dump("Any accounts exist? " + anyExists + "\n");
+
+  // Only one account should exist.
+  do_check_true(!syncExists || !firefoxExists);
+  do_check_eq(anyExists, firefoxExists || syncExists);
+
+  dump("Launching setup.\n");
+  Accounts.launchSetup();
+});
+
+run_next_test();
new file mode 100644
--- /dev/null
+++ b/mobile/android/modules/Accounts.jsm
@@ -0,0 +1,76 @@
+/* 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";
+
+this.EXPORTED_SYMBOLS = ["Accounts"];
+
+const { utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/Messaging.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Promise.jsm");
+
+/**
+ * A promise-based API for querying the existence of Sync accounts,
+ * and accessing the Sync setup wizard.
+ *
+ * Usage:
+ *
+ *   Cu.import("resource://gre/modules/Accounts.jsm");
+ *   Accounts.anySyncAccountsExist().then(
+ *     (exist) => {
+ *       console.log("Accounts exist? " + exist);
+ *       if (!exist) {
+ *         Accounts.launchSetup();
+ *       }
+ *     },
+ *     (err) => {
+ *       console.log("We failed so hard.");
+ *     }
+ *   );
+ */
+let Accounts = Object.freeze({
+  _accountsExist: function (kind) {
+    let deferred = Promise.defer();
+
+    sendMessageToJava({
+      type: "Accounts:Exist",
+      kind: kind,
+    }, (data, error) => {
+      if (error) {
+        deferred.reject(error);
+      } else {
+        deferred.resolve(JSON.parse(data).exists);
+      }
+    });
+
+    return deferred.promise;
+  },
+
+  firefoxAccountsExist: function () {
+    return this._accountsExist("fxa");
+  },
+
+  syncAccountsExist: function () {
+    return this._accountsExist("sync11");
+  },
+
+  anySyncAccountsExist: function () {
+    return this._accountsExist("any");
+  },
+
+  /**
+   * Fire-and-forget: open the Firefox accounts activity, which
+   * will be the Getting Started screen if FxA isn't yet set up.
+   *
+   * There is no return value from this method.
+   */
+  launchSetup: function () {
+    sendMessageToJava({
+      type: "Accounts:Create",
+    });
+  },
+});
+
--- a/mobile/android/modules/moz.build
+++ b/mobile/android/modules/moz.build
@@ -1,15 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 EXTRA_JS_MODULES += [
+    'Accounts.jsm',
     'ContactService.jsm',
     'HelperApps.jsm',
     'Home.jsm',
     'HomeProvider.jsm',
     'JNI.jsm',
     'LightweightThemeConsumer.jsm',
     'Messaging.jsm',
     'Notifications.jsm',