summaryrefslogtreecommitdiffstats
path: root/src/com
diff options
context:
space:
mode:
authorAmith Yamasani <yamasani@google.com>2010-12-01 09:04:36 -0800
committerAmith Yamasani <yamasani@google.com>2010-12-01 15:14:09 -0800
commit43c697854c7e373fbc1dae8b7a5259a32de346b4 (patch)
tree5d75968ceed0c4454af580f56311bd0ccf52ceac /src/com
parentf3c32f49cdafef93ef783947d5ed4aaa287cba61 (diff)
downloadpackages_apps_Settings-43c697854c7e373fbc1dae8b7a5259a32de346b4.tar.gz
packages_apps_Settings-43c697854c7e373fbc1dae8b7a5259a32de346b4.tar.bz2
packages_apps_Settings-43c697854c7e373fbc1dae8b7a5259a32de346b4.zip
Move Account & sync settings into Settings app.
Fragmentized some of the activities and moved buttons into the menu area. Bug: 3148844
Diffstat (limited to 'src/com')
-rw-r--r--src/com/android/settings/AccountPreference.java4
-rw-r--r--src/com/android/settings/DialogCreatable.java29
-rw-r--r--src/com/android/settings/Settings.java2
-rw-r--r--src/com/android/settings/SettingsPreferenceFragment.java13
-rw-r--r--src/com/android/settings/accounts/AccountPreferenceBase.java198
-rw-r--r--src/com/android/settings/accounts/AccountSyncSettings.java505
-rw-r--r--src/com/android/settings/accounts/AccountSyncSettingsInAddAccount.java42
-rw-r--r--src/com/android/settings/accounts/AddAccountSettings.java114
-rw-r--r--src/com/android/settings/accounts/ChooseAccountActivity.java233
-rw-r--r--src/com/android/settings/accounts/ManageAccountsSettings.java (renamed from src/com/android/settings/ManageAccountsSettings.java)106
-rw-r--r--src/com/android/settings/accounts/ProviderPreference.java46
-rw-r--r--src/com/android/settings/accounts/SyncActivityTooManyDeletes.java134
-rw-r--r--src/com/android/settings/accounts/SyncStateCheckBoxPreference.java165
13 files changed, 1498 insertions, 93 deletions
diff --git a/src/com/android/settings/AccountPreference.java b/src/com/android/settings/AccountPreference.java
index dc5633457..437839996 100644
--- a/src/com/android/settings/AccountPreference.java
+++ b/src/com/android/settings/AccountPreference.java
@@ -52,10 +52,6 @@ public class AccountPreference extends Preference {
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 000000000..1d10be7f8
--- /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 c94a0e168..a47fa5627 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -208,4 +208,6 @@ public class Settings extends PreferenceActivity {
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 3c771f592..a2f701d05 100644
--- a/src/com/android/settings/SettingsPreferenceFragment.java
+++ b/src/com/android/settings/SettingsPreferenceFragment.java
@@ -34,15 +34,6 @@ import android.view.View.OnClickListener;
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 @@ public class SettingsPreferenceFragment extends PreferenceFragment
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 000000000..a84bece04
--- /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 000000000..141f2445f
--- /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 000000000..8fa576ab1
--- /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 000000000..4c5c0b8c8
--- /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 000000000..9d4965f71
--- /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
index 93053f166..8f61516c2 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.net.ConnectivityManager;
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.HashMap;
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 @@ public class ManageAccountsSettings extends PreferenceFragment
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 class ManageAccountsSettings extends PreferenceFragment
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 @@ public class ManageAccountsSettings extends PreferenceFragment
}
@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 @@ public class ManageAccountsSettings extends PreferenceFragment
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 @@ public class ManageAccountsSettings extends PreferenceFragment
} 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 @@ public class ManageAccountsSettings extends PreferenceFragment
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 @@ public class ManageAccountsSettings extends PreferenceFragment
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 @@ public class ManageAccountsSettings extends PreferenceFragment
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 @@ public class ManageAccountsSettings extends PreferenceFragment
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 000000000..399652394
--- /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 000000000..b31561cc1
--- /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 000000000..b200eb67d
--- /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;
+ }
+}