summaryrefslogtreecommitdiffstats
path: root/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/activities/FxAccountStatusFragment.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/services/src/main/java/org/mozilla/gecko/fxa/activities/FxAccountStatusFragment.java')
-rw-r--r--mobile/android/services/src/main/java/org/mozilla/gecko/fxa/activities/FxAccountStatusFragment.java949
1 files changed, 0 insertions, 949 deletions
diff --git a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/activities/FxAccountStatusFragment.java b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/activities/FxAccountStatusFragment.java
deleted file mode 100644
index a30b92e5f..000000000
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/activities/FxAccountStatusFragment.java
+++ /dev/null
@@ -1,949 +0,0 @@
-/* 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.fxa.activities;
-
-import android.accounts.Account;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SharedPreferences;
-import android.os.Bundle;
-import android.os.Handler;
-import android.preference.CheckBoxPreference;
-import android.preference.EditTextPreference;
-import android.preference.Preference;
-import android.preference.Preference.OnPreferenceChangeListener;
-import android.preference.Preference.OnPreferenceClickListener;
-import android.preference.PreferenceCategory;
-import android.preference.PreferenceScreen;
-import android.support.v4.content.LocalBroadcastManager;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-
-import com.squareup.picasso.Picasso;
-import com.squareup.picasso.Target;
-
-import org.mozilla.gecko.AppConstants;
-import org.mozilla.gecko.R;
-import org.mozilla.gecko.background.common.log.Logger;
-import org.mozilla.gecko.background.fxa.FxAccountUtils;
-import org.mozilla.gecko.background.preferences.PreferenceFragment;
-import org.mozilla.gecko.fxa.FxAccountConstants;
-import org.mozilla.gecko.fxa.SyncStatusListener;
-import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
-import org.mozilla.gecko.fxa.login.Married;
-import org.mozilla.gecko.fxa.login.State;
-import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper;
-import org.mozilla.gecko.sync.ExtendedJSONObject;
-import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate;
-import org.mozilla.gecko.sync.SyncConfiguration;
-import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
-import org.mozilla.gecko.util.HardwareUtils;
-import org.mozilla.gecko.util.ThreadUtils;
-
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * A fragment that displays the status of an AndroidFxAccount.
- * <p>
- * The owning activity is responsible for providing an AndroidFxAccount at
- * appropriate times.
- */
-public class FxAccountStatusFragment
- extends PreferenceFragment
- implements OnPreferenceClickListener, OnPreferenceChangeListener {
- private static final String LOG_TAG = FxAccountStatusFragment.class.getSimpleName();
-
- /**
- * If a device claims to have synced before this date, we will assume it has never synced.
- */
- private static final Date EARLIEST_VALID_SYNCED_DATE;
-
- static {
- final Calendar c = GregorianCalendar.getInstance();
- c.set(2000, Calendar.JANUARY, 1, 0, 0, 0);
- EARLIEST_VALID_SYNCED_DATE = c.getTime();
- }
-
- // When a checkbox is toggled, wait 5 seconds (for other checkbox actions)
- // before trying to sync. Should we kill off the fragment before the sync
- // request happens, that's okay: the runnable will run if the UI thread is
- // still around to service it, and since we're not updating any UI, we'll just
- // schedule the sync as usual. See also comment below about garbage
- // collection.
- private static final long DELAY_IN_MILLISECONDS_BEFORE_REQUESTING_SYNC = 5 * 1000;
- private static final long LAST_SYNCED_TIME_UPDATE_INTERVAL_IN_MILLISECONDS = 60 * 1000;
- private static final long PROFILE_FETCH_RETRY_INTERVAL_IN_MILLISECONDS = 60 * 1000;
-
- private static final String[] STAGES_TO_SYNC_ON_DEVICE_NAME_CHANGE = new String[] { "clients" };
-
- // By default, the auth/account server preference is only shown when the
- // account is configured to use a custom server. In debug mode, this is set.
- private static boolean ALWAYS_SHOW_AUTH_SERVER = false;
-
- // By default, the Sync server preference is only shown when the account is
- // configured to use a custom Sync server. In debug mode, this is set.
- private static boolean ALWAYS_SHOW_SYNC_SERVER = false;
-
- protected PreferenceCategory accountCategory;
- protected Preference profilePreference;
- protected Preference manageAccountPreference;
- protected Preference authServerPreference;
- protected Preference removeAccountPreference;
-
- protected Preference needsPasswordPreference;
- protected Preference needsUpgradePreference;
- protected Preference needsVerificationPreference;
- protected Preference needsMasterSyncAutomaticallyEnabledPreference;
- protected Preference needsFinishMigratingPreference;
-
- protected PreferenceCategory syncCategory;
-
- protected CheckBoxPreference bookmarksPreference;
- protected CheckBoxPreference historyPreference;
- protected CheckBoxPreference tabsPreference;
- protected CheckBoxPreference passwordsPreference;
- protected CheckBoxPreference readingListPreference;
-
- protected EditTextPreference deviceNamePreference;
- protected Preference syncServerPreference;
- protected Preference morePreference;
- protected Preference syncNowPreference;
-
- protected volatile AndroidFxAccount fxAccount;
- // The contract is: when fxAccount is non-null, then clientsDataDelegate is
- // non-null. If violated then an IllegalStateException is thrown.
- protected volatile SharedPreferencesClientsDataDelegate clientsDataDelegate;
-
- // Used to post delayed sync requests.
- protected Handler handler;
-
- // Member variable so that re-posting pushes back the already posted instance.
- // This Runnable references the fxAccount above, but it is not specific to a
- // single account. (That is, it does not capture a single account instance.)
- protected Runnable requestSyncRunnable;
-
- // Runnable to update last synced time.
- protected Runnable lastSyncedTimeUpdateRunnable;
-
- // Broadcast Receiver to update profile Information.
- protected FxAccountProfileInformationReceiver accountProfileInformationReceiver;
-
- protected final InnerSyncStatusDelegate syncStatusDelegate = new InnerSyncStatusDelegate();
- private Target profileAvatarTarget;
-
- protected Preference ensureFindPreference(String key) {
- Preference preference = findPreference(key);
- if (preference == null) {
- throw new IllegalStateException("Could not find preference with key: " + key);
- }
- return preference;
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- // We need to do this before we can query the hardware menu button state.
- // We're guaranteed to have an activity at this point (onAttach is called
- // before onCreate). It's okay to call this multiple times (with different
- // contexts).
- HardwareUtils.init(getActivity());
-
- addPreferences();
- }
-
- protected void addPreferences() {
- addPreferencesFromResource(R.xml.fxaccount_status_prefscreen);
-
- accountCategory = (PreferenceCategory) ensureFindPreference("signed_in_as_category");
- profilePreference = ensureFindPreference("profile");
- manageAccountPreference = ensureFindPreference("manage_account");
- authServerPreference = ensureFindPreference("auth_server");
- removeAccountPreference = ensureFindPreference("remove_account");
-
- needsPasswordPreference = ensureFindPreference("needs_credentials");
- needsUpgradePreference = ensureFindPreference("needs_upgrade");
- needsVerificationPreference = ensureFindPreference("needs_verification");
- needsMasterSyncAutomaticallyEnabledPreference = ensureFindPreference("needs_master_sync_automatically_enabled");
- needsFinishMigratingPreference = ensureFindPreference("needs_finish_migrating");
-
- syncCategory = (PreferenceCategory) ensureFindPreference("sync_category");
-
- bookmarksPreference = (CheckBoxPreference) ensureFindPreference("bookmarks");
- historyPreference = (CheckBoxPreference) ensureFindPreference("history");
- tabsPreference = (CheckBoxPreference) ensureFindPreference("tabs");
- passwordsPreference = (CheckBoxPreference) ensureFindPreference("passwords");
-
- if (!FxAccountUtils.LOG_PERSONAL_INFORMATION) {
- removeDebugButtons();
- } else {
- connectDebugButtons();
- ALWAYS_SHOW_AUTH_SERVER = true;
- ALWAYS_SHOW_SYNC_SERVER = true;
- }
-
- profilePreference.setOnPreferenceClickListener(this);
- manageAccountPreference.setOnPreferenceClickListener(this);
- removeAccountPreference.setOnPreferenceClickListener(this);
-
- needsPasswordPreference.setOnPreferenceClickListener(this);
- needsVerificationPreference.setOnPreferenceClickListener(this);
- needsFinishMigratingPreference.setOnPreferenceClickListener(this);
-
- bookmarksPreference.setOnPreferenceClickListener(this);
- historyPreference.setOnPreferenceClickListener(this);
- tabsPreference.setOnPreferenceClickListener(this);
- passwordsPreference.setOnPreferenceClickListener(this);
-
- deviceNamePreference = (EditTextPreference) ensureFindPreference("device_name");
- deviceNamePreference.setOnPreferenceChangeListener(this);
-
- syncServerPreference = ensureFindPreference("sync_server");
- morePreference = ensureFindPreference("more");
- morePreference.setOnPreferenceClickListener(this);
-
- syncNowPreference = ensureFindPreference("sync_now");
- syncNowPreference.setEnabled(true);
- syncNowPreference.setOnPreferenceClickListener(this);
-
- ensureFindPreference("linktos").setOnPreferenceClickListener(this);
- ensureFindPreference("linkprivacy").setOnPreferenceClickListener(this);
- }
-
- /**
- * We intentionally don't refresh here. Our owning activity is responsible for
- * providing an AndroidFxAccount to our refresh method in its onResume method.
- */
- @Override
- public void onResume() {
- super.onResume();
- }
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- if (preference == profilePreference) {
- ActivityUtils.openURLInFennec(getActivity().getApplicationContext(), "about:accounts?action=avatar");
- return true;
- }
-
- if (preference == manageAccountPreference) {
- ActivityUtils.openURLInFennec(getActivity().getApplicationContext(), "about:accounts?action=manage");
- return true;
- }
-
- if (preference == removeAccountPreference) {
- FxAccountStatusActivity.maybeDeleteAndroidAccount(getActivity(), fxAccount.getAndroidAccount(), null);
- return true;
- }
-
- if (preference == needsPasswordPreference) {
- final Intent intent = new Intent(FxAccountConstants.ACTION_FXA_UPDATE_CREDENTIALS);
- intent.putExtra(FxAccountWebFlowActivity.EXTRA_ENDPOINT, FxAccountConstants.ENDPOINT_PREFERENCES);
- // 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(FxAccountConstants.ACTION_FXA_FINISH_MIGRATING);
- intent.putExtra(FxAccountWebFlowActivity.EXTRA_ENDPOINT, FxAccountConstants.ENDPOINT_PREFERENCES);
- // 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) {
- final Intent intent = new Intent(FxAccountConstants.ACTION_FXA_CONFIRM_ACCOUNT);
- // 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);
- intent.putExtra(FxAccountWebFlowActivity.EXTRA_ENDPOINT, FxAccountConstants.ENDPOINT_PREFERENCES);
- 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) {
- fxAccount.requestImmediateSync(null, null);
- }
- return true;
- }
-
- if (TextUtils.equals("linktos", preference.getKey())) {
- ActivityUtils.openURLInFennec(getActivity().getApplicationContext(), getResources().getString(R.string.fxaccount_link_tos));
- return true;
- }
-
- if (TextUtils.equals("linkprivacy", preference.getKey())) {
- ActivityUtils.openURLInFennec(getActivity().getApplicationContext(), getResources().getString(R.string.fxaccount_link_pn));
- return true;
- }
-
- return false;
- }
-
- protected void setCheckboxesEnabled(boolean enabled) {
- bookmarksPreference.setEnabled(enabled);
- historyPreference.setEnabled(enabled);
- tabsPreference.setEnabled(enabled);
- passwordsPreference.setEnabled(enabled);
- // Since we can't sync, we can't update our remote client record.
- deviceNamePreference.setEnabled(enabled);
- syncNowPreference.setEnabled(enabled);
- }
-
- /**
- * Show at most one error preference, hiding all others.
- *
- * @param errorPreferenceToShow
- * single error preference to show; if null, hide all error preferences
- */
- protected void showOnlyOneErrorPreference(Preference errorPreferenceToShow) {
- final Preference[] errorPreferences = new Preference[] {
- this.needsPasswordPreference,
- this.needsUpgradePreference,
- this.needsVerificationPreference,
- this.needsMasterSyncAutomaticallyEnabledPreference,
- this.needsFinishMigratingPreference,
- };
- for (Preference errorPreference : errorPreferences) {
- final boolean currentlyShown = null != findPreference(errorPreference.getKey());
- final boolean shouldBeShown = errorPreference == errorPreferenceToShow;
- if (currentlyShown == shouldBeShown) {
- continue;
- }
- if (shouldBeShown) {
- syncCategory.addPreference(errorPreference);
- } else {
- syncCategory.removePreference(errorPreference);
- }
- }
- }
-
- protected void showNeedsPassword() {
- syncCategory.setTitle(R.string.fxaccount_status_sync);
- showOnlyOneErrorPreference(needsPasswordPreference);
- setCheckboxesEnabled(false);
- }
-
- protected void showNeedsUpgrade() {
- syncCategory.setTitle(R.string.fxaccount_status_sync);
- showOnlyOneErrorPreference(needsUpgradePreference);
- setCheckboxesEnabled(false);
- }
-
- protected void showNeedsVerification() {
- syncCategory.setTitle(R.string.fxaccount_status_sync);
- showOnlyOneErrorPreference(needsVerificationPreference);
- setCheckboxesEnabled(false);
- }
-
- protected void showNeedsMasterSyncAutomaticallyEnabled() {
- syncCategory.setTitle(R.string.fxaccount_status_sync);
- needsMasterSyncAutomaticallyEnabledPreference.setTitle(AppConstants.Versions.preLollipop ?
- R.string.fxaccount_status_needs_master_sync_automatically_enabled :
- R.string.fxaccount_status_needs_master_sync_automatically_enabled_v21);
- showOnlyOneErrorPreference(needsMasterSyncAutomaticallyEnabledPreference);
- setCheckboxesEnabled(false);
- }
-
- protected void showNeedsFinishMigrating() {
- syncCategory.setTitle(R.string.fxaccount_status_sync);
- showOnlyOneErrorPreference(needsFinishMigratingPreference);
- setCheckboxesEnabled(false);
- }
-
- protected void showConnected() {
- syncCategory.setTitle(R.string.fxaccount_status_sync_enabled);
- showOnlyOneErrorPreference(null);
- setCheckboxesEnabled(true);
- }
-
- protected class InnerSyncStatusDelegate implements SyncStatusListener {
- protected final Runnable refreshRunnable = new Runnable() {
- @Override
- public void run() {
- refresh();
- }
- };
-
- @Override
- public Context getContext() {
- return FxAccountStatusFragment.this.getActivity();
- }
-
- @Override
- public Account getAccount() {
- return fxAccount.getAndroidAccount();
- }
-
- @Override
- public void onSyncStarted() {
- if (fxAccount == null) {
- return;
- }
- Logger.info(LOG_TAG, "Got sync started message; refreshing.");
- getActivity().runOnUiThread(refreshRunnable);
- }
-
- @Override
- public void onSyncFinished() {
- if (fxAccount == null) {
- return;
- }
- Logger.info(LOG_TAG, "Got sync finished message; refreshing.");
- getActivity().runOnUiThread(refreshRunnable);
- }
- }
-
- /**
- * 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);
-
- // 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();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- FxAccountSyncStatusHelper.getInstance().stopObserving(syncStatusDelegate);
-
- // Focus lost, remove scheduled update if any.
- if (lastSyncedTimeUpdateRunnable != null) {
- handler.removeCallbacks(lastSyncedTimeUpdateRunnable);
- }
-
- // Focus lost, unregister broadcast receiver.
- if (accountProfileInformationReceiver != null) {
- LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(accountProfileInformationReceiver);
- }
-
- if (profileAvatarTarget != null) {
- Picasso.with(getActivity()).cancelRequest(profileAvatarTarget);
- profileAvatarTarget = null;
- }
- }
-
- protected void hardRefresh() {
- // This is the only way to guarantee that the EditText dialogs created by
- // EditTextPreferences are re-created. This works around the issue described
- // at http://androiddev.orkitra.com/?p=112079.
- final PreferenceScreen statusScreen = (PreferenceScreen) ensureFindPreference("status_screen");
- statusScreen.removeAll();
- addPreferences();
-
- refresh();
- }
-
- 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();
- }
-
- // This is a helper function similar to TabsAccessor.getLastSyncedString() to calculate relative "Last synced" time span.
- private String getLastSyncedString(final long startTime) {
- if (new Date(startTime).before(EARLIEST_VALID_SYNCED_DATE)) {
- return getActivity().getString(R.string.fxaccount_status_never_synced);
- }
- final CharSequence relativeTimeSpanString = DateUtils.getRelativeTimeSpanString(startTime);
- return getActivity().getResources().getString(R.string.fxaccount_status_last_synced, relativeTimeSpanString);
- }
-
- 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();
- }
-
- private void updateProfileInformation() {
-
- 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);
- }
-
- /**
- * 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());
- }
-
- // 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);
- }
-
- private void scheduleAndUpdateLastSyncedTime() {
- final String lastSynced = getLastSyncedString(fxAccount.getLastSyncedTimestamp());
- syncNowPreference.setSummary(lastSynced);
- handler.postDelayed(lastSyncedTimeUpdateRunnable, LAST_SYNCED_TIME_UPDATE_INTERVAL_IN_MILLISECONDS);
- }
-
- protected void updateAuthServerPreference() {
- final String authServer = fxAccount.getAccountServerURI();
- final boolean shouldBeShown = ALWAYS_SHOW_AUTH_SERVER || !FxAccountConstants.DEFAULT_AUTH_SERVER_ENDPOINT.equals(authServer);
- final boolean currentlyShown = null != findPreference(authServerPreference.getKey());
- if (currentlyShown != shouldBeShown) {
- if (shouldBeShown) {
- accountCategory.addPreference(authServerPreference);
- } else {
- accountCategory.removePreference(authServerPreference);
- }
- }
- // 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.
- authServerPreference.setSummary(authServer);
- }
-
- 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);
- }
-
- /**
- * 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 engine selections to local shared preferences, and request a sync
- * to persist selections to remote storage.
- */
- protected void saveEngineSelections() {
- final Map<String, Boolean> engineSelections = new HashMap<String, Boolean>();
- engineSelections.put("bookmarks", bookmarksPreference.isChecked());
- engineSelections.put("history", historyPreference.isChecked());
- engineSelections.put("passwords", passwordsPreference.isChecked());
- engineSelections.put("tabs", tabsPreference.isChecked());
-
- // No GlobalSession.config, so store directly to shared prefs. We do this on
- // a background thread to avoid IO on the main thread and strict mode
- // warnings.
- new Thread(new PersistEngineSelectionsRunnable(engineSelections)).start();
- }
-
- protected void requestDelayedSync() {
- Logger.info(LOG_TAG, "Posting a delayed request for a sync sometime soon.");
- handler.removeCallbacks(requestSyncRunnable);
- handler.postDelayed(requestSyncRunnable, DELAY_IN_MILLISECONDS_BEFORE_REQUESTING_SYNC);
- }
-
- /**
- * Remove all traces of debug buttons. By default, no debug buttons are shown.
- */
- protected void removeDebugButtons() {
- final PreferenceScreen statusScreen = (PreferenceScreen) ensureFindPreference("status_screen");
- final PreferenceCategory debugCategory = (PreferenceCategory) ensureFindPreference("debug_category");
- statusScreen.removePreference(debugCategory);
- }
-
- /**
- * A Runnable that persists engine selections to shared prefs, and then
- * requests a delayed sync.
- * <p>
- * References the member <code>fxAccount</code> and is specific to the Android
- * account associated to that account.
- */
- protected class PersistEngineSelectionsRunnable implements Runnable {
- private final Map<String, Boolean> engineSelections;
-
- protected PersistEngineSelectionsRunnable(Map<String, Boolean> engineSelections) {
- this.engineSelections = engineSelections;
- }
-
- @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;
- }
- }
- }
-
- /**
- * A Runnable that requests a sync.
- * <p>
- * References the member <code>fxAccount</code>, but is not specific to the
- * Android account associated to that account.
- */
- protected class RequestSyncRunnable implements Runnable {
- @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.requestEventualSync(null, null);
- }
- }
-
- /**
- * The Runnable that schedules a future update and updates the last synced time.
- */
- protected class LastSyncTimeUpdateRunnable implements Runnable {
- @Override
- public void run() {
- scheduleAndUpdateLastSyncedTime();
- }
- }
-
- /**
- * Broadcast receiver to receive updates for the cached profile action.
- */
- public class FxAccountProfileInformationReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!intent.getAction().equals(FxAccountConstants.ACCOUNT_PROFILE_JSON_UPDATED_ACTION)) {
- return;
- }
-
- Logger.info(LOG_TAG, "Profile avatar cache update action broadcast received.");
- // Update the UI from cached profile json on the main thread.
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- updateProfileInformation();
- }
- });
- }
- }
-
- /**
- * A separate listener to separate debug logic from main code paths.
- */
- protected class DebugPreferenceClickListener implements OnPreferenceClickListener {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- final String key = preference.getKey();
- if ("debug_refresh".equals(key)) {
- Logger.info(LOG_TAG, "Refreshing.");
- refresh();
- } else if ("debug_dump".equals(key)) {
- fxAccount.dump();
- } else if ("debug_force_sync".equals(key)) {
- Logger.info(LOG_TAG, "Force syncing.");
- fxAccount.requestImmediateSync(null, null);
- // No sense refreshing, since the sync will complete in the future.
- } else if ("debug_forget_certificate".equals(key)) {
- State state = fxAccount.getState();
- try {
- Married married = (Married) state;
- Logger.info(LOG_TAG, "Moving to Cohabiting state: Forgetting certificate.");
- fxAccount.setState(married.makeCohabitingState());
- refresh();
- } catch (ClassCastException e) {
- Logger.info(LOG_TAG, "Not in Married state; can't forget certificate.");
- // Ignore.
- }
- } else if ("debug_invalidate_certificate".equals(key)) {
- State state = fxAccount.getState();
- try {
- Married married = (Married) state;
- Logger.info(LOG_TAG, "Invalidating certificate.");
- fxAccount.setState(married.makeCohabitingState().withCertificate("INVALID CERTIFICATE"));
- refresh();
- } catch (ClassCastException e) {
- Logger.info(LOG_TAG, "Not in Married state; can't invalidate certificate.");
- // Ignore.
- }
- } else if ("debug_require_password".equals(key)) {
- Logger.info(LOG_TAG, "Moving to Separated state: Forgetting password.");
- State state = fxAccount.getState();
- fxAccount.setState(state.makeSeparatedState());
- refresh();
- } else if ("debug_require_upgrade".equals(key)) {
- Logger.info(LOG_TAG, "Moving to Doghouse state: Requiring upgrade.");
- State state = fxAccount.getState();
- fxAccount.setState(state.makeDoghouseState());
- refresh();
- } else if ("debug_migrated_from_sync11".equals(key)) {
- Logger.info(LOG_TAG, "Moving to MigratedFromSync11 state: Requiring password.");
- State state = fxAccount.getState();
- fxAccount.setState(state.makeMigratedFromSync11State(null));
- refresh();
- } else if ("debug_make_account_stage".equals(key)) {
- Logger.info(LOG_TAG, "Moving Account endpoints, in place, to stage. Deleting Sync and RL prefs and requiring password.");
- fxAccount.unsafeTransitionToStageEndpoints();
- refresh();
- } else if ("debug_make_account_default".equals(key)) {
- Logger.info(LOG_TAG, "Moving Account endpoints, in place, to default (production). Deleting Sync and RL prefs and requiring password.");
- fxAccount.unsafeTransitionToDefaultEndpoints();
- refresh();
- } else {
- return false;
- }
- return true;
- }
- }
-
- /**
- * Iterate through debug buttons, adding a special debug preference click
- * listener to each of them.
- */
- protected void connectDebugButtons() {
- // Separate listener to really separate debug logic from main code paths.
- final OnPreferenceClickListener listener = new DebugPreferenceClickListener();
-
- // We don't want to use Android resource strings for debug UI, so we just
- // use the keys throughout.
- final PreferenceCategory debugCategory = (PreferenceCategory) ensureFindPreference("debug_category");
- debugCategory.setTitle(debugCategory.getKey());
-
- for (int i = 0; i < debugCategory.getPreferenceCount(); i++) {
- final Preference button = debugCategory.getPreference(i);
- button.setTitle(button.getKey()); // Not very friendly, but this is for debugging only!
- button.setOnPreferenceClickListener(listener);
- }
- }
-
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- if (preference == deviceNamePreference) {
- String newClientName = (String) newValue;
- if (TextUtils.isEmpty(newClientName)) {
- newClientName = clientsDataDelegate.getDefaultClientName();
- }
- final long now = System.currentTimeMillis();
- clientsDataDelegate.setClientName(newClientName, now);
- // Force sync the client record, we want the user to see the device name change immediately
- // on the FxA Device Manager if possible ( = we are online) to avoid confusion
- // ("I changed my Android's device name but I don't see it on my computer").
- fxAccount.requestImmediateSync(STAGES_TO_SYNC_ON_DEVICE_NAME_CHANGE, null);
- hardRefresh(); // Updates the value displayed to the user, among other things.
- return true;
- }
-
- // For everything else, accept the change.
- return true;
- }
-}