ManageAccountsSettings.java revision 855d216dd4abcf1d7f597f8cae21a44d49607eec
1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings.accounts;
18
19import com.android.settings.AccountPreference;
20import com.android.settings.DialogCreatable;
21import com.android.settings.R;
22import com.android.settings.vpn.VpnTypeSelection;
23import com.google.android.collect.Maps;
24
25import android.accounts.Account;
26import android.accounts.AccountManager;
27import android.accounts.AuthenticatorDescription;
28import android.accounts.OnAccountsUpdateListener;
29import android.app.Activity;
30import android.app.AlertDialog;
31import android.app.Dialog;
32import android.content.ContentResolver;
33import android.content.Context;
34import android.content.DialogInterface;
35import android.content.Intent;
36import android.content.SyncAdapterType;
37import android.content.SyncInfo;
38import android.content.SyncStatusInfo;
39import android.content.pm.PackageManager;
40import android.graphics.drawable.Drawable;
41import android.net.ConnectivityManager;
42import android.os.Bundle;
43import android.preference.CheckBoxPreference;
44import android.preference.Preference;
45import android.preference.PreferenceActivity;
46import android.preference.PreferenceCategory;
47import android.preference.PreferenceScreen;
48import android.util.Log;
49import android.view.LayoutInflater;
50import android.view.Menu;
51import android.view.MenuInflater;
52import android.view.MenuItem;
53import android.view.View;
54import android.view.ViewGroup;
55import android.widget.Button;
56import android.widget.TextView;
57
58import java.util.ArrayList;
59import java.util.HashMap;
60import java.util.HashSet;
61import java.util.Map;
62
63public class ManageAccountsSettings extends AccountPreferenceBase
64        implements OnAccountsUpdateListener, DialogCreatable {
65
66    private static final String TAG = ManageAccountsSettings.class.getSimpleName();
67
68    private static final String AUTHORITIES_FILTER_KEY = "authorities";
69    private static final boolean LDEBUG = Log.isLoggable(TAG, Log.DEBUG);
70
71    private static final String AUTO_SYNC_CHECKBOX_KEY = "syncAutomaticallyCheckBox";
72    private static final String MANAGE_ACCOUNTS_CATEGORY_KEY = "manageAccountsCategory";
73    private static final String BACKGROUND_DATA_CHECKBOX_KEY = "backgroundDataCheckBox";
74    private static final int DIALOG_DISABLE_BACKGROUND_DATA = 1;
75
76    private static final int MENU_ADD_ACCOUNT = Menu.FIRST;
77
78    private static final int REQUEST_SHOW_SYNC_SETTINGS = 1;
79
80    private CheckBoxPreference mBackgroundDataCheckBox;
81    private PreferenceCategory mManageAccountsCategory;
82    private String[] mAuthorities;
83    private TextView mErrorInfoView;
84    private Button mAddAccountButton;
85    private CheckBoxPreference mAutoSyncCheckbox;
86
87    private SettingsDialogFragment mDialogFragment;
88
89    private AuthenticatorDescription[] mAuthDescs;
90    private Map<String, AuthenticatorDescription> mTypeToAuthDescription
91            = new HashMap<String, AuthenticatorDescription>();
92    private HashMap<String, ArrayList<String>> mAccountTypeToAuthorities = null;
93
94    @Override
95    public void onCreate(Bundle icicle) {
96        super.onCreate(icicle);
97
98        addPreferencesFromResource(R.xml.manage_accounts_settings);
99        setHasOptionsMenu(true);
100    }
101
102    @Override
103    public void onStart() {
104        super.onStart();
105        AccountManager.get(getActivity()).addOnAccountsUpdatedListener(this, null, true);
106    }
107
108    @Override
109    public View onCreateView(LayoutInflater inflater, ViewGroup container,
110            Bundle savedInstanceState) {
111        final View view = inflater.inflate(R.layout.manage_accounts_screen, container, false);
112        return view;
113    }
114
115    @Override
116    public void onActivityCreated(Bundle savedInstanceState) {
117        super.onActivityCreated(savedInstanceState);
118
119        final Activity activity = getActivity();
120        final View view = getView();
121
122        mErrorInfoView = (TextView)view.findViewById(R.id.sync_settings_error_info);
123        mErrorInfoView.setVisibility(View.GONE);
124        mErrorInfoView.setCompoundDrawablesWithIntrinsicBounds(
125                activity.getResources().getDrawable(R.drawable.ic_list_syncerror),
126                null, null, null);
127
128        mBackgroundDataCheckBox = (CheckBoxPreference) findPreference(BACKGROUND_DATA_CHECKBOX_KEY);
129        mAutoSyncCheckbox = (CheckBoxPreference) findPreference(AUTO_SYNC_CHECKBOX_KEY);
130
131        mManageAccountsCategory = (PreferenceCategory)findPreference(MANAGE_ACCOUNTS_CATEGORY_KEY);
132        mAuthorities = activity.getIntent().getStringArrayExtra(AUTHORITIES_FILTER_KEY);
133
134        updateAuthDescriptions();
135    }
136
137    @Override
138    public void onStop() {
139        super.onStop();
140        AccountManager.get(getActivity()).removeOnAccountsUpdatedListener(this);
141    }
142
143    @Override
144    public boolean onPreferenceTreeClick(PreferenceScreen preferences, Preference preference) {
145        if (preference == mBackgroundDataCheckBox) {
146            final ConnectivityManager connManager = (ConnectivityManager)
147                    getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
148            final boolean oldBackgroundDataSetting = connManager.getBackgroundDataSetting();
149            final boolean backgroundDataSetting = mBackgroundDataCheckBox.isChecked();
150            if (oldBackgroundDataSetting != backgroundDataSetting) {
151                if (backgroundDataSetting) {
152                    setBackgroundDataInt(true);
153                    onSyncStateUpdated();
154                } else {
155                    // This will get unchecked only if the user hits "Ok"
156                    mBackgroundDataCheckBox.setChecked(true);
157                    showDialog(DIALOG_DISABLE_BACKGROUND_DATA);
158                }
159            }
160        } else if (preference == mAutoSyncCheckbox) {
161            ContentResolver.setMasterSyncAutomatically(mAutoSyncCheckbox.isChecked());
162            onSyncStateUpdated();
163        } else if (preference instanceof AccountPreference) {
164            startAccountSettings((AccountPreference) preference);
165        } else {
166            return false;
167        }
168        return true;
169    }
170
171    private void startAccountSettings(AccountPreference acctPref) {
172        Bundle args = new Bundle();
173        args.putParcelable(AccountSyncSettings.ACCOUNT_KEY, acctPref.getAccount());
174        ((PreferenceActivity) getActivity()).startPreferencePanel(
175                AccountSyncSettings.class.getCanonicalName(), args,
176                R.string.account_sync_settings_title, acctPref.getAccount().name,
177                this, REQUEST_SHOW_SYNC_SETTINGS);
178    }
179
180    @Override
181    public Dialog onCreateDialog(int id) {
182        switch (id) {
183            case DIALOG_DISABLE_BACKGROUND_DATA:
184                final CheckBoxPreference pref =
185                    (CheckBoxPreference) findPreference(BACKGROUND_DATA_CHECKBOX_KEY);
186                return new AlertDialog.Builder(getActivity())
187                        .setTitle(R.string.background_data_dialog_title)
188                        .setIcon(android.R.drawable.ic_dialog_alert)
189                        .setMessage(R.string.background_data_dialog_message)
190                        .setPositiveButton(android.R.string.ok,
191                                    new DialogInterface.OnClickListener() {
192                                public void onClick(DialogInterface dialog, int which) {
193                                    setBackgroundDataInt(false);
194                                    pref.setChecked(false);
195                                    onSyncStateUpdated();
196                                }
197                            })
198                        .setNegativeButton(android.R.string.cancel, null)
199                        .create();
200        }
201
202        return null;
203    }
204
205    public void showDialog(int dialogId) {
206        if (mDialogFragment != null) {
207            Log.e(TAG, "Old dialog fragment not null!");
208        }
209        mDialogFragment = new SettingsDialogFragment(this, dialogId);
210        mDialogFragment.show(getActivity().getFragmentManager(), Integer.toString(dialogId));
211    }
212
213    @Override
214    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
215        MenuItem actionItem =
216                menu.add(0, MENU_ADD_ACCOUNT, 0, R.string.add_account_label)
217                .setIcon(R.drawable.ic_menu_add);
218        actionItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM
219                | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
220    }
221
222    @Override
223    public boolean onOptionsItemSelected(MenuItem item) {
224        if (item.getItemId() == MENU_ADD_ACCOUNT) {
225            onAddAccountClicked();
226            return true;
227        } else {
228            return super.onOptionsItemSelected(item);
229        }
230    }
231
232    private void setBackgroundDataInt(boolean enabled) {
233        final ConnectivityManager connManager = (ConnectivityManager)
234                getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
235        connManager.setBackgroundDataSetting(enabled);
236    }
237
238    protected void onSyncStateUpdated() {
239        // Catch any delayed delivery of update messages
240        if (getActivity() == null) return;
241        // Set background connection state
242        final ConnectivityManager connManager = (ConnectivityManager)
243                getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
244        final boolean backgroundDataSetting = connManager.getBackgroundDataSetting();
245        mBackgroundDataCheckBox.setChecked(backgroundDataSetting);
246        boolean masterSyncAutomatically = ContentResolver.getMasterSyncAutomatically();
247        mAutoSyncCheckbox.setChecked(masterSyncAutomatically);
248
249        // iterate over all the preferences, setting the state properly for each
250        SyncInfo currentSync = ContentResolver.getCurrentSync();
251
252        boolean anySyncFailed = false; // true if sync on any account failed
253
254        // only track userfacing sync adapters when deciding if account is synced or not
255        final SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypes();
256        HashSet<String> userFacing = new HashSet<String>();
257        for (int k = 0, n = syncAdapters.length; k < n; k++) {
258            final SyncAdapterType sa = syncAdapters[k];
259            if (sa.isUserVisible()) {
260                userFacing.add(sa.authority);
261            }
262        }
263        for (int i = 0, count = mManageAccountsCategory.getPreferenceCount(); i < count; i++) {
264            Preference pref = mManageAccountsCategory.getPreference(i);
265            if (! (pref instanceof AccountPreference)) {
266                continue;
267            }
268
269            AccountPreference accountPref = (AccountPreference) pref;
270            Account account = accountPref.getAccount();
271            int syncCount = 0;
272            boolean syncIsFailing = false;
273            final ArrayList<String> authorities = accountPref.getAuthorities();
274            if (authorities != null) {
275                for (String authority : authorities) {
276                    SyncStatusInfo status = ContentResolver.getSyncStatus(account, authority);
277                    boolean syncEnabled = ContentResolver.getSyncAutomatically(account, authority)
278                            && masterSyncAutomatically
279                            && backgroundDataSetting
280                            && (ContentResolver.getIsSyncable(account, authority) > 0);
281                    boolean authorityIsPending = ContentResolver.isSyncPending(account, authority);
282                    boolean activelySyncing = currentSync != null
283                            && currentSync.authority.equals(authority)
284                            && new Account(currentSync.account.name, currentSync.account.type)
285                                    .equals(account);
286                    boolean lastSyncFailed = status != null
287                            && syncEnabled
288                            && status.lastFailureTime != 0
289                            && status.getLastFailureMesgAsInt(0)
290                               != ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS;
291                    if (lastSyncFailed && !activelySyncing && !authorityIsPending) {
292                        syncIsFailing = true;
293                        anySyncFailed = true;
294                    }
295                    syncCount += syncEnabled && userFacing.contains(authority) ? 1 : 0;
296                }
297            } else {
298                if (Log.isLoggable(TAG, Log.VERBOSE)) {
299                    Log.v(TAG, "no syncadapters found for " + account);
300                }
301            }
302            int syncStatus = AccountPreference.SYNC_DISABLED;
303            if (syncIsFailing) {
304                syncStatus = AccountPreference.SYNC_ERROR;
305            } else if (syncCount == 0) {
306                syncStatus = AccountPreference.SYNC_DISABLED;
307            } else if (syncCount > 0) {
308                syncStatus = AccountPreference.SYNC_ENABLED;
309            }
310            accountPref.setSyncStatus(syncStatus);
311        }
312
313        mErrorInfoView.setVisibility(anySyncFailed ? View.VISIBLE : View.GONE);
314    }
315
316    @Override
317    public void onAccountsUpdated(Account[] accounts) {
318        mManageAccountsCategory.removeAll();
319        for (int i = 0, n = accounts.length; i < n; i++) {
320            final Account account = accounts[i];
321            final ArrayList<String> auths = getAuthoritiesForAccountType(account.type);
322
323            boolean showAccount = true;
324            if (mAuthorities != null && auths != null) {
325                showAccount = false;
326                for (String requestedAuthority : mAuthorities) {
327                    if (auths.contains(requestedAuthority)) {
328                        showAccount = true;
329                        break;
330                    }
331                }
332            }
333
334            if (showAccount) {
335                final Drawable icon = getDrawableForType(account.type);
336                final AccountPreference preference =
337                        new AccountPreference(getActivity(), account, icon, auths);
338                mManageAccountsCategory.addPreference(preference);
339            }
340        }
341        onSyncStateUpdated();
342    }
343
344    protected void onAuthDescriptionsUpdated() {
345        // Update account icons for all account preference items
346        for (int i = 0; i < mManageAccountsCategory.getPreferenceCount(); i++) {
347            AccountPreference pref = (AccountPreference) mManageAccountsCategory.getPreference(i);
348            pref.setProviderIcon(getDrawableForType(pref.getAccount().type));
349            pref.setSummary(getLabelForType(pref.getAccount().type));
350        }
351    }
352
353    public void onAddAccountClicked() {
354        Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
355        intent.putExtra(AUTHORITIES_FILTER_KEY, mAuthorities);
356        startActivity(intent);
357    }
358}
359