summaryrefslogtreecommitdiffstats
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
parentf3c32f49cdafef93ef783947d5ed4aaa287cba61 (diff)
downloadpackages_apps_Settings-43c697854c7e373fbc1dae8b7a5259a32de346b4.zip
packages_apps_Settings-43c697854c7e373fbc1dae8b7a5259a32de346b4.tar.gz
packages_apps_Settings-43c697854c7e373fbc1dae8b7a5259a32de346b4.tar.bz2
Move Account & sync settings into Settings app.
Fragmentized some of the activities and moved buttons into the menu area. Bug: 3148844
-rw-r--r--AndroidManifest.xml60
-rw-r--r--proguard.flags1
-rw-r--r--res/layout/account_sync_screen.xml86
-rw-r--r--res/layout/add_account_screen.xml33
-rw-r--r--res/layout/preference_widget_sync_toggle.xml57
-rw-r--r--res/layout/title.xml63
-rw-r--r--res/values/strings.xml71
-rw-r--r--res/xml/account_sync_settings.xml22
-rw-r--r--res/xml/add_account_settings.xml19
-rw-r--r--res/xml/settings_headers.xml2
-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
23 files changed, 1910 insertions, 95 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 1638c90..3de32cc 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -41,6 +41,13 @@
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
<uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.MOVE_PACKAGE" />
+ <uses-permission android:name="android.permission.USE_CREDENTIALS" />
+ <uses-permission android:name="android.permission.BACKUP" />
+ <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
+ <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
+ <uses-permission android:name="android.permission.READ_SYNC_STATS" />
+ <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
+ <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<application android:label="@string/settings_label"
android:icon="@mipmap/ic_launcher_settings"
@@ -942,11 +949,62 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
- android:value="com.android.settings.ManageAccountsSettings" />
+ android:value="com.android.settings.accounts.ManageAccountsSettings" />
+ <meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
+ android:resource="@id/sync_settings" />
+ </activity>
+
+ <activity android:name="Settings$AccountSyncSettingsActivity"
+ android:label="@string/account_sync_settings_title"
+ android:theme="@android:style/Theme.Holo">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <action android:name="android.settings.ACCOUNT_SYNC_SETTINGS" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+ android:value="com.android.settings.accounts.AccountSyncSettings" />
+ <meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
+ android:resource="@id/sync_settings" />
+ </activity>
+
+ <activity android:name="Settings$AccountSyncSettingsInAddAccountActivity"
+ android:label="@string/account_sync_settings_title"
+ android:theme="@android:style/Theme.Holo">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <action android:name="android.settings.ACCOUNT_SYNC_SETTINGS_ADD_ACCOUNT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+ android:value="com.android.settings.accounts.AccountSyncSettingsInAddAccount" />
<meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
android:resource="@id/sync_settings" />
</activity>
+ <activity android:name="com.android.settings.accounts.AddAccountSettings"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar"
+ android:label="@string/header_add_an_account">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <action android:name="android.settings.ADD_ACCOUNT_SETTINGS" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="com.android.settings.accounts.ChooseAccountActivity"
+ android:label="@string/header_add_an_account"
+ android:theme="@android:style/Theme.Holo.DialogWhenLarge"/>
+
+ <activity android:name="com.android.settings.accounts.SyncActivityTooManyDeletes"
+ android:theme="@android:style/Theme.Holo.Dialog"
+ android:label="@string/sync_too_many_deletes">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<receiver android:name=".widget.SettingsAppWidgetProvider"
android:label="@string/gadget_title" android:exported="false">
<intent-filter>
diff --git a/proguard.flags b/proguard.flags
index 7cd46e7..8318101 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -9,4 +9,5 @@
-keep class com.android.settings.inputmethod.*
-keep class com.android.settings.MasterClear
-keep class com.android.settings.MasterClearConfirm
+-keep class com.android.settings.accounts.*
diff --git a/res/layout/account_sync_screen.xml b/res/layout/account_sync_screen.xml
new file mode 100644
index 0000000..5a4bc59
--- /dev/null
+++ b/res/layout/account_sync_screen.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2006, 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.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <include layout="@layout/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <ListView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:drawSelectorOnTop="false"
+ android:scrollbarAlwaysDrawVerticalTrack="true"
+ />
+
+ <TextView android:id="@+id/sync_settings_error_info"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/sync_is_failing" />
+
+ <!--
+ <LinearLayout android:id="@+id/remove_account_area"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:background="@android:drawable/bottom_bar">
+
+ <View
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+
+ <Button android:id="@+id/remove_account_button"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="2"
+ android:layout_marginTop="5dip"
+ android:text="@string/remove_account_label" />
+
+ <View
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+ </LinearLayout>
+ -->
+
+ <RelativeLayout android:id="@+id/finish_button_area"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:background="@android:drawable/bottom_bar"
+ android:visibility="gone">
+
+ <Button android:id="@+id/finish_button"
+ android:layout_width="150dip"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dip"
+ android:layout_alignParentRight="true"
+ android:drawableRight="@drawable/ic_btn_next"
+ android:drawablePadding="3dip"
+ android:text="@string/finish_button_label"
+ />
+
+ </RelativeLayout>
+
+</LinearLayout>
diff --git a/res/layout/add_account_screen.xml b/res/layout/add_account_screen.xml
new file mode 100644
index 0000000..67cb7aa
--- /dev/null
+++ b/res/layout/add_account_screen.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/layout/list_content.xml
+**
+** Copyright 2006, 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.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <ListView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:drawSelectorOnTop="false"
+ android:scrollbarAlwaysDrawVerticalTrack="true"
+ />
+
+</LinearLayout>
diff --git a/res/layout/preference_widget_sync_toggle.xml b/res/layout/preference_widget_sync_toggle.xml
new file mode 100644
index 0000000..6eaa86e
--- /dev/null
+++ b/res/layout/preference_widget_sync_toggle.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 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.
+-->
+
+<!-- Layout used by SyncStateCheckBoxPreference. This is inflated
+ inside android.R.layout.preference. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|right"
+ android:gravity="center_vertical|right">
+
+ <ImageView
+ android:id="@+android:id/sync_failed"
+ android:src="@drawable/ic_list_syncerror"
+ android:layout_width="wrap_content"
+ android:layout_marginLeft="6sp"
+ android:layout_marginBottom="2sp"
+ android:layout_height="wrap_content" />
+
+ <ImageView
+ android:id="@+android:id/sync_active"
+ android:src="@drawable/ic_list_sync_anim"
+ android:layout_width="wrap_content"
+ android:layout_marginLeft="6sp"
+ android:layout_marginBottom="2sp"
+ android:layout_height="wrap_content" />
+
+ <ImageView
+ android:id="@+android:id/sync_pending"
+ android:src="@drawable/ic_list_sync_anim0"
+ android:layout_width="wrap_content"
+ android:layout_marginLeft="6sp"
+ android:layout_marginBottom="2sp"
+ android:layout_height="wrap_content" />
+
+ <CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+android:id/checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="4sp"
+ android:layout_gravity="center_vertical"
+ android:focusable="false"
+ android:clickable="false" />
+</LinearLayout>
diff --git a/res/layout/title.xml b/res/layout/title.xml
new file mode 100644
index 0000000..63a30a7
--- /dev/null
+++ b/res/layout/title.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (C) 2009 Google Inc.
+ *
+ * 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.
+ */
+-->
+
+<!-- The title area at the top of the settings pane -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/title_area"
+ android:orientation="horizontal">
+
+ <ImageView android:id="@+id/provider_icon"
+ android:layout_width="48dip"
+ android:layout_height="48dip"
+ android:layout_margin="5dip"
+ android:layout_gravity="center_vertical" />
+
+ <LinearLayout
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/user_id"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorTertiary"
+ android:textStyle="bold"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:gravity="center_vertical"
+ android:layout_marginTop="5dip"/>
+
+ <TextView
+ android:id="@+id/provider_id"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorTertiary"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:layout_gravity="bottom"
+ android:layout_marginBottom="5dip"/>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f4ff9f3..413807c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3015,6 +3015,8 @@ found in the list of installed applications.</string>
<!-- Do not translate. This is a stub which will be removed soon. -->
<string name="time_zone_auto_stub" translatable="false">Select Time Zone</string>
+ <!-- Account specific sync settings title [CHAR LIMIT=35] -->
+ <string name="account_sync_settings_title">Sync settings</string>
<!-- Message when sync is currently failing [CHAR LIMIT=100] -->
<string name="sync_is_failing">Sync is currently experiencing problems. It will be back shortly.</string>
<!-- Button label to add an account [CHAR LIMIT=20] -->
@@ -3046,4 +3048,73 @@ found in the list of installed applications.</string>
<!-- Sync status shown when last sync resulted in an error [CHAR LIMIT=20] -->
<string name="sync_error">Sync error</string>
+ <!-- Title of the feed synchronization activity. -->
+ <string name="app_label">Account and Sync Settings</string>
+ <!-- Error message when the sync tried to delete too many things -->
+ <string name="sync_too_many_deletes">Delete limit exceeded</string>
+ <!-- Data synchronization settings screen, setting option name -->
+ <string name="settings_backup">Back up settings</string>
+ <!-- Data synchronization settings screen, setting option summary text when check box is selected -->
+ <string name="settings_backup_summary">Back up my settings</string>
+ <!-- Data synchronization settings screen, menu option -->
+ <string name="sync_menu_sync_now">Sync now</string>
+ <!-- Data synchronization settings screen, menu option -->
+ <string name="sync_menu_sync_cancel">Cancel sync</string>
+ <!-- Data synchronization settings screen, summary of a sync provider (for example, Gmail) when autosync is off and the user wants to do a one-time sync. -->
+ <string name="sync_one_time_sync">Touch to sync now<xliff:g id="last_sync_time">\n%1$s</xliff:g></string>
+ <!-- Data synchronization settings screen, checkbox setting option name. Controls whether Gmail should be synced. Should use "Gmail" to refer to this app.-->
+ <string name="sync_gmail">Gmail</string>
+ <!-- Data synchronization settings screen, checkbox setting option name. Controls whether Calendar shoudl be synced. -->
+ <string name="sync_calendar">Calendar</string>
+ <!-- Data synchronization settings screen, checkbox setting option name -->
+ <string name="sync_contacts">Contacts</string>
+ <!-- Dialog message for when there are too many deletes that would take place and we want user confirmation -->
+ <string name="sync_too_many_deletes_desc">There are <xliff:g id="number_of_deleted_items">%1$d</xliff:g> deleted items for <xliff:g id="type_of_sync">%2$s</xliff:g>, account <xliff:g id="account_name">%3$s</xliff:g>. What would you like to do?</string>
+ <!-- Dialog action for when there are too many deletes that would take place and we want user confirmation, and the user wants to delete the items -->
+ <string name="sync_really_delete">Delete the items.</string>
+ <!-- Dialog action for when there are too many deletes that would take place and we want user confirmation, and the user wants to undo the deletions -->
+ <string name="sync_undo_deletes">Undo the deletes.</string>
+ <!-- Dialog action for when there are too many deletes that would take place and we want user confirmation, and the user wants to do nothing for now -->
+ <string name="sync_do_nothing">Do nothing for now.</string>
+ <!-- Message introducing the user to Google Sync. -->
+ <string name="sync_plug"><font fgcolor="#ffffffff">Welcome to Google sync!</font>
+ \nA Google approach to synchronizing data to allow access to your contacts, appointments, and more from wherever you are.
+ </string>
+
+ <!-- Header title for those settings relating to application syncing. -->
+ <string name="header_application_sync_settings">Application sync settings</string>
+
+ <!-- Header for data and synchronization -->
+ <string name="header_data_and_synchronization">Data &amp; synchronization</string>
+ <!-- Preference item that lets the user change password -->
+ <string name="preference_change_password_title">Change password</string>
+ <!-- Header that appears at the top of Account Settings screen -->
+ <string name="header_account_settings">Account settings</string>
+ <!-- Button label to remove current account -->
+ <string name="remove_account_label">Remove account</string>
+ <!-- Title shown in AddAccount -->
+ <string name="header_add_an_account">Add an account</string>
+ <!-- Button shown when this activity is run from SetupWizard -->
+ <string name="finish_button_label">Finish</string>
+ <!-- Title of remove message for remove account dialog -->
+ <string name="really_remove_account_title">Remove account</string>
+ <!-- Remove account message in dialog -->
+ <string name="really_remove_account_message" product="tablet">Do you really want to remove this account? Removing it will also delete all of its messages, contacts, and other data from the tablet.
+ \nProceed?</string>
+ <!-- Remove account message in dialog -->
+ <string name="really_remove_account_message" product="default">Do you really want to remove this account? Removing it will also delete all of its messages, contacts, and other data from the phone.
+ \nProceed?</string>
+ <!-- This is shown if the autheticator for a given account fails to remove it. -->
+ <string name="remove_account_failed" product="tablet">This account is required by some applications. You can only remove it by resetting the tablet to factory defaults (which deletes all your personal data). You do that in the Settings application, under Privacy.</string>
+ <!-- This is shown if the autheticator for a given account fails to remove it. -->
+ <string name="remove_account_failed" product="default">This account is required by some applications. You can only remove it by resetting the phone to factory defaults (which deletes all your personal data). You do that in the Settings application, under Privacy.</string>
+ <!-- What to show in messaging that refers to this provider, e.g. AccountSyncSettings -->
+ <string name="provider_label">Push subscriptions</string>
+ <!-- Formatter in AccountSyncSettings for each application we wish to synchronize, e.g. "Sync Calendar" -->
+ <string name="sync_item_title">Sync <xliff:g id="authority" example="Calendar">%s</xliff:g></string>
+ <!-- Title of dialog shown when you can't manually sync an item because it's disabled -->
+ <string name="cant_sync_dialog_title">Cannot manually sync</string>
+ <!-- Messaage shown in dialog when you can't manually sync -->
+ <string name="cant_sync_dialog_message">Sync for this item is currently disabled. To change your preference, temporarily turn on background data and automatic sync.</string>
+
</resources>
diff --git a/res/xml/account_sync_settings.xml b/res/xml/account_sync_settings.xml
new file mode 100644
index 0000000..ff49c9c
--- /dev/null
+++ b/res/xml/account_sync_settings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <PreferenceCategory android:key="dataAndSynchronization"
+ android:title="@string/header_data_and_synchronization" />
+
+</PreferenceScreen>
diff --git a/res/xml/add_account_settings.xml b/res/xml/add_account_settings.xml
new file mode 100644
index 0000000..0ec46b7
--- /dev/null
+++ b/res/xml/add_account_settings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+
+</PreferenceScreen>
diff --git a/res/xml/settings_headers.xml b/res/xml/settings_headers.xml
index e4b668b..25f9368 100644
--- a/res/xml/settings_headers.xml
+++ b/res/xml/settings_headers.xml
@@ -90,7 +90,7 @@
preference. -->
<header
- android:fragment="com.android.settings.ManageAccountsSettings"
+ android:fragment="com.android.settings.accounts.ManageAccountsSettings"
android:icon="@drawable/ic_settings_sync"
android:title="@string/sync_settings"
android:id="@+id/sync_settings" />
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 @@ 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 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 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 3c771f5..a2f701d 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 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
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.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 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;
+ }
+}