@Override
 public void run() {
   // Name shadowing -- do you like it, or do you love it?
   AndroidFxAccount fxAccount = FxAccountStatusFragment.this.fxAccount;
   if (fxAccount == null) {
     return;
   }
   Logger.info(LOG_TAG, "Requesting a sync sometime soon.");
   fxAccount.requestSync();
 }
 protected Bundle getExtrasForAccount() {
   final Bundle extras = new Bundle();
   final ExtendedJSONObject o = new ExtendedJSONObject();
   o.put(FxAccountAbstractSetupActivity.JSON_KEY_AUTH, fxAccount.getAccountServerURI());
   final ExtendedJSONObject services = new ExtendedJSONObject();
   services.put(FxAccountAbstractSetupActivity.JSON_KEY_SYNC, fxAccount.getTokenServerURI());
   services.put(FxAccountAbstractSetupActivity.JSON_KEY_PROFILE, fxAccount.getProfileServerURI());
   o.put(FxAccountAbstractSetupActivity.JSON_KEY_SERVICES, services);
   extras.putString(FxAccountAbstractSetupActivity.EXTRA_EXTRAS, o.toJSONString());
   return extras;
 }
Beispiel #3
0
 /**
  * Is a sync currently in progress?
  *
  * @return true if Android is currently syncing the underlying Android Account.
  */
 public boolean isCurrentlySyncing() {
   boolean active = false;
   for (String authority : AndroidFxAccount.getAndroidAuthorities()) {
     active |= ContentResolver.isSyncActive(account, authority);
   }
   return active;
 }
 @Override
 public void run() {
   try {
     // Name shadowing -- do you like it, or do you love it?
     AndroidFxAccount fxAccount = FxAccountStatusFragment.this.fxAccount;
     if (fxAccount == null) {
       return;
     }
     Logger.info(LOG_TAG, "Persisting engine selections: " + engineSelections.toString());
     SyncConfiguration.storeSelectedEnginesToPrefs(fxAccount.getSyncPrefs(), engineSelections);
     requestDelayedSync();
   } catch (Exception e) {
     Logger.warn(LOG_TAG, "Got exception persisting selected engines; ignoring.", e);
     return;
   }
 }
  protected void refresh() {
    // refresh is called from our onResume, which can happen before the owning
    // Activity tells us about an account (via our public
    // refresh(AndroidFxAccount) method).
    if (fxAccount == null) {
      throw new IllegalArgumentException("fxAccount must not be null");
    }

    updateProfileInformation();
    updateAuthServerPreference();
    updateSyncServerPreference();

    try {
      // There are error states determined by Android, not the login state
      // machine, and we have a chance to present these states here. We handle
      // them specially, since we can't surface these states as part of syncing,
      // because they generally stop syncs from happening regularly. Right now
      // there are no such states.

      // Interrogate the Firefox Account's state.
      State state = fxAccount.getState();
      switch (state.getNeededAction()) {
        case NeedsUpgrade:
          showNeedsUpgrade();
          break;
        case NeedsPassword:
          showNeedsPassword();
          break;
        case NeedsVerification:
          showNeedsVerification();
          break;
        case NeedsFinishMigrating:
          showNeedsFinishMigrating();
          break;
        case None:
          showConnected();
          break;
      }

      // We check for the master setting last, since it is not strictly
      // necessary for the user to address this error state: it's really a
      // warning state. We surface it for the user's convenience, and to prevent
      // confused folks wondering why Sync is not working at all.
      final boolean masterSyncAutomatically = ContentResolver.getMasterSyncAutomatically();
      if (!masterSyncAutomatically) {
        showNeedsMasterSyncAutomaticallyEnabled();
        return;
      }
    } finally {
      // No matter our state, we should update the checkboxes.
      updateSelectedEngines();
    }

    final String clientName = clientsDataDelegate.getClientName();
    deviceNamePreference.setSummary(clientName);
    deviceNamePreference.setText(clientName);

    updateSyncNowPreference();
  }
  private void updateProfileInformation() {
    if (!AppConstants.MOZ_ANDROID_FIREFOX_ACCOUNT_PROFILES) {
      // Life is so much simpler.
      emailPreference.setTitle(fxAccount.getEmail());
      return;
    }

    final ExtendedJSONObject profileJSON = fxAccount.getProfileJSON();
    if (profileJSON == null) {
      // Update the profile title with email as the fallback.
      // Profile icon by default use the default avatar as the fallback.
      profilePreference.setTitle(fxAccount.getEmail());
      return;
    }

    updateProfileInformation(profileJSON);
  }
 protected void updateSyncNowPreference() {
   final boolean currentlySyncing = fxAccount.isCurrentlySyncing();
   syncNowPreference.setEnabled(!currentlySyncing);
   if (currentlySyncing) {
     syncNowPreference.setTitle(R.string.fxaccount_status_syncing);
   } else {
     syncNowPreference.setTitle(R.string.fxaccount_status_sync_now);
   }
   scheduleAndUpdateLastSyncedTime();
 }
  /**
   * Update profile information from json on UI thread.
   *
   * @param profileJSON json fetched from server.
   */
  protected void updateProfileInformation(final ExtendedJSONObject profileJSON) {
    // View changes must always be done on UI thread.
    ThreadUtils.assertOnUiThread();

    FxAccountUtils.pii(LOG_TAG, "Profile JSON is: " + profileJSON.toJSONString());

    final String userName = profileJSON.getString(FxAccountConstants.KEY_PROFILE_JSON_USERNAME);
    // Update the profile username and email if available.
    if (!TextUtils.isEmpty(userName)) {
      profilePreference.setTitle(userName);
      profilePreference.setSummary(fxAccount.getEmail());
    } else {
      profilePreference.setTitle(fxAccount.getEmail());
    }

    // Icon update from java is not supported prior to API 11, skip the avatar image fetch and
    // update for older device.
    if (!AppConstants.Versions.feature11Plus) {
      Logger.info(LOG_TAG, "Skipping profile image fetch for older pre-API 11 devices.");
      return;
    }

    // Avatar URI empty, skip profile image fetch.
    final String avatarURI = profileJSON.getString(FxAccountConstants.KEY_PROFILE_JSON_AVATAR);
    if (TextUtils.isEmpty(avatarURI)) {
      Logger.info(LOG_TAG, "AvatarURI is empty, skipping profile image fetch.");
      return;
    }

    // Using noPlaceholder would avoid a pop of the default image, but it's not available in the
    // version of Picasso
    // we ship in the tree.
    Picasso.with(getActivity())
        .load(avatarURI)
        .centerInside()
        .resizeDimen(R.dimen.fxaccount_profile_image_width, R.dimen.fxaccount_profile_image_height)
        .placeholder(R.drawable.sync_avatar_default)
        .error(R.drawable.sync_avatar_default)
        .into(profileAvatarTarget);
  }
  @Override
  public synchronized void onStatusChanged(int which) {
    for (Entry<FirefoxAccounts.SyncStatusListener, Boolean> entry : delegates.entrySet()) {
      final FirefoxAccounts.SyncStatusListener delegate = entry.getKey();
      final AndroidFxAccount fxAccount =
          new AndroidFxAccount(delegate.getContext(), delegate.getAccount());
      final boolean active = fxAccount.isCurrentlySyncing();
      // Remember for later.
      boolean wasActiveLastTime = entry.getValue();
      // It's okay to update the value of an entry while iterating the entrySet.
      entry.setValue(active);

      if (active && !wasActiveLastTime) {
        // We've started a sync.
        delegate.onSyncStarted();
      }
      if (!active && wasActiveLastTime) {
        // We've finished a sync.
        delegate.onSyncFinished();
      }
    }
  }
  /**
   * Notify the fragment that a new AndroidFxAccount instance is current.
   *
   * <p><b>Important:</b> call this method on the UI thread!
   *
   * <p>In future, this might be a Loader.
   *
   * @param fxAccount new instance.
   */
  public void refresh(AndroidFxAccount fxAccount) {
    if (fxAccount == null) {
      throw new IllegalArgumentException("fxAccount must not be null");
    }
    this.fxAccount = fxAccount;
    try {
      this.clientsDataDelegate =
          new SharedPreferencesClientsDataDelegate(
              fxAccount.getSyncPrefs(), getActivity().getApplicationContext());
    } catch (Exception e) {
      Logger.error(
          LOG_TAG, "Got exception fetching Sync prefs associated to Firefox Account; aborting.", e);
      // Something is terribly wrong; best to get a stack trace rather than
      // continue with a null clients delegate.
      throw new IllegalStateException(e);
    }

    handler = new Handler(); // Attached to current (assumed to be UI) thread.

    // Runnable is not specific to one Firefox Account. This runnable will keep
    // a reference to this fragment alive, but we expect posted runnables to be
    // serviced very quickly, so this is not an issue.
    requestSyncRunnable = new RequestSyncRunnable();
    lastSyncedTimeUpdateRunnable = new LastSyncTimeUpdateRunnable();

    // We would very much like register these status observers in bookended
    // onResume/onPause calls, but because the Fragment gets onResume during the
    // Activity's super.onResume, it hasn't yet been told its Firefox Account.
    // So we register the observer here (and remove it in onPause), and open
    // ourselves to the possibility that we don't have properly paired
    // register/unregister calls.
    FxAccountSyncStatusHelper.getInstance().startObserving(syncStatusDelegate);

    if (AppConstants.MOZ_ANDROID_FIREFOX_ACCOUNT_PROFILES) {
      // Register a local broadcast receiver to get profile cached notification.
      final IntentFilter intentFilter = new IntentFilter();
      intentFilter.addAction(FxAccountConstants.ACCOUNT_PROFILE_JSON_UPDATED_ACTION);
      accountProfileInformationReceiver = new FxAccountProfileInformationReceiver();
      LocalBroadcastManager.getInstance(getActivity())
          .registerReceiver(accountProfileInformationReceiver, intentFilter);

      // profilePreference is set during onCreate, so it's definitely not null here.
      final float cornerRadius =
          getResources().getDimension(R.dimen.fxaccount_profile_image_width) / 2;
      profileAvatarTarget =
          new PicassoPreferenceIconTarget(getResources(), profilePreference, cornerRadius);
    }

    refresh();
  }
 protected void updateSyncServerPreference() {
   final String syncServer = fxAccount.getTokenServerURI();
   final boolean shouldBeShown =
       ALWAYS_SHOW_SYNC_SERVER
           || !FxAccountConstants.DEFAULT_TOKEN_SERVER_ENDPOINT.equals(syncServer);
   final boolean currentlyShown = null != findPreference(syncServerPreference.getKey());
   if (currentlyShown != shouldBeShown) {
     if (shouldBeShown) {
       syncCategory.addPreference(syncServerPreference);
     } else {
       syncCategory.removePreference(syncServerPreference);
     }
   }
   // Always set the summary, because on first run, the preference is visible,
   // and the above block will be skipped if there is a custom value.
   syncServerPreference.setSummary(syncServer);
 }
 public void updateCredentials(String email, String password) {
   String serverURI = fxAccount.getAccountServerURI();
   Executor executor = Executors.newSingleThreadExecutor();
   FxAccountClient client = new FxAccountClient20(serverURI, executor);
   PasswordStretcher passwordStretcher = makePasswordStretcher(password);
   try {
     hideRemoteError();
     RequestDelegate<LoginResponse> delegate =
         new UpdateCredentialsDelegate(email, passwordStretcher, serverURI);
     new FxAccountSignInTask(
             this, this, email, passwordStretcher, client, getQueryParameters(), delegate)
         .execute();
   } catch (Exception e) {
     Logger.warn(LOG_TAG, "Got exception updating credentials for account.", e);
     showRemoteError(e, R.string.fxaccount_update_credentials_unknown_error);
   }
 }
  /**
   * Query shared prefs for the current engine state, and update the UI accordingly.
   *
   * <p>In future, we might want this to be on a background thread, or implemented as a Loader.
   */
  protected void updateSelectedEngines() {
    try {
      SharedPreferences syncPrefs = fxAccount.getSyncPrefs();
      Map<String, Boolean> engines = SyncConfiguration.getUserSelectedEngines(syncPrefs);
      if (engines != null) {
        bookmarksPreference.setChecked(
            engines.containsKey("bookmarks") && engines.get("bookmarks"));
        historyPreference.setChecked(engines.containsKey("history") && engines.get("history"));
        passwordsPreference.setChecked(
            engines.containsKey("passwords") && engines.get("passwords"));
        tabsPreference.setChecked(engines.containsKey("tabs") && engines.get("tabs"));
        return;
      }

      // We don't have user specified preferences.  Perhaps we have seen a meta/global?
      Set<String> enabledNames = SyncConfiguration.getEnabledEngineNames(syncPrefs);
      if (enabledNames != null) {
        bookmarksPreference.setChecked(enabledNames.contains("bookmarks"));
        historyPreference.setChecked(enabledNames.contains("history"));
        passwordsPreference.setChecked(enabledNames.contains("passwords"));
        tabsPreference.setChecked(enabledNames.contains("tabs"));
        return;
      }

      // Okay, we don't have userSelectedEngines or enabledEngines. That means
      // the user hasn't specified to begin with, we haven't specified here, and
      // we haven't already seen, Sync engines. We don't know our state, so
      // let's check everything (the default) and disable everything.
      bookmarksPreference.setChecked(true);
      historyPreference.setChecked(true);
      passwordsPreference.setChecked(true);
      tabsPreference.setChecked(true);
      setCheckboxesEnabled(false);
    } catch (Exception e) {
      Logger.warn(LOG_TAG, "Got exception getting engines to select; ignoring.", e);
      return;
    }
  }
  /**
   * Persist Firefox account to disk as a JSON object.
   *
   * @param AndroidFxAccount the account to persist to disk
   * @param filename name of file to persist to; must not contain path separators.
   */
  public static void pickle(final AndroidFxAccount account, final String filename) {
    final ExtendedJSONObject o = new ExtendedJSONObject();
    o.put(KEY_PICKLE_VERSION, Long.valueOf(PICKLE_VERSION));
    o.put(KEY_PICKLE_TIMESTAMP, Long.valueOf(System.currentTimeMillis()));

    o.put(KEY_ACCOUNT_VERSION, AndroidFxAccount.CURRENT_ACCOUNT_VERSION);
    o.put(KEY_ACCOUNT_TYPE, FxAccountConstants.ACCOUNT_TYPE);
    o.put(KEY_EMAIL, account.getEmail());
    o.put(KEY_PROFILE, account.getProfile());
    o.put(KEY_IDP_SERVER_URI, account.getAccountServerURI());
    o.put(KEY_TOKEN_SERVER_URI, account.getTokenServerURI());
    o.put(KEY_IS_SYNCING_ENABLED, account.isSyncing());

    // TODO: If prefs version changes under us, SyncPrefsPath will change, "clearing" prefs.

    final ExtendedJSONObject bundle = account.unbundle();
    if (bundle == null) {
      Logger.warn(LOG_TAG, "Unable to obtain account bundle; aborting.");
      return;
    }
    o.put(KEY_BUNDLE, bundle);

    writeToDisk(account.context, filename, o);
  }
  @Override
  public boolean onPreferenceClick(Preference preference) {
    if (preference == needsPasswordPreference) {
      Intent intent = new Intent(getActivity(), FxAccountUpdateCredentialsActivity.class);
      final Bundle extras = getExtrasForAccount();
      if (extras != null) {
        intent.putExtras(extras);
      }
      // Per http://stackoverflow.com/a/8992365, this triggers a known bug with
      // the soft keyboard not being shown for the started activity. Why, Android, why?
      intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
      startActivity(intent);

      return true;
    }

    if (preference == needsFinishMigratingPreference) {
      final Intent intent = new Intent(getActivity(), FxAccountFinishMigratingActivity.class);
      final Bundle extras = getExtrasForAccount();
      if (extras != null) {
        intent.putExtras(extras);
      }
      // Per http://stackoverflow.com/a/8992365, this triggers a known bug with
      // the soft keyboard not being shown for the started activity. Why, Android, why?
      intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
      startActivity(intent);

      return true;
    }

    if (preference == needsVerificationPreference) {
      FxAccountCodeResender.resendCode(getActivity().getApplicationContext(), fxAccount);

      Intent intent = new Intent(getActivity(), FxAccountConfirmAccountActivity.class);
      // Per http://stackoverflow.com/a/8992365, this triggers a known bug with
      // the soft keyboard not being shown for the started activity. Why, Android, why?
      intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
      startActivity(intent);

      return true;
    }

    if (preference == bookmarksPreference
        || preference == historyPreference
        || preference == passwordsPreference
        || preference == tabsPreference) {
      saveEngineSelections();
      return true;
    }

    if (preference == morePreference) {
      getActivity().openOptionsMenu();
      return true;
    }

    if (preference == syncNowPreference) {
      if (fxAccount != null) {
        FirefoxAccounts.requestSync(
            fxAccount.getAndroidAccount(), FirefoxAccounts.FORCE, null, null);
      }
      return true;
    }

    return false;
  }
Beispiel #16
0
  public static AndroidFxAccount addAndroidAccount(
      Context context,
      String email,
      String profile,
      String idpServerURI,
      String tokenServerURI,
      State state,
      final int accountVersion,
      final boolean syncEnabled,
      final boolean fromPickle,
      ExtendedJSONObject bundle)
      throws UnsupportedEncodingException, GeneralSecurityException, URISyntaxException {
    if (email == null) {
      throw new IllegalArgumentException("email must not be null");
    }
    if (profile == null) {
      throw new IllegalArgumentException("profile must not be null");
    }
    if (idpServerURI == null) {
      throw new IllegalArgumentException("idpServerURI must not be null");
    }
    if (tokenServerURI == null) {
      throw new IllegalArgumentException("tokenServerURI must not be null");
    }
    if (state == null) {
      throw new IllegalArgumentException("state must not be null");
    }

    // TODO: Add migration code.
    if (accountVersion != CURRENT_ACCOUNT_VERSION) {
      throw new IllegalStateException(
          "Could not create account of version "
              + accountVersion
              + ". Current version is "
              + CURRENT_ACCOUNT_VERSION
              + ".");
    }

    // Android has internal restrictions that require all values in this
    // bundle to be strings. *sigh*
    Bundle userdata = new Bundle();
    userdata.putString(ACCOUNT_KEY_ACCOUNT_VERSION, "" + CURRENT_ACCOUNT_VERSION);
    userdata.putString(ACCOUNT_KEY_IDP_SERVER, idpServerURI);
    userdata.putString(ACCOUNT_KEY_TOKEN_SERVER, tokenServerURI);
    userdata.putString(ACCOUNT_KEY_AUDIENCE, FxAccountUtils.getAudienceForURL(tokenServerURI));
    userdata.putString(ACCOUNT_KEY_PROFILE, profile);

    if (bundle == null) {
      bundle = new ExtendedJSONObject();
      // TODO: How to upgrade?
      bundle.put(BUNDLE_KEY_BUNDLE_VERSION, CURRENT_BUNDLE_VERSION);
    }
    bundle.put(BUNDLE_KEY_STATE_LABEL, state.getStateLabel().name());
    bundle.put(BUNDLE_KEY_STATE, state.toJSONObject().toJSONString());

    userdata.putString(ACCOUNT_KEY_DESCRIPTOR, bundle.toJSONString());

    Account account = new Account(email, FxAccountConstants.ACCOUNT_TYPE);
    AccountManager accountManager = AccountManager.get(context);
    // We don't set an Android password, because we don't want to persist the
    // password (or anything else as powerful as the password). Instead, we
    // internally manage a sessionToken with a remotely owned lifecycle.
    boolean added = accountManager.addAccountExplicitly(account, null, userdata);
    if (!added) {
      return null;
    }

    // Try to work around an intermittent issue described at
    // http://stackoverflow.com/a/11698139.  What happens is that tests that
    // delete and re-create the same account frequently will find the account
    // missing all or some of the userdata bundle, possibly due to an Android
    // AccountManager caching bug.
    for (String key : userdata.keySet()) {
      accountManager.setUserData(account, key, userdata.getString(key));
    }

    AndroidFxAccount fxAccount = new AndroidFxAccount(context, account);

    if (!fromPickle) {
      fxAccount.clearSyncPrefs();
    }

    if (syncEnabled) {
      fxAccount.enableSyncing();
    } else {
      fxAccount.disableSyncing();
    }

    return fxAccount;
  }
  /**
   * Create Android account from saved JSON object. Assumes that an account does not exist.
   *
   * @param context Android context.
   * @param filename name of file to read from; must not contain path separators.
   * @return created Android account, or null on error.
   */
  public static AndroidFxAccount unpickle(final Context context, final String filename) {
    final String jsonString = Utils.readFile(context, filename);
    if (jsonString == null) {
      Logger.info(LOG_TAG, "Pickle file '" + filename + "' not found; aborting.");
      return null;
    }

    ExtendedJSONObject json = null;
    try {
      json = ExtendedJSONObject.parseJSONObject(jsonString);
    } catch (Exception e) {
      Logger.warn(LOG_TAG, "Got exception reading pickle file '" + filename + "'; aborting.", e);
      return null;
    }

    final UnpickleParams params;
    try {
      params = UnpickleParams.fromJSON(json);
    } catch (Exception e) {
      Logger.warn(LOG_TAG, "Got exception extracting unpickle json; aborting.", e);
      return null;
    }

    final AndroidFxAccount account;
    try {
      account =
          AndroidFxAccount.addAndroidAccount(
              context,
              params.email,
              params.profile,
              params.idpServerURI,
              params.tokenServerURI,
              params.state,
              params.accountVersion,
              params.isSyncingEnabled,
              true,
              params.bundle);
    } catch (Exception e) {
      Logger.warn(LOG_TAG, "Exception when adding Android Account; aborting.", e);
      return null;
    }

    if (account == null) {
      Logger.warn(LOG_TAG, "Failed to add Android Account; aborting.");
      return null;
    }

    Long timestamp = json.getLong(KEY_PICKLE_TIMESTAMP);
    if (timestamp == null) {
      Logger.warn(LOG_TAG, "Did not find timestamp in pickle file; ignoring.");
      timestamp = Long.valueOf(-1);
    }

    Logger.info(
        LOG_TAG,
        "Un-pickled Android account named "
            + params.email
            + " (version "
            + params.pickleVersion
            + ", pickled at "
            + timestamp
            + ").");

    return account;
  }
 private void scheduleAndUpdateLastSyncedTime() {
   final String lastSynced = getLastSyncedString(fxAccount.getLastSyncedTimestamp());
   syncNowPreference.setSummary(lastSynced);
   handler.postDelayed(
       lastSyncedTimeUpdateRunnable, LAST_SYNCED_TIME_UPDATE_INTERVAL_IN_MILLISECONDS);
 }
    @Override
    public void handleSuccess(LoginResponse result) {
      Logger.info(LOG_TAG, "Got success response; adding Android account.");

      // We're on the UI thread, but it's okay to create the account here.
      AndroidFxAccount fxAccount;
      try {
        final String profile = Constants.DEFAULT_PROFILE;
        final String tokenServerURI = getTokenServerEndpoint();
        // It is crucial that we use the email address provided by the server
        // (rather than whatever the user entered), because the user's keys are
        // wrapped and salted with the initial email they provided to
        // /create/account. Of course, we want to pass through what the user
        // entered locally as much as possible, so we create the Android account
        // with their entered email address, etc.
        // The passwordStretcher should have seen this email address before, so
        // we shouldn't be calculating the expensive stretch twice.
        byte[] quickStretchedPW =
            passwordStretcher.getQuickStretchedPW(result.remoteEmail.getBytes("UTF-8"));
        byte[] unwrapkB = FxAccountUtils.generateUnwrapBKey(quickStretchedPW);
        State state =
            new Engaged(
                email,
                result.uid,
                result.verified,
                unwrapkB,
                result.sessionToken,
                result.keyFetchToken);
        fxAccount =
            AndroidFxAccount.addAndroidAccount(
                getApplicationContext(), email, profile, serverURI, tokenServerURI, state);
        if (fxAccount == null) {
          throw new RuntimeException("Could not add Android account.");
        }

        if (selectedEngines != null) {
          Logger.info(LOG_TAG, "User has selected engines; storing to prefs.");
          SyncConfiguration.storeSelectedEnginesToPrefs(fxAccount.getSyncPrefs(), selectedEngines);
        }
      } catch (Exception e) {
        handleError(e);
        return;
      }

      // For great debugging.
      if (FxAccountUtils.LOG_PERSONAL_INFORMATION) {
        fxAccount.dump();
      }

      // The GetStarted activity has called us and needs to return a result to the authenticator.
      final Intent intent = new Intent();
      intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, email);
      intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, FxAccountConstants.ACCOUNT_TYPE);
      // intent.putExtra(AccountManager.KEY_AUTHTOKEN, accountType);
      setResult(RESULT_OK, intent);

      // Show success activity depending on verification status.
      Intent successIntent = makeSuccessIntent(email, result);
      startActivity(successIntent);
      finish();
    }