Move Account & sync settings into Settings app.

Fragmentized some of the activities and moved buttons into the menu area.

Bug: 3148844
diff --git a/src/com/android/settings/AccountPreference.java b/src/com/android/settings/AccountPreference.java
index dc56334..4378399 100644
--- a/src/com/android/settings/AccountPreference.java
+++ b/src/com/android/settings/AccountPreference.java
@@ -52,10 +52,6 @@
         setWidgetLayoutResource(R.layout.account_preference);
         setTitle(mAccount.name);
         setSummary("");
-        // Add account info to the intent for AccountSyncSettings
-        Intent intent = new Intent("android.settings.ACCOUNT_SYNC_SETTINGS");
-        intent.putExtra("account", mAccount);
-        setIntent(intent);
         setPersistent(false);
         setSyncStatus(SYNC_DISABLED);
     }
diff --git a/src/com/android/settings/DialogCreatable.java b/src/com/android/settings/DialogCreatable.java
new file mode 100644
index 0000000..1d10be7
--- /dev/null
+++ b/src/com/android/settings/DialogCreatable.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings;
+
+import android.app.Dialog;
+
+/**
+ * Letting the class, assumed to be Fragment, create a Dialog on it. Should be useful
+ * you want to utilize some capability in {@link SettingsPreferenceFragment} but don't want
+ * the class inherit the class itself (See {@link ProxySelector} for example).
+ */
+public interface DialogCreatable {
+
+    public Dialog onCreateDialog(int dialogId);
+}
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index c94a0e1..a47fa56 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -208,4 +208,6 @@
     public static class VoiceInputOutputSettingsActivity extends Settings { }
     public static class ManageAccountsSettingsActivity extends Settings { }
     public static class PowerUsageSummaryActivity extends Settings { }
+    public static class AccountSyncSettingsActivity extends Settings { }
+    public static class AccountSyncSettingsInAddAccountActivity extends Settings { }
 }
diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java
index 3c771f5..a2f701d 100644
--- a/src/com/android/settings/SettingsPreferenceFragment.java
+++ b/src/com/android/settings/SettingsPreferenceFragment.java
@@ -34,15 +34,6 @@
 import android.widget.Button;
 
 /**
- * Letting the class, assumed to be Fragment, create a Dialog on it. Should be useful
- * you want to utilize some capability in {@link SettingsPreferenceFragment} but don't want
- * the class inherit the class itself (See {@link ProxySelector} for example).
- */
-interface DialogCreatable {
-    public Dialog onCreateDialog(int dialogId);
-}
-
-/**
  * Base class for Settings fragments, with some helper functions and dialog management.
  */
 public class SettingsPreferenceFragment extends PreferenceFragment
@@ -122,12 +113,12 @@
         mDialogFragment = null;
     }
 
-    static class SettingsDialogFragment extends DialogFragment {
+    public static class SettingsDialogFragment extends DialogFragment {
         private int mDialogId;
 
         private DialogCreatable mFragment;
 
-        SettingsDialogFragment(DialogCreatable fragment, int dialogId) {
+        public SettingsDialogFragment(DialogCreatable fragment, int dialogId) {
             mDialogId = dialogId;
             mFragment = fragment;
         }
diff --git a/src/com/android/settings/accounts/AccountPreferenceBase.java b/src/com/android/settings/accounts/AccountPreferenceBase.java
new file mode 100644
index 0000000..a84bece
--- /dev/null
+++ b/src/com/android/settings/accounts/AccountPreferenceBase.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.accounts;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.android.settings.SettingsPreferenceFragment;
+import com.google.android.collect.Maps;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AuthenticatorDescription;
+import android.accounts.OnAccountsUpdateListener;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.SyncAdapterType;
+import android.content.SyncStatusObserver;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceScreen;
+import android.util.Log;
+
+class AccountPreferenceBase extends SettingsPreferenceFragment
+        implements OnAccountsUpdateListener {
+
+    protected static final String TAG = "AccountSettings";
+    public static final String AUTHORITIES_FILTER_KEY = "authorities";
+    public static final String ACCOUNT_TYPES_FILTER_KEY = "account_types";
+    private Map<String, AuthenticatorDescription> mTypeToAuthDescription
+            = new HashMap<String, AuthenticatorDescription>();
+    protected AuthenticatorDescription[] mAuthDescs;
+    private final Handler mHandler = new Handler();
+    private Object mStatusChangeListenerHandle;
+    private HashMap<String, ArrayList<String>> mAccountTypeToAuthorities = null;
+
+    /**
+     * Overload to handle account updates.
+     */
+    public void onAccountsUpdated(Account[] accounts) {
+
+    }
+
+    /**
+     * Overload to handle authenticator description updates
+     */
+    protected void onAuthDescriptionsUpdated() {
+
+    }
+
+    /**
+     * Overload to handle sync state updates.
+     */
+    protected void onSyncStateUpdated() {
+
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mStatusChangeListenerHandle = ContentResolver.addStatusChangeListener(
+                ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE
+                | ContentResolver.SYNC_OBSERVER_TYPE_STATUS
+                | ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS,
+                mSyncStatusObserver);
+        onSyncStateUpdated();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        ContentResolver.removeStatusChangeListener(mStatusChangeListenerHandle);
+    }
+
+
+    private SyncStatusObserver mSyncStatusObserver = new SyncStatusObserver() {
+        public void onStatusChanged(int which) {
+            mHandler.post(new Runnable() {
+                public void run() {
+                    onSyncStateUpdated();
+                }
+            });
+        }
+    };
+
+    public ArrayList<String> getAuthoritiesForAccountType(String type) {
+        if (mAccountTypeToAuthorities == null) {
+            mAccountTypeToAuthorities = Maps.newHashMap();
+            SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypes();
+            for (int i = 0, n = syncAdapters.length; i < n; i++) {
+                final SyncAdapterType sa = syncAdapters[i];
+                ArrayList<String> authorities = mAccountTypeToAuthorities.get(sa.accountType);
+                if (authorities == null) {
+                    authorities = new ArrayList<String>();
+                    mAccountTypeToAuthorities.put(sa.accountType, authorities);
+                }
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.d(TAG, "added authority " + sa.authority + " to accountType " 
+                            + sa.accountType);
+                }
+                authorities.add(sa.authority);
+            }
+        }
+        return mAccountTypeToAuthorities.get(type);
+    }
+
+    /**
+     * Gets an icon associated with a particular account type. If none found, return null.
+     * @param accountType the type of account
+     * @return a drawable for the icon or null if one cannot be found.
+     */
+    protected Drawable getDrawableForType(final String accountType) {
+        Drawable icon = null;
+        if (mTypeToAuthDescription.containsKey(accountType)) {
+            try {
+                AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType);
+                Context authContext = getActivity().createPackageContext(desc.packageName, 0);
+                icon = authContext.getResources().getDrawable(desc.iconId);
+            } catch (PackageManager.NameNotFoundException e) {
+                // TODO: place holder icon for missing account icons?
+                Log.w(TAG, "No icon for account type " + accountType);
+            }
+        }
+        return icon;
+    }
+
+    /**
+     * Gets the label associated with a particular account type. If none found, return null.
+     * @param accountType the type of account
+     * @return a CharSequence for the label or null if one cannot be found.
+     */
+    protected CharSequence getLabelForType(final String accountType) {
+        CharSequence label = null;
+        if (mTypeToAuthDescription.containsKey(accountType)) {
+            try {
+                AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType);
+                Context authContext = getActivity().createPackageContext(desc.packageName, 0);
+                label = authContext.getResources().getText(desc.labelId);
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.w(TAG, "No label for account type " + ", type " + accountType);
+            }
+        }
+        return label;
+    }
+
+    /**
+     * Gets the preferences.xml file associated with a particular account type.
+     * @param accountType the type of account
+     * @return a PreferenceScreen inflated from accountPreferenceId.
+     */
+    protected PreferenceScreen addPreferencesForType(final String accountType) {
+        PreferenceScreen prefs = null;
+        if (mTypeToAuthDescription.containsKey(accountType)) {
+            AuthenticatorDescription desc = null;
+            try {
+                desc = mTypeToAuthDescription.get(accountType);
+                if (desc != null && desc.accountPreferencesId != 0) {
+                    Context authContext = getActivity().createPackageContext(desc.packageName, 0);
+                    prefs = getPreferenceManager().inflateFromResource(authContext,
+                            desc.accountPreferencesId, getPreferenceScreen());
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.w(TAG, "Couldn't load preferences.xml file from " + desc.packageName);
+            }
+        }
+        return prefs;
+    }
+
+    /**
+     * Updates provider icons. Subclasses should call this in onCreate()
+     * and update any UI that depends on AuthenticatorDescriptions in onAuthDescriptionsUpdated().
+     */
+    protected void updateAuthDescriptions() {
+        mAuthDescs = AccountManager.get(getActivity()).getAuthenticatorTypes();
+        for (int i = 0; i < mAuthDescs.length; i++) {
+            mTypeToAuthDescription.put(mAuthDescs[i].type, mAuthDescs[i]);
+        }
+        onAuthDescriptionsUpdated();
+    }
+}
diff --git a/src/com/android/settings/accounts/AccountSyncSettings.java b/src/com/android/settings/accounts/AccountSyncSettings.java
new file mode 100644
index 0000000..141f244
--- /dev/null
+++ b/src/com/android/settings/accounts/AccountSyncSettings.java
@@ -0,0 +1,505 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.accounts;
+
+import com.android.settings.R;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SyncAdapterType;
+import android.content.SyncInfo;
+import android.content.SyncStatusInfo;
+import android.content.pm.ProviderInfo;
+import android.net.ConnectivityManager;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.text.TextUtils;
+import android.text.format.DateFormat;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+
+public class AccountSyncSettings extends AccountPreferenceBase {
+
+    public static final String ACCOUNT_KEY = "account";
+    protected static final int MENU_REMOVE_ACCOUNT_ID = Menu.FIRST;
+    private static final int MENU_SYNC_NOW_ID = Menu.FIRST + 1;
+    private static final int MENU_SYNC_CANCEL_ID = Menu.FIRST + 2;
+    private static final int REALLY_REMOVE_DIALOG = 100;
+    private static final int FAILED_REMOVAL_DIALOG = 101;
+    private static final int CANT_DO_ONETIME_SYNC_DIALOG = 102;
+    private TextView mUserId;
+    private TextView mProviderId;
+    private ImageView mProviderIcon;
+    private TextView mErrorInfoView;
+    protected View mRemoveAccountArea;
+    private java.text.DateFormat mDateFormat;
+    private java.text.DateFormat mTimeFormat;
+    private Account mAccount;
+    // List of all accounts, updated when accounts are added/removed
+    // We need to re-scan the accounts on sync events, in case sync state changes.
+    private Account[] mAccounts;
+    private Button mRemoveAccountButton;
+    private ArrayList<SyncStateCheckBoxPreference> mCheckBoxes =
+                new ArrayList<SyncStateCheckBoxPreference>();
+    private ArrayList<String> mInvisibleAdapters = Lists.newArrayList();
+
+    @Override
+    public Dialog onCreateDialog(final int id) {
+        Dialog dialog = null;
+        if (id == REALLY_REMOVE_DIALOG) {
+            dialog = new AlertDialog.Builder(getActivity())
+                .setTitle(R.string.really_remove_account_title)
+                .setMessage(R.string.really_remove_account_message)
+                .setNegativeButton(android.R.string.cancel, null)
+                .setPositiveButton(R.string.remove_account_label,
+                        new DialogInterface.OnClickListener() {
+                    public void onClick(DialogInterface dialog, int which) {
+                        AccountManager.get(AccountSyncSettings.this.getActivity())
+                                .removeAccount(mAccount,
+                                new AccountManagerCallback<Boolean>() {
+                            public void run(AccountManagerFuture<Boolean> future) {
+                                boolean failed = true;
+                                try {
+                                    if (future.getResult() == true) {
+                                        failed = false;
+                                    }
+                                } catch (OperationCanceledException e) {
+                                    // handled below
+                                } catch (IOException e) {
+                                    // handled below
+                                } catch (AuthenticatorException e) {
+                                    // handled below
+                                }
+                                if (failed) {
+                                    showDialog(FAILED_REMOVAL_DIALOG);
+                                } else {
+                                    finish();
+                                }
+                            }
+                        }, null);
+                    }
+                })
+                .create();
+        } else if (id == FAILED_REMOVAL_DIALOG) {
+            dialog = new AlertDialog.Builder(getActivity())
+                .setTitle(R.string.really_remove_account_title)
+                .setPositiveButton(android.R.string.ok, null)
+                .setMessage(R.string.remove_account_failed)
+                .create();
+        } else if (id == CANT_DO_ONETIME_SYNC_DIALOG) {
+            dialog = new AlertDialog.Builder(getActivity())
+                .setTitle(R.string.cant_sync_dialog_title)
+                .setMessage(R.string.cant_sync_dialog_message)
+                .setPositiveButton(android.R.string.ok, null)
+                .create();
+        }
+        return dialog;
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        setHasOptionsMenu(true);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        final View view = inflater.inflate(R.layout.account_sync_screen, container, false);
+        
+        initializeUi(view);
+
+        return view;
+    }
+
+    protected void initializeUi(final View rootView) {
+        addPreferencesFromResource(R.xml.account_sync_settings);
+
+        mErrorInfoView = (TextView) rootView.findViewById(R.id.sync_settings_error_info);
+        mErrorInfoView.setVisibility(View.GONE);
+        mErrorInfoView.setCompoundDrawablesWithIntrinsicBounds(
+                getResources().getDrawable(R.drawable.ic_list_syncerror), null, null, null);
+
+        mUserId = (TextView) rootView.findViewById(R.id.user_id);
+        mProviderId = (TextView) rootView.findViewById(R.id.provider_id);
+        mProviderIcon = (ImageView) rootView.findViewById(R.id.provider_icon);
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+
+        final Activity activity = getActivity();
+
+        mDateFormat = DateFormat.getDateFormat(activity);
+        mTimeFormat = DateFormat.getTimeFormat(activity);
+
+        mAccount = (Account) getArguments().getParcelable(ACCOUNT_KEY);
+        if (mAccount != null) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "Got account: " + mAccount);
+            mUserId.setText(mAccount.name);
+            mProviderId.setText(mAccount.type);
+        }
+    }
+
+    @Override
+    public void onResume() {
+        final Activity activity = getActivity();
+        AccountManager.get(activity).addOnAccountsUpdatedListener(this, null, false);
+        updateAuthDescriptions();
+        onAccountsUpdated(AccountManager.get(activity).getAccounts());
+
+        super.onResume();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        AccountManager.get(getActivity()).removeOnAccountsUpdatedListener(this);
+    }
+
+    private void addSyncStateCheckBox(Account account, String authority) {
+        SyncStateCheckBoxPreference item =
+                new SyncStateCheckBoxPreference(getActivity(), account, authority);
+        item.setPersistent(false);
+        final ProviderInfo providerInfo = getPackageManager().resolveContentProvider(authority, 0);
+        CharSequence providerLabel = providerInfo != null
+                ? providerInfo.loadLabel(getPackageManager()) : null;
+        if (TextUtils.isEmpty(providerLabel)) {
+            Log.e(TAG, "Provider needs a label for authority '" + authority + "'");
+            providerLabel = authority;
+        }
+        String title = getString(R.string.sync_item_title, providerLabel);
+        item.setTitle(title);
+        item.setKey(authority);
+        getPreferenceScreen().addPreference(item);
+        mCheckBoxes.add(item);
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        super.onCreateOptionsMenu(menu, inflater);
+
+        MenuItem removeAccount = menu.add(0, MENU_REMOVE_ACCOUNT_ID, 0,
+                                          getString(R.string.remove_account_label))
+                .setIcon(com.android.internal.R.drawable.ic_menu_delete);
+        MenuItem syncNow = menu.add(0, MENU_SYNC_NOW_ID, 0,
+                                          getString(R.string.sync_menu_sync_now))
+                .setIcon(com.android.internal.R.drawable.ic_menu_refresh);
+        MenuItem syncCancel = menu.add(0, MENU_SYNC_CANCEL_ID, 0,
+                                       getString(R.string.sync_menu_sync_cancel))
+                .setIcon(com.android.internal.R.drawable.ic_menu_close_clear_cancel);
+
+        removeAccount.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS
+                | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+        syncNow.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+        syncCancel.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+    }
+
+    @Override
+    public void onPrepareOptionsMenu(Menu menu) {
+        super.onPrepareOptionsMenu(menu);
+        boolean syncActive = ContentResolver.getCurrentSync() != null;
+        menu.findItem(MENU_SYNC_NOW_ID).setVisible(!syncActive);
+        menu.findItem(MENU_SYNC_CANCEL_ID).setVisible(syncActive);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case MENU_SYNC_NOW_ID:
+                startSyncForEnabledProviders();
+                return true;
+            case MENU_SYNC_CANCEL_ID:
+                cancelSyncForEnabledProviders();
+                return true;
+            case MENU_REMOVE_ACCOUNT_ID:
+                showDialog(REALLY_REMOVE_DIALOG);
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    public boolean onPreferenceTreeClick(PreferenceScreen preferences, Preference preference) {
+        if (preference instanceof SyncStateCheckBoxPreference) {
+            SyncStateCheckBoxPreference syncPref = (SyncStateCheckBoxPreference) preference;
+            String authority = syncPref.getAuthority();
+            Account account = syncPref.getAccount();
+            boolean syncAutomatically = ContentResolver.getSyncAutomatically(account, authority);
+            if (syncPref.isOneTimeSyncMode()) {
+                requestOrCancelSync(account, authority, true);
+            } else {
+                boolean syncOn = syncPref.isChecked();
+                boolean oldSyncState = syncAutomatically;
+                if (syncOn != oldSyncState) {
+                    // if we're enabling sync, this will request a sync as well
+                    ContentResolver.setSyncAutomatically(account, authority, syncOn);
+                    // if the master sync switch is off, the request above will
+                    // get dropped.  when the user clicks on this toggle,
+                    // we want to force the sync, however.
+                    if (!ContentResolver.getMasterSyncAutomatically() || !syncOn) {
+                        requestOrCancelSync(account, authority, syncOn);
+                    }
+                }
+            }
+            return true;
+        } else {
+            return super.onPreferenceTreeClick(preferences, preference);
+        }
+    }
+
+    private void startSyncForEnabledProviders() {
+        requestOrCancelSyncForEnabledProviders(true /* start them */);
+        getActivity().invalidateOptionsMenu();
+    }
+
+    private void cancelSyncForEnabledProviders() {
+        requestOrCancelSyncForEnabledProviders(false /* cancel them */);
+        getActivity().invalidateOptionsMenu();
+    }
+
+    private void requestOrCancelSyncForEnabledProviders(boolean startSync) {
+        // sync everything that the user has enabled
+        int count = getPreferenceScreen().getPreferenceCount();
+        for (int i = 0; i < count; i++) {
+            Preference pref = getPreferenceScreen().getPreference(i);
+            if (! (pref instanceof SyncStateCheckBoxPreference)) {
+                continue;
+            }
+            SyncStateCheckBoxPreference syncPref = (SyncStateCheckBoxPreference) pref;
+            if (!syncPref.isChecked()) {
+                continue;
+            }
+            requestOrCancelSync(syncPref.getAccount(), syncPref.getAuthority(), startSync);
+        }
+        // plus whatever the system needs to sync, e.g., invisible sync adapters
+        if (mAccount != null) {
+            for (String authority : mInvisibleAdapters) {
+                requestOrCancelSync(mAccount, authority, startSync);
+            }
+        }
+    }
+
+    private void requestOrCancelSync(Account account, String authority, boolean flag) {
+        if (flag) {
+            Bundle extras = new Bundle();
+            extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+            ContentResolver.requestSync(account, authority, extras);
+        } else {
+            ContentResolver.cancelSync(account, authority);
+        }
+    }
+
+    private boolean isSyncing(List<SyncInfo> currentSyncs, Account account, String authority) {
+        for (SyncInfo syncInfo : currentSyncs) {
+            if (syncInfo.account.equals(account) && syncInfo.authority.equals(authority)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    protected void onSyncStateUpdated() {
+        // iterate over all the preferences, setting the state properly for each
+        Date date = new Date();
+        List<SyncInfo> currentSyncs = ContentResolver.getCurrentSyncs();
+        boolean syncIsFailing = false;
+
+        // Refresh the sync status checkboxes - some syncs may have become active.
+        updateAccountCheckboxes(mAccounts);
+
+        for (int i = 0, count = getPreferenceScreen().getPreferenceCount(); i < count; i++) {
+            Preference pref = getPreferenceScreen().getPreference(i);
+            if (! (pref instanceof SyncStateCheckBoxPreference)) {
+                continue;
+            }
+            SyncStateCheckBoxPreference syncPref = (SyncStateCheckBoxPreference) pref;
+
+            String authority = syncPref.getAuthority();
+            Account account = syncPref.getAccount();
+
+            SyncStatusInfo status = ContentResolver.getSyncStatus(account, authority);
+            boolean syncEnabled = ContentResolver.getSyncAutomatically(account, authority);
+            boolean authorityIsPending = status == null ? false : status.pending;
+            boolean initialSync = status == null ? false : status.initialize;
+
+            boolean activelySyncing = isSyncing(currentSyncs, account, authority);
+            boolean lastSyncFailed = status != null
+                    && status.lastFailureTime != 0
+                    && status.getLastFailureMesgAsInt(0)
+                       != ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS;
+            if (!syncEnabled) lastSyncFailed = false;
+            if (lastSyncFailed && !activelySyncing && !authorityIsPending) {
+                syncIsFailing = true;
+            }
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.d(TAG, "Update sync status: " + account + " " + authority +
+                        " active = " + activelySyncing + " pend =" +  authorityIsPending);
+            }
+
+            final long successEndTime = (status == null) ? 0 : status.lastSuccessTime;
+            if (successEndTime != 0) {
+                date.setTime(successEndTime);
+                final String timeString = mDateFormat.format(date) + " "
+                        + mTimeFormat.format(date);
+                syncPref.setSummary(timeString);
+            } else {
+                syncPref.setSummary("");
+            }
+            int syncState = ContentResolver.getIsSyncable(account, authority);
+
+            syncPref.setActive(activelySyncing && (syncState >= 0) &&
+                    !initialSync);
+            syncPref.setPending(authorityIsPending && (syncState >= 0) &&
+                    !initialSync);
+
+            syncPref.setFailed(lastSyncFailed);
+            ConnectivityManager connManager =
+                (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+            final boolean masterSyncAutomatically = ContentResolver.getMasterSyncAutomatically();
+            final boolean backgroundDataEnabled = connManager.getBackgroundDataSetting();
+            final boolean oneTimeSyncMode = !masterSyncAutomatically || !backgroundDataEnabled;
+            syncPref.setOneTimeSyncMode(oneTimeSyncMode);
+            syncPref.setChecked(oneTimeSyncMode || syncEnabled);
+        }
+        mErrorInfoView.setVisibility(syncIsFailing ? View.VISIBLE : View.GONE);
+        getActivity().invalidateOptionsMenu();
+    }
+
+    @Override
+    public void onAccountsUpdated(Account[] accounts) {
+        super.onAccountsUpdated(accounts);
+        mAccounts = accounts;
+        updateAccountCheckboxes(accounts);
+        onSyncStateUpdated();
+    }
+
+    private void updateAccountCheckboxes(Account[] accounts) {
+        mInvisibleAdapters.clear();
+
+        SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypes();
+        HashMap<String, ArrayList<String>> accountTypeToAuthorities =
+            Maps.newHashMap();
+        for (int i = 0, n = syncAdapters.length; i < n; i++) {
+            final SyncAdapterType sa = syncAdapters[i];
+            if (sa.isUserVisible()) {
+                ArrayList<String> authorities = accountTypeToAuthorities.get(sa.accountType);
+                if (authorities == null) {
+                    authorities = new ArrayList<String>();
+                    accountTypeToAuthorities.put(sa.accountType, authorities);
+                }
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.d(TAG, "onAccountUpdated: added authority " + sa.authority
+                            + " to accountType " + sa.accountType);
+                }
+                authorities.add(sa.authority);
+            } else {
+                // keep track of invisible sync adapters, so sync now forces
+                // them to sync as well.
+                mInvisibleAdapters.add(sa.authority);
+            }
+        }
+
+        for (int i = 0, n = mCheckBoxes.size(); i < n; i++) {
+            getPreferenceScreen().removePreference(mCheckBoxes.get(i));
+        }
+        mCheckBoxes.clear();
+
+        for (int i = 0, n = accounts.length; i < n; i++) {
+            final Account account = accounts[i];
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.d(TAG, "looking for sync adapters that match account " + account);
+            }
+            final ArrayList<String> authorities = accountTypeToAuthorities.get(account.type);
+            if (authorities != null && (mAccount == null || mAccount.equals(account))) {
+                for (int j = 0, m = authorities.size(); j < m; j++) {
+                    final String authority = authorities.get(j);
+                    // We could check services here....
+                    int syncState = ContentResolver.getIsSyncable(account, authority);
+                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.d(TAG, "  found authority " + authority + " " + syncState);
+                    }
+                    if (syncState > 0) {
+                        addSyncStateCheckBox(account, authority);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Updates the titlebar with an icon for the provider type.
+     */
+    @Override
+    protected void onAuthDescriptionsUpdated() {
+        super.onAuthDescriptionsUpdated();
+        getPreferenceScreen().removeAll();
+        mProviderIcon.setImageDrawable(getDrawableForType(mAccount.type));
+        mProviderId.setText(getLabelForType(mAccount.type));
+        PreferenceScreen prefs = addPreferencesForType(mAccount.type);
+        if (prefs != null) {
+            updatePreferenceIntents(prefs);
+        }
+        addPreferencesFromResource(R.xml.account_sync_settings);
+    }
+
+    private void updatePreferenceIntents(PreferenceScreen prefs) {
+        for (int i = 0; i < prefs.getPreferenceCount(); i++) {
+            Intent intent = prefs.getPreference(i).getIntent();
+            if (intent != null) {
+                intent.putExtra(ACCOUNT_KEY, mAccount);
+                // This is somewhat of a hack. Since the preference screen we're accessing comes
+                // from another package, we need to modify the intent to launch it with
+                // FLAG_ACTIVITY_NEW_TASK.
+                // TODO: Do something smarter if we ever have PreferenceScreens of our own.
+                intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
+            }
+        }
+    }
+}
diff --git a/src/com/android/settings/accounts/AccountSyncSettingsInAddAccount.java b/src/com/android/settings/accounts/AccountSyncSettingsInAddAccount.java
new file mode 100644
index 0000000..8fa576a
--- /dev/null
+++ b/src/com/android/settings/accounts/AccountSyncSettingsInAddAccount.java
@@ -0,0 +1,42 @@
+
+package com.android.settings.accounts;
+
+import com.android.settings.R;
+
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+/**
+ * This is AccountSyncSettings with 'remove account' button always gone and 
+ * a wizard-like button bar to complete the activity.
+ */
+public class AccountSyncSettingsInAddAccount extends AccountSyncSettings 
+        implements OnClickListener {
+    private View mFinishArea;
+    private View mFinishButton;
+
+    @Override
+    protected void initializeUi(final View rootView) {
+        super.initializeUi(rootView);
+
+        mFinishArea = (View) rootView.findViewById(R.id.finish_button_area);
+        mFinishArea.setVisibility(View.VISIBLE);
+        mFinishButton = (View) rootView.findViewById(R.id.finish_button);
+        mFinishButton.setOnClickListener(this);
+    }
+
+    @Override
+    public void onPrepareOptionsMenu(Menu menu) {
+        super.onPrepareOptionsMenu(menu);
+        // Remove the "remove account" menu item
+        menu.findItem(MENU_REMOVE_ACCOUNT_ID).setVisible(false);
+    }
+
+    public void onClick(View v) {
+        finish();
+    }
+}
diff --git a/src/com/android/settings/accounts/AddAccountSettings.java b/src/com/android/settings/accounts/AddAccountSettings.java
new file mode 100644
index 0000000..4c5c0b8
--- /dev/null
+++ b/src/com/android/settings/accounts/AddAccountSettings.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.accounts;
+
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Entry point Actiivty for account setup. Works as follows
+ *
+ * 1) When the other Activities launch this Activity, it launches {@link ChooseAccountActivity}
+ *    without showing anything.
+ * 2) After receiving an account type from ChooseAccountActivity, this Activity launches the
+ *    account setup specified by AccountManager.
+ * 3) After the account setup, this Activity finishes without showing anything.
+ *
+ * Note:
+ * Previously this Activity did what {@link ChooseAccountActivity} does right now, but we
+ * currently delegate the work to the other Activity. When we let this Activity do that work, users
+ * would see the list of account types when leaving this Activity, since the UI is already ready
+ * when returning from each account setup, which doesn't look good.
+ */
+public class AddAccountSettings extends Activity {
+    private static final String TAG = "AccountSettings";
+
+    /* package */ static final String EXTRA_SELECTED_ACCOUNT = "selected_account";
+
+    private static final int CHOOSE_ACCOUNT_REQUEST = 1;
+
+    private AccountManagerCallback<Bundle> mCallback = new AccountManagerCallback<Bundle>() {
+        public void run(AccountManagerFuture<Bundle> future) {
+            try {
+                Bundle bundle = future.getResult();
+                bundle.keySet();
+                setResult(RESULT_OK);
+                if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "account added: " + bundle);
+            } catch (OperationCanceledException e) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "addAccount was canceled");
+            } catch (IOException e) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "addAccount failed: " + e);
+            } catch (AuthenticatorException e) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "addAccount failed: " + e);
+            } finally {
+                finish();
+            }
+        }
+    };
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final String[] authorities =
+                getIntent().getStringArrayExtra(AccountPreferenceBase.AUTHORITIES_FILTER_KEY);
+        final String[] accountTypes =
+                getIntent().getStringArrayExtra(AccountPreferenceBase.ACCOUNT_TYPES_FILTER_KEY);
+        final Intent intent = new Intent(this, ChooseAccountActivity.class);
+        if (authorities != null) {
+            intent.putExtra(AccountPreferenceBase.AUTHORITIES_FILTER_KEY, authorities);
+        }
+        if (accountTypes != null) {
+            intent.putExtra(AccountPreferenceBase.ACCOUNT_TYPES_FILTER_KEY, accountTypes);
+        }
+        startActivityForResult(intent, CHOOSE_ACCOUNT_REQUEST);
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        switch (requestCode) {
+        case CHOOSE_ACCOUNT_REQUEST:
+            if (resultCode == RESULT_CANCELED) {
+                setResult(resultCode);
+                finish();
+                return;
+            }
+            // Go to account setup screen. finish() is called inside mCallback.
+            addAccount(data.getStringExtra(EXTRA_SELECTED_ACCOUNT));
+            break;
+        }
+    }
+
+    private void addAccount(String accountType) {
+        AccountManager.get(this).addAccount(
+                accountType,
+                null, /* authTokenType */
+                null, /* requiredFeatures */
+                null, /* addAccountOptions */
+                this,
+                mCallback,
+                null /* handler */);
+    }
+}
diff --git a/src/com/android/settings/accounts/ChooseAccountActivity.java b/src/com/android/settings/accounts/ChooseAccountActivity.java
new file mode 100644
index 0000000..9d4965f
--- /dev/null
+++ b/src/com/android/settings/accounts/ChooseAccountActivity.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.accounts;
+
+import com.android.settings.R;
+import com.google.android.collect.Maps;
+
+import android.accounts.AccountManager;
+import android.accounts.AuthenticatorDescription;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SyncAdapterType;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceGroup;
+import android.preference.PreferenceScreen;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+/**
+ * Activity asking a user to select an account to be set up.
+ */
+public class ChooseAccountActivity extends PreferenceActivity {
+
+    private static final String TAG = "ChooseAccountActivity";
+    private String[] mAuthorities;
+    private PreferenceGroup mAddAccountGroup;
+    private final ArrayList<ProviderEntry> mProviderList = new ArrayList<ProviderEntry>();
+    public HashSet<String> mAccountTypesFilter;
+    private AuthenticatorDescription[] mAuthDescs;
+    private HashMap<String, ArrayList<String>> mAccountTypeToAuthorities = null;
+    private Map<String, AuthenticatorDescription> mTypeToAuthDescription
+            = new HashMap<String, AuthenticatorDescription>();
+    
+    private static class ProviderEntry {
+        private final CharSequence name;
+        private final String type;
+        ProviderEntry(CharSequence providerName, String accountType) {
+            name = providerName;
+            type = accountType;
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        setContentView(R.layout.add_account_screen);
+        addPreferencesFromResource(R.xml.add_account_settings);
+        mAuthorities = getIntent().getStringArrayExtra(
+                AccountPreferenceBase.AUTHORITIES_FILTER_KEY);
+        String[] accountTypesFilter = getIntent().getStringArrayExtra(
+                AccountPreferenceBase.ACCOUNT_TYPES_FILTER_KEY);
+        if (accountTypesFilter != null) {
+            mAccountTypesFilter = new HashSet<String>();
+            for (String accountType : accountTypesFilter) {
+                mAccountTypesFilter.add(accountType);
+            }
+        }
+        mAddAccountGroup = getPreferenceScreen();
+        updateAuthDescriptions();
+    }
+
+    /**
+     * Updates provider icons. Subclasses should call this in onCreate()
+     * and update any UI that depends on AuthenticatorDescriptions in onAuthDescriptionsUpdated().
+     */
+    private void updateAuthDescriptions() {
+        mAuthDescs = AccountManager.get(this).getAuthenticatorTypes();
+        for (int i = 0; i < mAuthDescs.length; i++) {
+            mTypeToAuthDescription.put(mAuthDescs[i].type, mAuthDescs[i]);
+        }
+        onAuthDescriptionsUpdated();
+    }
+
+    private void onAuthDescriptionsUpdated() {
+        // Create list of providers to show on preference screen
+        for (int i = 0; i < mAuthDescs.length; i++) {
+            String accountType = mAuthDescs[i].type;
+            CharSequence providerName = getLabelForType(accountType);
+
+            // Skip preferences for authorities not specified. If no authorities specified,
+            // then include them all.
+            ArrayList<String> accountAuths = getAuthoritiesForAccountType(accountType);
+            boolean addAccountPref = true;
+            if (mAuthorities != null && mAuthorities.length > 0 && accountAuths != null) {
+                addAccountPref = false;
+                for (int k = 0; k < mAuthorities.length; k++) {
+                    if (accountAuths.contains(mAuthorities[k])) {
+                        addAccountPref = true;
+                        break;
+                    }
+                }
+            }
+            if (addAccountPref && mAccountTypesFilter != null
+                    && !mAccountTypesFilter.contains(accountType)) {
+                addAccountPref = false;
+            }
+            if (addAccountPref) {
+                mProviderList.add(new ProviderEntry(providerName, accountType));
+            } else {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "Skipped pref " + providerName + ": has no authority we need");
+                }
+            }
+        }
+
+        if (mProviderList.size() == 1) {
+            // If there's only one provider that matches, just run it.
+            finishWithAccountType(mProviderList.get(0).type);
+        } else if (mProviderList.size() > 0) {
+            mAddAccountGroup.removeAll();
+            for (ProviderEntry pref : mProviderList) {
+                Drawable drawable = getDrawableForType(pref.type);
+                ProviderPreference p =
+                        new ProviderPreference(this, pref.type, drawable, pref.name);
+                mAddAccountGroup.addPreference(p);
+            }
+        } else {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                final StringBuilder auths = new StringBuilder();
+                for (String a : mAuthorities) {
+                    auths.append(a);
+                    auths.append(' ');
+                }
+                Log.v(TAG, "No providers found for authorities: " + auths);
+            }
+            setResult(RESULT_CANCELED);
+            finish();
+        }
+    }
+
+    public ArrayList<String> getAuthoritiesForAccountType(String type) {
+        if (mAccountTypeToAuthorities == null) {
+            mAccountTypeToAuthorities = Maps.newHashMap();
+            SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypes();
+            for (int i = 0, n = syncAdapters.length; i < n; i++) {
+                final SyncAdapterType sa = syncAdapters[i];
+                ArrayList<String> authorities = mAccountTypeToAuthorities.get(sa.accountType);
+                if (authorities == null) {
+                    authorities = new ArrayList<String>();
+                    mAccountTypeToAuthorities.put(sa.accountType, authorities);
+                }
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.d(TAG, "added authority " + sa.authority + " to accountType "
+                            + sa.accountType);
+                }
+                authorities.add(sa.authority);
+            }
+        }
+        return mAccountTypeToAuthorities.get(type);
+    }
+
+    /**
+     * Gets an icon associated with a particular account type. If none found, return null.
+     * @param accountType the type of account
+     * @return a drawable for the icon or null if one cannot be found.
+     */
+    protected Drawable getDrawableForType(final String accountType) {
+        Drawable icon = null;
+        if (mTypeToAuthDescription.containsKey(accountType)) {
+            try {
+                AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType);
+                Context authContext = createPackageContext(desc.packageName, 0);
+                icon = authContext.getResources().getDrawable(desc.iconId);
+            } catch (PackageManager.NameNotFoundException e) {
+                // TODO: place holder icon for missing account icons?
+                Log.w(TAG, "No icon for account type " + accountType);
+            }
+        }
+        return icon;
+    }
+
+    /**
+     * Gets the label associated with a particular account type. If none found, return null.
+     * @param accountType the type of account
+     * @return a CharSequence for the label or null if one cannot be found.
+     */
+    protected CharSequence getLabelForType(final String accountType) {
+        CharSequence label = null;
+        if (mTypeToAuthDescription.containsKey(accountType)) {
+            try {
+                AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType);
+                Context authContext = createPackageContext(desc.packageName, 0);
+                label = authContext.getResources().getText(desc.labelId);
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.w(TAG, "No label for account type " + ", type " + accountType);
+            }
+        }
+        return label;
+    }
+
+    @Override
+    public boolean onPreferenceTreeClick(PreferenceScreen preferences, Preference preference) {
+        if (preference instanceof ProviderPreference) {
+            ProviderPreference pref = (ProviderPreference) preference;
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "Attempting to add account of type " + pref.getAccountType());
+            }
+            finishWithAccountType(pref.getAccountType());
+        }
+        return true;
+    }
+
+    private void finishWithAccountType(String accountType) {
+        Intent intent = new Intent();
+        intent.putExtra(AddAccountSettings.EXTRA_SELECTED_ACCOUNT, accountType);
+        setResult(RESULT_OK, intent);
+        finish();
+    }
+}
diff --git a/src/com/android/settings/ManageAccountsSettings.java b/src/com/android/settings/accounts/ManageAccountsSettings.java
similarity index 80%
rename from src/com/android/settings/ManageAccountsSettings.java
rename to src/com/android/settings/accounts/ManageAccountsSettings.java
index 93053f1..8f61516 100644
--- a/src/com/android/settings/ManageAccountsSettings.java
+++ b/src/com/android/settings/accounts/ManageAccountsSettings.java
@@ -14,9 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.settings;
+package com.android.settings.accounts;
 
-import com.android.settings.SettingsPreferenceFragment.SettingsDialogFragment;
+import com.android.settings.AccountPreference;
+import com.android.settings.DialogCreatable;
+import com.android.settings.R;
+import com.android.settings.vpn.VpnTypeSelection;
 import com.google.android.collect.Maps;
 
 import android.accounts.Account;
@@ -39,8 +42,8 @@
 import android.os.Bundle;
 import android.preference.CheckBoxPreference;
 import android.preference.Preference;
+import android.preference.PreferenceActivity;
 import android.preference.PreferenceCategory;
-import android.preference.PreferenceFragment;
 import android.preference.PreferenceScreen;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -57,9 +60,9 @@
 import java.util.HashSet;
 import java.util.Map;
 
-public class ManageAccountsSettings extends PreferenceFragment
- implements OnAccountsUpdateListener,
-        DialogCreatable {
+public class ManageAccountsSettings extends AccountPreferenceBase
+        implements OnAccountsUpdateListener, DialogCreatable {
+
     private static final String TAG = ManageAccountsSettings.class.getSimpleName();
 
     private static final String AUTHORITIES_FILTER_KEY = "authorities";
@@ -72,6 +75,8 @@
 
     private static final int MENU_ADD_ACCOUNT = Menu.FIRST;
 
+    private static final int REQUEST_SHOW_SYNC_SETTINGS = 1;
+
     private CheckBoxPreference mBackgroundDataCheckBox;
     private PreferenceCategory mManageAccountsCategory;
     private String[] mAuthorities;
@@ -90,6 +95,8 @@
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
+        addPreferencesFromResource(R.xml.manage_accounts_settings);
+        AccountManager.get(getActivity()).addOnAccountsUpdatedListener(this, null, true);
         setHasOptionsMenu(true);
     }
 
@@ -101,17 +108,10 @@
     }
 
     @Override
-    public void onResume() {
-        super.onResume();
-        onSyncStateUpdated();
-    }
-
-    @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
 
         final Activity activity = getActivity();
-        addPreferencesFromResource(R.xml.manage_accounts_settings);
         final View view = getView();
 
         mErrorInfoView = (TextView)view.findViewById(R.id.sync_settings_error_info);
@@ -126,8 +126,7 @@
         mManageAccountsCategory = (PreferenceCategory)findPreference(MANAGE_ACCOUNTS_CATEGORY_KEY);
         mAuthorities = activity.getIntent().getStringArrayExtra(AUTHORITIES_FILTER_KEY);
 
-        AccountManager.get(activity).addOnAccountsUpdatedListener(this, null, true);
-        updateAuthDescriptions(activity);
+        updateAuthDescriptions();
     }
 
     @Override
@@ -156,12 +155,23 @@
         } else if (preference == mAutoSyncCheckbox) {
             ContentResolver.setMasterSyncAutomatically(mAutoSyncCheckbox.isChecked());
             onSyncStateUpdated();
+        } else if (preference instanceof AccountPreference) {
+            startAccountSettings((AccountPreference) preference);
         } else {
             return false;
         }
         return true;
     }
 
+    private void startAccountSettings(AccountPreference acctPref) {
+        Bundle args = new Bundle();
+        args.putParcelable(AccountSyncSettings.ACCOUNT_KEY, acctPref.getAccount());
+        ((PreferenceActivity) getActivity()).startPreferencePanel(
+                AccountSyncSettings.class.getCanonicalName(), args,
+                R.string.account_sync_settings_title, acctPref.getAccount().name,
+                this, REQUEST_SHOW_SYNC_SETTINGS);
+    }
+
     @Override
     public Dialog onCreateDialog(int id) {
         switch (id) {
@@ -187,7 +197,7 @@
         return null;
     }
 
-    void showDialog(int dialogId) {
+    public void showDialog(int dialogId) {
         if (mDialogFragment != null) {
             Log.e(TAG, "Old dialog fragment not null!");
         }
@@ -216,7 +226,7 @@
         connManager.setBackgroundDataSetting(enabled);
     }
 
-    private void onSyncStateUpdated() {
+    protected void onSyncStateUpdated() {
         // Set background connection state
         final ConnectivityManager connManager = (ConnectivityManager)
                 getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -320,7 +330,7 @@
         onSyncStateUpdated();
     }
 
-    private void onAuthDescriptionsUpdated() {
+    protected void onAuthDescriptionsUpdated() {
         // Update account icons for all account preference items
         for (int i = 0; i < mManageAccountsCategory.getPreferenceCount(); i++) {
             AccountPreference pref = (AccountPreference) mManageAccountsCategory.getPreference(i);
@@ -334,64 +344,4 @@
         intent.putExtra(AUTHORITIES_FILTER_KEY, mAuthorities);
         startActivity(intent);
     }
-
-    /* The logic below is copied from AcountPrefernceBase */
-
-    private Drawable getDrawableForType(final String accountType) {
-        Drawable icon = null;
-        if (mTypeToAuthDescription.containsKey(accountType)) {
-            try {
-                AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType);
-                Context authContext = getActivity().createPackageContext(desc.packageName, 0);
-                icon = authContext.getResources().getDrawable(desc.iconId);
-            } catch (PackageManager.NameNotFoundException e) {
-                // TODO: place holder icon for missing account icons?
-                Log.w(TAG, "No icon for account type " + accountType);
-            }
-        }
-        return icon;
-    }
-
-    private CharSequence getLabelForType(final String accountType) {
-        CharSequence label = null;
-        if (mTypeToAuthDescription.containsKey(accountType)) {
-             try {
-                 AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType);
-                 Context authContext = getActivity().createPackageContext(desc.packageName, 0);
-                 label = authContext.getResources().getText(desc.labelId);
-             } catch (PackageManager.NameNotFoundException e) {
-                 Log.w(TAG, "No label for account type " + ", type " + accountType);
-             }
-        }
-        return label;
-    }
-
-    private ArrayList<String> getAuthoritiesForAccountType(String type) {
-        if (mAccountTypeToAuthorities == null) {
-            mAccountTypeToAuthorities = Maps.newHashMap();
-            SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypes();
-            for (int i = 0, n = syncAdapters.length; i < n; i++) {
-                final SyncAdapterType sa = syncAdapters[i];
-                ArrayList<String> authorities = mAccountTypeToAuthorities.get(sa.accountType);
-                if (authorities == null) {
-                    authorities = new ArrayList<String>();
-                    mAccountTypeToAuthorities.put(sa.accountType, authorities);
-                }
-                if (LDEBUG) {
-                    Log.d(TAG, "added authority " + sa.authority + " to accountType "
-                            + sa.accountType);
-                }
-                authorities.add(sa.authority);
-            }
-        }
-        return mAccountTypeToAuthorities.get(type);
-    }
-
-    private void updateAuthDescriptions(Context context) {
-        mAuthDescs = AccountManager.get(context).getAuthenticatorTypes();
-        for (int i = 0; i < mAuthDescs.length; i++) {
-            mTypeToAuthDescription.put(mAuthDescs[i].type, mAuthDescs[i]);
-        }
-        onAuthDescriptionsUpdated();
-    }
 }
diff --git a/src/com/android/settings/accounts/ProviderPreference.java b/src/com/android/settings/accounts/ProviderPreference.java
new file mode 100644
index 0000000..3996523
--- /dev/null
+++ b/src/com/android/settings/accounts/ProviderPreference.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.accounts;
+
+import com.android.settings.R;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.preference.Preference;
+import android.view.View;
+import android.widget.ImageView;
+
+/**
+ * ProviderPreference is used to display an image to the left of a provider name.
+ * The preference ultimately calls AccountManager.addAccount() for the account type.
+ */
+public class ProviderPreference extends Preference {
+    private String mAccountType;
+
+    public ProviderPreference(
+            Context context, String accountType, Drawable icon, CharSequence providerName) {
+        super(context);
+        mAccountType = accountType;
+        setIcon(icon);
+        setPersistent(false);
+        setTitle(providerName);
+    }
+
+    public String getAccountType() {
+        return mAccountType;
+    }
+}
diff --git a/src/com/android/settings/accounts/SyncActivityTooManyDeletes.java b/src/com/android/settings/accounts/SyncActivityTooManyDeletes.java
new file mode 100644
index 0000000..b31561c
--- /dev/null
+++ b/src/com/android/settings/accounts/SyncActivityTooManyDeletes.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.accounts;
+
+import com.android.settings.R;
+
+import android.accounts.Account;
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * Presents multiple options for handling the case where a sync was aborted because there
+ * were too many pending deletes. One option is to force the delete, another is to rollback
+ * the deletes, the third is to do nothing.
+ */
+public class SyncActivityTooManyDeletes extends Activity
+        implements AdapterView.OnItemClickListener {
+
+    private long mNumDeletes;
+    private Account mAccount;
+    private String mAuthority;
+    private String mProvider;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Bundle extras = getIntent().getExtras();
+        if (extras == null) {
+            finish();
+            return;
+        }
+
+        mNumDeletes = extras.getLong("numDeletes");
+        mAccount = (Account) extras.getParcelable("account");
+        mAuthority = extras.getString("authority");
+        mProvider = extras.getString("provider");
+
+        // the order of these must match up with the constants for position used in onItemClick
+        CharSequence[] options = new CharSequence[]{
+                getResources().getText(R.string.sync_really_delete),
+                getResources().getText(R.string.sync_undo_deletes),
+                getResources().getText(R.string.sync_do_nothing)
+        };
+
+        ListAdapter adapter = new ArrayAdapter<CharSequence>(this,
+                android.R.layout.simple_list_item_1,
+                android.R.id.text1,
+                options);
+
+        ListView listView = new ListView(this);
+        listView.setAdapter(adapter);
+        listView.setItemsCanFocus(true);
+        listView.setOnItemClickListener(this);
+
+        TextView textView = new TextView(this);
+        CharSequence tooManyDeletesDescFormat =
+                getResources().getText(R.string.sync_too_many_deletes_desc);
+        textView.setText(String.format(tooManyDeletesDescFormat.toString(),
+                mNumDeletes, mProvider, mAccount.name));
+
+        final LinearLayout ll = new LinearLayout(this);
+        ll.setOrientation(LinearLayout.VERTICAL);
+        final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0);
+        ll.addView(textView, lp);
+        ll.addView(listView, lp);
+
+        // TODO: consider displaying the icon of the account type
+//        AuthenticatorDescription[] descs = AccountManager.get(this).getAuthenticatorTypes();
+//        for (AuthenticatorDescription desc : descs) {
+//            if (desc.type.equals(mAccount.type)) {
+//                try {
+//                    final Context authContext = createPackageContext(desc.packageName, 0);
+//                    ImageView imageView = new ImageView(this);
+//                    imageView.setImageDrawable(authContext.getResources().getDrawable(desc.iconId));
+//                    ll.addView(imageView, lp);
+//                } catch (PackageManager.NameNotFoundException e) {
+//                }
+//                break;
+//            }
+//        }
+
+        setContentView(ll);
+    }
+
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        // the constants for position correspond to the items options array in onCreate()
+        if (position == 0) startSyncReallyDelete();
+        else if (position == 1) startSyncUndoDeletes();
+        finish();
+    }
+
+    private void startSyncReallyDelete() {
+        Bundle extras = new Bundle();
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS, true);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
+        ContentResolver.requestSync(mAccount, mAuthority, extras);
+    }
+
+    private void startSyncUndoDeletes() {
+        Bundle extras = new Bundle();
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS, true);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
+        ContentResolver.requestSync(mAccount, mAuthority, extras);
+    }
+}
diff --git a/src/com/android/settings/accounts/SyncStateCheckBoxPreference.java b/src/com/android/settings/accounts/SyncStateCheckBoxPreference.java
new file mode 100644
index 0000000..b200eb6
--- /dev/null
+++ b/src/com/android/settings/accounts/SyncStateCheckBoxPreference.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.accounts;
+
+import com.android.settings.R;
+
+import android.content.Context;
+import android.graphics.drawable.AnimationDrawable;
+import android.preference.CheckBoxPreference;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.accounts.Account;
+
+public class SyncStateCheckBoxPreference extends CheckBoxPreference {
+
+    private boolean mIsActive = false;
+    private boolean mIsPending = false;
+    private boolean mFailed = false;
+    private Account mAccount;
+    private String mAuthority;
+
+    /**
+     * A mode for this preference where clicking does a one-time sync instead of
+     * toggling whether the provider will do autosync.
+     */
+    private boolean mOneTimeSyncMode = false;
+    
+    public SyncStateCheckBoxPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setWidgetLayoutResource(R.layout.preference_widget_sync_toggle);
+        mAccount = null;
+        mAuthority = null;
+    }
+
+    public SyncStateCheckBoxPreference(Context context, Account account, String authority) {
+        super(context, null);
+        mAccount = account;
+        mAuthority = authority;
+        setWidgetLayoutResource(R.layout.preference_widget_sync_toggle);
+    }
+
+    @Override
+    public void onBindView(View view) {
+        super.onBindView(view);
+        ImageView syncActiveView = (ImageView) view.findViewById(R.id.sync_active);
+        View syncPendingView = view.findViewById(R.id.sync_pending);
+        View syncFailedView = view.findViewById(R.id.sync_failed);
+
+        syncActiveView.setVisibility(mIsActive ? View.VISIBLE : View.GONE);
+        final AnimationDrawable anim = (AnimationDrawable) syncActiveView.getDrawable();
+        boolean showError;
+        boolean showPending;
+        if (mIsActive) {
+            syncActiveView.post(new Runnable() {
+                public void run() {
+                    anim.start();
+                }
+            });
+            showPending = false;
+            showError = false;
+        } else {
+            anim.stop();
+            if (mIsPending) {
+                showPending = true;
+                showError = false;
+            } else {
+                showPending = false;
+                showError = mFailed;
+            }
+        }
+
+        syncFailedView.setVisibility(showError ? View.VISIBLE : View.GONE);
+        syncPendingView.setVisibility((showPending && !mIsActive) ? View.VISIBLE : View.GONE);
+        
+        View checkBox = view.findViewById(android.R.id.checkbox);
+        if (mOneTimeSyncMode) {
+            checkBox.setVisibility(View.GONE);
+            
+            /*
+             * Override the summary. Fill in the %1$s with the existing summary
+             * (what ends up happening is the old summary is shown on the next
+             * line).
+             */
+            TextView summary = (TextView) view.findViewById(android.R.id.summary);
+            summary.setText(getContext().getString(R.string.sync_one_time_sync, getSummary()));
+        } else {
+            checkBox.setVisibility(View.VISIBLE);
+        }
+    }
+
+    /**
+     * Set whether the sync is active.
+     * @param isActive whether or not the sync is active
+     */
+    public void setActive(boolean isActive) {
+        mIsActive = isActive;
+        notifyChanged();
+    }
+
+    /**
+     * Set whether a sync is pending.
+     * @param isPending whether or not the sync is pending
+     */
+    public void setPending(boolean isPending) {
+        mIsPending = isPending;
+        notifyChanged();
+    }
+
+    /**
+     * Set whether the corresponding sync failed.
+     * @param failed whether or not the sync failed
+     */
+    public void setFailed(boolean failed) {
+        mFailed = failed;
+        notifyChanged();
+    }
+
+    /**
+     * Sets whether the preference is in one-time sync mode.
+     */
+    public void setOneTimeSyncMode(boolean oneTimeSyncMode) {
+        mOneTimeSyncMode = oneTimeSyncMode;
+        notifyChanged();
+    }
+    
+    /**
+     * Gets whether the preference is in one-time sync mode.
+     */
+    public boolean isOneTimeSyncMode() {
+        return mOneTimeSyncMode;
+    }
+
+    @Override
+    protected void onClick() {
+        // When we're in one-time sync mode, we don't want a click to change the
+        // checkbox state
+        if (!mOneTimeSyncMode) {
+            super.onClick();
+        }            
+    }
+
+    public Account getAccount() {
+        return mAccount;
+    }
+
+    public String getAuthority() {
+        return mAuthority;
+    }
+}