ManageAccountsSettings.java revision 97d07fa3aedde44368818551dc789eaff7bfb047
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 android.accounts.Account;
20import android.accounts.AccountManager;
21import android.accounts.OnAccountsUpdateListener;
22import android.app.ActionBar;
23import android.app.Activity;
24import android.app.ActivityManager;
25import android.app.ActivityManagerNative;
26import android.content.ContentResolver;
27import android.content.Context;
28import android.content.Intent;
29import android.content.SyncAdapterType;
30import android.content.SyncInfo;
31import android.content.SyncStatusInfo;
32import android.content.pm.PackageManager;
33import android.content.pm.ResolveInfo;
34import android.graphics.drawable.Drawable;
35import android.os.Bundle;
36import android.preference.Preference;
37import android.preference.PreferenceActivity;
38import android.preference.PreferenceScreen;
39import android.text.format.DateFormat;
40import android.util.Log;
41import android.view.Gravity;
42import android.view.LayoutInflater;
43import android.view.Menu;
44import android.view.MenuInflater;
45import android.view.MenuItem;
46import android.view.View;
47import android.view.ViewGroup;
48import android.widget.CompoundButton;
49import android.widget.ListView;
50import android.widget.Switch;
51import android.widget.TextView;
52
53import com.android.settings.AccountPreference;
54import com.android.settings.R;
55import com.android.settings.Settings;
56import com.android.settings.Utils;
57
58import java.util.ArrayList;
59import java.util.Date;
60import java.util.HashSet;
61
62public class ManageAccountsSettings extends AccountPreferenceBase
63        implements OnAccountsUpdateListener {
64
65    private static final String ACCOUNT_KEY = "account"; // to pass to auth settings
66    public static final String KEY_ACCOUNT_TYPE = "account_type";
67    public static final String KEY_ACCOUNT_LABEL = "account_label";
68
69    private static final int MENU_SYNC_NOW_ID = Menu.FIRST;
70    private static final int MENU_SYNC_CANCEL_ID    = Menu.FIRST + 1;
71
72    private static final int REQUEST_SHOW_SYNC_SETTINGS = 1;
73
74    private String[] mAuthorities;
75    private TextView mErrorInfoView;
76
77    private SettingsDialogFragment mDialogFragment;
78    // If an account type is set, then show only accounts of that type
79    private String mAccountType;
80    // Temporary hack, to deal with backward compatibility
81    private Account mFirstAccount;
82
83    @Override
84    public void onCreate(Bundle icicle) {
85        super.onCreate(icicle);
86
87        Bundle args = getArguments();
88        if (args != null && args.containsKey(KEY_ACCOUNT_TYPE)) {
89            mAccountType = args.getString(KEY_ACCOUNT_TYPE);
90        }
91        addPreferencesFromResource(R.xml.manage_accounts_settings);
92        setHasOptionsMenu(true);
93    }
94
95    @Override
96    public void onStart() {
97        super.onStart();
98        Activity activity = getActivity();
99        AccountManager.get(activity).addOnAccountsUpdatedListener(this, null, true);
100    }
101
102    @Override
103    public View onCreateView(LayoutInflater inflater, ViewGroup container,
104            Bundle savedInstanceState) {
105        final View view = inflater.inflate(R.layout.manage_accounts_screen, container, false);
106        final ListView list = (ListView) view.findViewById(android.R.id.list);
107        Utils.prepareCustomPreferencesList(container, view, list, false);
108        return view;
109    }
110
111    @Override
112    public void onActivityCreated(Bundle savedInstanceState) {
113        super.onActivityCreated(savedInstanceState);
114
115        final Activity activity = getActivity();
116        final View view = getView();
117
118        mErrorInfoView = (TextView)view.findViewById(R.id.sync_settings_error_info);
119        mErrorInfoView.setVisibility(View.GONE);
120
121        mAuthorities = activity.getIntent().getStringArrayExtra(AUTHORITIES_FILTER_KEY);
122
123        Bundle args = getArguments();
124        if (args != null && args.containsKey(KEY_ACCOUNT_LABEL)) {
125            getActivity().setTitle(args.getString(KEY_ACCOUNT_LABEL));
126        }
127        updateAuthDescriptions();
128    }
129
130    @Override
131    public void onStop() {
132        super.onStop();
133        final Activity activity = getActivity();
134        AccountManager.get(activity).removeOnAccountsUpdatedListener(this);
135        activity.getActionBar().setDisplayOptions(0, ActionBar.DISPLAY_SHOW_CUSTOM);
136        activity.getActionBar().setCustomView(null);
137    }
138
139    @Override
140    public boolean onPreferenceTreeClick(PreferenceScreen preferences, Preference preference) {
141        if (preference instanceof AccountPreference) {
142            startAccountSettings((AccountPreference) preference);
143        } else {
144            return false;
145        }
146        return true;
147    }
148
149    private void startAccountSettings(AccountPreference acctPref) {
150        Bundle args = new Bundle();
151        args.putParcelable(AccountSyncSettings.ACCOUNT_KEY, acctPref.getAccount());
152        ((PreferenceActivity) getActivity()).startPreferencePanel(
153                AccountSyncSettings.class.getCanonicalName(), args,
154                R.string.account_sync_settings_title, acctPref.getAccount().name,
155                this, REQUEST_SHOW_SYNC_SETTINGS);
156    }
157
158    @Override
159    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
160        MenuItem syncNow = menu.add(0, MENU_SYNC_NOW_ID, 0,
161                getString(R.string.sync_menu_sync_now))
162                .setIcon(R.drawable.ic_menu_refresh_holo_dark);
163        MenuItem syncCancel = menu.add(0, MENU_SYNC_CANCEL_ID, 0,
164                getString(R.string.sync_menu_sync_cancel))
165                .setIcon(com.android.internal.R.drawable.ic_menu_close_clear_cancel);
166        super.onCreateOptionsMenu(menu, inflater);
167    }
168
169    @Override
170    public void onPrepareOptionsMenu(Menu menu) {
171        super.onPrepareOptionsMenu(menu);
172        boolean syncActive = ContentResolver.getCurrentSync() != null;
173        menu.findItem(MENU_SYNC_NOW_ID).setVisible(!syncActive && mFirstAccount != null);
174        menu.findItem(MENU_SYNC_CANCEL_ID).setVisible(syncActive && mFirstAccount != null);
175    }
176
177    @Override
178    public boolean onOptionsItemSelected(MenuItem item) {
179        switch (item.getItemId()) {
180        case MENU_SYNC_NOW_ID:
181            requestOrCancelSyncForAccounts(true);
182            return true;
183        case MENU_SYNC_CANCEL_ID:
184            requestOrCancelSyncForAccounts(false);
185            return true;
186        }
187        return super.onOptionsItemSelected(item);
188    }
189
190    private void requestOrCancelSyncForAccounts(boolean sync) {
191        SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypes();
192        Bundle extras = new Bundle();
193        extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
194        int count = getPreferenceScreen().getPreferenceCount();
195        // For each account
196        for (int i = 0; i < count; i++) {
197            Preference pref = getPreferenceScreen().getPreference(i);
198            if (pref instanceof AccountPreference) {
199                Account account = ((AccountPreference) pref).getAccount();
200                // For all available sync authorities, sync those that are enabled for the account
201                for (int j = 0; j < syncAdapters.length; j++) {
202                    SyncAdapterType sa = syncAdapters[j];
203                    if (syncAdapters[j].accountType.equals(mAccountType)
204                            && ContentResolver.getSyncAutomatically(account, sa.authority)) {
205                        if (sync) {
206                            ContentResolver.requestSync(account, sa.authority, extras);
207                        } else {
208                            ContentResolver.cancelSync(account, sa.authority);
209                        }
210                    }
211                }
212            }
213        }
214    }
215
216    @Override
217    protected void onSyncStateUpdated() {
218        // Catch any delayed delivery of update messages
219        if (getActivity() == null) return;
220
221        // iterate over all the preferences, setting the state properly for each
222        SyncInfo currentSync = ContentResolver.getCurrentSync();
223
224        boolean anySyncFailed = false; // true if sync on any account failed
225        Date date = new Date();
226
227        // only track userfacing sync adapters when deciding if account is synced or not
228        final SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypes();
229        HashSet<String> userFacing = new HashSet<String>();
230        for (int k = 0, n = syncAdapters.length; k < n; k++) {
231            final SyncAdapterType sa = syncAdapters[k];
232            if (sa.isUserVisible()) {
233                userFacing.add(sa.authority);
234            }
235        }
236        for (int i = 0, count = getPreferenceScreen().getPreferenceCount(); i < count; i++) {
237            Preference pref = getPreferenceScreen().getPreference(i);
238            if (! (pref instanceof AccountPreference)) {
239                continue;
240            }
241
242            AccountPreference accountPref = (AccountPreference) pref;
243            Account account = accountPref.getAccount();
244            int syncCount = 0;
245            long lastSuccessTime = 0;
246            boolean syncIsFailing = false;
247            final ArrayList<String> authorities = accountPref.getAuthorities();
248            boolean syncingNow = false;
249            if (authorities != null) {
250                for (String authority : authorities) {
251                    SyncStatusInfo status = ContentResolver.getSyncStatus(account, authority);
252                    boolean syncEnabled = ContentResolver.getSyncAutomatically(account, authority)
253                            && ContentResolver.getMasterSyncAutomatically()
254                            && (ContentResolver.getIsSyncable(account, authority) > 0);
255                    boolean authorityIsPending = ContentResolver.isSyncPending(account, authority);
256                    boolean activelySyncing = currentSync != null
257                            && currentSync.authority.equals(authority)
258                            && new Account(currentSync.account.name, currentSync.account.type)
259                                    .equals(account);
260                    boolean lastSyncFailed = status != null
261                            && syncEnabled
262                            && status.lastFailureTime != 0
263                            && status.getLastFailureMesgAsInt(0)
264                               != ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS;
265                    if (lastSyncFailed && !activelySyncing && !authorityIsPending) {
266                        syncIsFailing = true;
267                        anySyncFailed = true;
268                    }
269                    syncingNow |= activelySyncing;
270                    if (status != null && lastSuccessTime < status.lastSuccessTime) {
271                        lastSuccessTime = status.lastSuccessTime;
272                    }
273                    syncCount += syncEnabled && userFacing.contains(authority) ? 1 : 0;
274                }
275            } else {
276                if (Log.isLoggable(TAG, Log.VERBOSE)) {
277                    Log.v(TAG, "no syncadapters found for " + account);
278                }
279            }
280            if (syncIsFailing) {
281                accountPref.setSyncStatus(AccountPreference.SYNC_ERROR, true);
282            } else if (syncCount == 0) {
283                accountPref.setSyncStatus(AccountPreference.SYNC_DISABLED, true);
284            } else if (syncCount > 0) {
285                if (syncingNow) {
286                    accountPref.setSyncStatus(AccountPreference.SYNC_IN_PROGRESS, true);
287                } else {
288                    accountPref.setSyncStatus(AccountPreference.SYNC_ENABLED, true);
289                    if (lastSuccessTime > 0) {
290                        accountPref.setSyncStatus(AccountPreference.SYNC_ENABLED, false);
291                        date.setTime(lastSuccessTime);
292                        final String timeString = formatSyncDate(date);
293                        accountPref.setSummary(getResources().getString(
294                                R.string.last_synced, timeString));
295                    }
296                }
297            } else {
298                accountPref.setSyncStatus(AccountPreference.SYNC_DISABLED, true);
299            }
300        }
301
302        mErrorInfoView.setVisibility(anySyncFailed ? View.VISIBLE : View.GONE);
303    }
304
305    @Override
306    public void onAccountsUpdated(Account[] accounts) {
307        if (getActivity() == null) return;
308        getPreferenceScreen().removeAll();
309        mFirstAccount = null;
310        addPreferencesFromResource(R.xml.manage_accounts_settings);
311        for (int i = 0, n = accounts.length; i < n; i++) {
312            final Account account = accounts[i];
313            // If an account type is specified for this screen, skip other types
314            if (mAccountType != null && !account.type.equals(mAccountType)) continue;
315            final ArrayList<String> auths = getAuthoritiesForAccountType(account.type);
316
317            boolean showAccount = true;
318            if (mAuthorities != null && auths != null) {
319                showAccount = false;
320                for (String requestedAuthority : mAuthorities) {
321                    if (auths.contains(requestedAuthority)) {
322                        showAccount = true;
323                        break;
324                    }
325                }
326            }
327
328            if (showAccount) {
329                final Drawable icon = getDrawableForType(account.type);
330                final AccountPreference preference =
331                        new AccountPreference(getActivity(), account, icon, auths, false);
332                getPreferenceScreen().addPreference(preference);
333                if (mFirstAccount == null) {
334                    mFirstAccount = account;
335                }
336            }
337        }
338        if (mAccountType != null && mFirstAccount != null) {
339            addAuthenticatorSettings();
340        } else {
341            // There's no account, reset to top-level of settings
342            Intent settingsTop = new Intent(android.provider.Settings.ACTION_SETTINGS);
343            settingsTop.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
344            getActivity().startActivity(settingsTop);
345        }
346        onSyncStateUpdated();
347    }
348
349    private void addAuthenticatorSettings() {
350        PreferenceScreen prefs = addPreferencesForType(mAccountType, getPreferenceScreen());
351        if (prefs != null) {
352            updatePreferenceIntents(prefs);
353        }
354    }
355
356    private void updatePreferenceIntents(PreferenceScreen prefs) {
357        PackageManager pm = getActivity().getPackageManager();
358        for (int i = 0; i < prefs.getPreferenceCount();) {
359            Intent intent = prefs.getPreference(i).getIntent();
360            if (intent != null) {
361                ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
362                if (ri == null) {
363                    prefs.removePreference(prefs.getPreference(i));
364                    continue;
365                } else {
366                    intent.putExtra(ACCOUNT_KEY, mFirstAccount);
367                    intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
368                }
369            }
370            i++;
371        }
372    }
373
374    @Override
375    protected void onAuthDescriptionsUpdated() {
376        // Update account icons for all account preference items
377        for (int i = 0; i < getPreferenceScreen().getPreferenceCount(); i++) {
378            Preference pref = getPreferenceScreen().getPreference(i);
379            if (pref instanceof AccountPreference) {
380                AccountPreference accPref = (AccountPreference) pref;
381                accPref.setSummary(getLabelForType(accPref.getAccount().type));
382            }
383        }
384    }
385}
386