OSDN Git Service

am 55ebba9b: Change to fit color name text on color selection spinner in settings...
[android-x86/packages-apps-Settings.git] / src / com / android / settings / accounts / AccountSyncSettings.java
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
17 package com.android.settings.accounts;
18
19 import com.google.android.collect.Lists;
20
21 import android.accounts.Account;
22 import android.accounts.AccountManager;
23 import android.accounts.AccountManagerCallback;
24 import android.accounts.AccountManagerFuture;
25 import android.accounts.AuthenticatorException;
26 import android.accounts.OperationCanceledException;
27 import android.app.Activity;
28 import android.app.AlertDialog;
29 import android.app.Dialog;
30 import android.content.ContentResolver;
31 import android.content.Context;
32 import android.content.DialogInterface;
33 import android.content.SyncAdapterType;
34 import android.content.SyncInfo;
35 import android.content.SyncStatusInfo;
36 import android.content.pm.ProviderInfo;
37 import android.os.Bundle;
38 import android.os.UserHandle;
39 import android.os.UserManager;
40 import android.preference.Preference;
41 import android.preference.PreferenceScreen;
42 import android.text.TextUtils;
43 import android.util.Log;
44 import android.view.LayoutInflater;
45 import android.view.Menu;
46 import android.view.MenuInflater;
47 import android.view.MenuItem;
48 import android.view.View;
49 import android.view.ViewGroup;
50 import android.widget.ImageView;
51 import android.widget.ListView;
52 import android.widget.TextView;
53
54 import com.android.settings.R;
55 import com.android.settings.Utils;
56
57 import java.io.IOException;
58 import java.util.ArrayList;
59 import java.util.Collections;
60 import java.util.Date;
61 import java.util.List;
62
63 public class AccountSyncSettings extends AccountPreferenceBase {
64
65     public static final String ACCOUNT_KEY = "account";
66     private static final int MENU_SYNC_NOW_ID       = Menu.FIRST;
67     private static final int MENU_SYNC_CANCEL_ID    = Menu.FIRST + 1;
68     private static final int MENU_REMOVE_ACCOUNT_ID = Menu.FIRST + 2;
69     private static final int REALLY_REMOVE_DIALOG = 100;
70     private static final int FAILED_REMOVAL_DIALOG = 101;
71     private static final int CANT_DO_ONETIME_SYNC_DIALOG = 102;
72     private TextView mUserId;
73     private TextView mProviderId;
74     private ImageView mProviderIcon;
75     private TextView mErrorInfoView;
76     private Account mAccount;
77     private ArrayList<SyncStateSwitchPreference> mSwitches =
78                 new ArrayList<SyncStateSwitchPreference>();
79     private ArrayList<SyncAdapterType> mInvisibleAdapters = Lists.newArrayList();
80
81     @Override
82     public Dialog onCreateDialog(final int id) {
83         Dialog dialog = null;
84         if (id == REALLY_REMOVE_DIALOG) {
85             dialog = new AlertDialog.Builder(getActivity())
86                 .setTitle(R.string.really_remove_account_title)
87                 .setMessage(R.string.really_remove_account_message)
88                 .setNegativeButton(android.R.string.cancel, null)
89                 .setPositiveButton(R.string.remove_account_label,
90                         new DialogInterface.OnClickListener() {
91                     @Override
92                     public void onClick(DialogInterface dialog, int which) {
93                         Activity activity = getActivity();
94                         AccountManager.get(activity)
95                                 .removeAccountAsUser(mAccount, activity,
96                                 new AccountManagerCallback<Bundle>() {
97                             @Override
98                             public void run(AccountManagerFuture<Bundle> future) {
99                                 // If already out of this screen, don't proceed.
100                                 if (!AccountSyncSettings.this.isResumed()) {
101                                     return;
102                                 }
103                                 boolean failed = true;
104                                 try {
105                                     if (future.getResult()
106                                             .getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) {
107                                         failed = false;
108                                     }
109                                 } catch (OperationCanceledException e) {
110                                     // handled below
111                                 } catch (IOException e) {
112                                     // handled below
113                                 } catch (AuthenticatorException e) {
114                                     // handled below
115                                 }
116                                 if (failed && getActivity() != null &&
117                                         !getActivity().isFinishing()) {
118                                     showDialog(FAILED_REMOVAL_DIALOG);
119                                 } else {
120                                     finish();
121                                 }
122                             }
123                         }, null, mUserHandle);
124                     }
125                 })
126                 .create();
127         } else if (id == FAILED_REMOVAL_DIALOG) {
128             dialog = new AlertDialog.Builder(getActivity())
129                 .setTitle(R.string.really_remove_account_title)
130                 .setPositiveButton(android.R.string.ok, null)
131                 .setMessage(R.string.remove_account_failed)
132                 .create();
133         } else if (id == CANT_DO_ONETIME_SYNC_DIALOG) {
134             dialog = new AlertDialog.Builder(getActivity())
135                 .setTitle(R.string.cant_sync_dialog_title)
136                 .setMessage(R.string.cant_sync_dialog_message)
137                 .setPositiveButton(android.R.string.ok, null)
138                 .create();
139         }
140         return dialog;
141     }
142
143     @Override
144     public void onCreate(Bundle icicle) {
145         super.onCreate(icicle);
146
147         setHasOptionsMenu(true);
148     }
149
150     @Override
151     public View onCreateView(LayoutInflater inflater, ViewGroup container,
152             Bundle savedInstanceState) {
153         final View view = inflater.inflate(R.layout.account_sync_screen, container, false);
154
155         final ListView list = (ListView) view.findViewById(android.R.id.list);
156         Utils.prepareCustomPreferencesList(container, view, list, false);
157
158         initializeUi(view);
159
160         return view;
161     }
162
163     protected void initializeUi(final View rootView) {
164         addPreferencesFromResource(R.xml.account_sync_settings);
165
166         mErrorInfoView = (TextView) rootView.findViewById(R.id.sync_settings_error_info);
167         mErrorInfoView.setVisibility(View.GONE);
168
169         mUserId = (TextView) rootView.findViewById(R.id.user_id);
170         mProviderId = (TextView) rootView.findViewById(R.id.provider_id);
171         mProviderIcon = (ImageView) rootView.findViewById(R.id.provider_icon);
172     }
173
174     @Override
175     public void onActivityCreated(Bundle savedInstanceState) {
176         super.onActivityCreated(savedInstanceState);
177
178         Bundle arguments = getArguments();
179         if (arguments == null) {
180             Log.e(TAG, "No arguments provided when starting intent. ACCOUNT_KEY needed.");
181             finish();
182             return;
183         }
184         mAccount = (Account) arguments.getParcelable(ACCOUNT_KEY);
185         if (!accountExists(mAccount)) {
186             Log.e(TAG, "Account provided does not exist: " + mAccount);
187             finish();
188             return;
189         }
190         if (Log.isLoggable(TAG, Log.VERBOSE)) {
191           Log.v(TAG, "Got account: " + mAccount);
192         }
193         mUserId.setText(mAccount.name);
194         mProviderId.setText(mAccount.type);
195     }
196
197     @Override
198     public void onResume() {
199         mAuthenticatorHelper.listenToAccountUpdates();
200         updateAuthDescriptions();
201         onAccountsUpdate(UserHandle.getCallingUserHandle());
202
203         super.onResume();
204     }
205
206     @Override
207     public void onPause() {
208         super.onPause();
209         mAuthenticatorHelper.stopListeningToAccountUpdates();
210     }
211
212     private void addSyncStateSwitch(Account account, String authority) {
213         SyncStateSwitchPreference item =
214                 new SyncStateSwitchPreference(getActivity(), account, authority);
215         item.setPersistent(false);
216         final ProviderInfo providerInfo = getPackageManager().resolveContentProviderAsUser(
217                 authority, 0, mUserHandle.getIdentifier());
218         if (providerInfo == null) {
219             return;
220         }
221         CharSequence providerLabel = providerInfo.loadLabel(getPackageManager());
222         if (TextUtils.isEmpty(providerLabel)) {
223             Log.e(TAG, "Provider needs a label for authority '" + authority + "'");
224             return;
225         }
226         String title = getString(R.string.sync_item_title, providerLabel);
227         item.setTitle(title);
228         item.setKey(authority);
229         mSwitches.add(item);
230     }
231
232     @Override
233     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
234
235         MenuItem syncNow = menu.add(0, MENU_SYNC_NOW_ID, 0,
236                 getString(R.string.sync_menu_sync_now))
237                 .setIcon(R.drawable.ic_menu_refresh_holo_dark);
238         MenuItem syncCancel = menu.add(0, MENU_SYNC_CANCEL_ID, 0,
239                 getString(R.string.sync_menu_sync_cancel))
240                 .setIcon(com.android.internal.R.drawable.ic_menu_close_clear_cancel);
241         final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
242         if (!um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, mUserHandle)) {
243             MenuItem removeAccount = menu.add(0, MENU_REMOVE_ACCOUNT_ID, 0,
244                     getString(R.string.remove_account_label))
245                     .setIcon(R.drawable.ic_menu_delete);
246             removeAccount.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER |
247                     MenuItem.SHOW_AS_ACTION_WITH_TEXT);
248         }
249         syncNow.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER |
250                 MenuItem.SHOW_AS_ACTION_WITH_TEXT);
251         syncCancel.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER |
252                 MenuItem.SHOW_AS_ACTION_WITH_TEXT);
253
254         super.onCreateOptionsMenu(menu, inflater);
255     }
256
257     @Override
258     public void onPrepareOptionsMenu(Menu menu) {
259         super.onPrepareOptionsMenu(menu);
260         // Note that this also counts accounts that are not currently displayed
261         boolean syncActive = !ContentResolver.getCurrentSyncsAsUser(
262                 mUserHandle.getIdentifier()).isEmpty();
263         menu.findItem(MENU_SYNC_NOW_ID).setVisible(!syncActive);
264         menu.findItem(MENU_SYNC_CANCEL_ID).setVisible(syncActive);
265     }
266
267     @Override
268     public boolean onOptionsItemSelected(MenuItem item) {
269         switch (item.getItemId()) {
270             case MENU_SYNC_NOW_ID:
271                 startSyncForEnabledProviders();
272                 return true;
273             case MENU_SYNC_CANCEL_ID:
274                 cancelSyncForEnabledProviders();
275                 return true;
276             case MENU_REMOVE_ACCOUNT_ID:
277                 showDialog(REALLY_REMOVE_DIALOG);
278                 return true;
279         }
280         return super.onOptionsItemSelected(item);
281     }
282
283     @Override
284     public boolean onPreferenceTreeClick(PreferenceScreen preferences, Preference preference) {
285         if (preference instanceof SyncStateSwitchPreference) {
286             SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) preference;
287             String authority = syncPref.getAuthority();
288             Account account = syncPref.getAccount();
289             final int userId = mUserHandle.getIdentifier();
290             boolean syncAutomatically = ContentResolver.getSyncAutomaticallyAsUser(account,
291                     authority, userId);
292             if (syncPref.isOneTimeSyncMode()) {
293                 requestOrCancelSync(account, authority, true);
294             } else {
295                 boolean syncOn = syncPref.isChecked();
296                 boolean oldSyncState = syncAutomatically;
297                 if (syncOn != oldSyncState) {
298                     // if we're enabling sync, this will request a sync as well
299                     ContentResolver.setSyncAutomaticallyAsUser(account, authority, syncOn, userId);
300                     // if the master sync switch is off, the request above will
301                     // get dropped.  when the user clicks on this toggle,
302                     // we want to force the sync, however.
303                     if (!ContentResolver.getMasterSyncAutomaticallyAsUser(userId) || !syncOn) {
304                         requestOrCancelSync(account, authority, syncOn);
305                     }
306                 }
307             }
308             return true;
309         } else {
310             return super.onPreferenceTreeClick(preferences, preference);
311         }
312     }
313
314     private void startSyncForEnabledProviders() {
315         requestOrCancelSyncForEnabledProviders(true /* start them */);
316         final Activity activity = getActivity();
317         if (activity != null) {
318             activity.invalidateOptionsMenu();
319         }
320     }
321
322     private void cancelSyncForEnabledProviders() {
323         requestOrCancelSyncForEnabledProviders(false /* cancel them */);
324         final Activity activity = getActivity();
325         if (activity != null) {
326             activity.invalidateOptionsMenu();
327         }
328     }
329
330     private void requestOrCancelSyncForEnabledProviders(boolean startSync) {
331         // sync everything that the user has enabled
332         int count = getPreferenceScreen().getPreferenceCount();
333         for (int i = 0; i < count; i++) {
334             Preference pref = getPreferenceScreen().getPreference(i);
335             if (! (pref instanceof SyncStateSwitchPreference)) {
336                 continue;
337             }
338             SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) pref;
339             if (!syncPref.isChecked()) {
340                 continue;
341             }
342             requestOrCancelSync(syncPref.getAccount(), syncPref.getAuthority(), startSync);
343         }
344         // plus whatever the system needs to sync, e.g., invisible sync adapters
345         if (mAccount != null) {
346             for (SyncAdapterType syncAdapter : mInvisibleAdapters) {
347                   requestOrCancelSync(mAccount, syncAdapter.authority, startSync);
348             }
349         }
350     }
351
352     private void requestOrCancelSync(Account account, String authority, boolean flag) {
353         if (flag) {
354             Bundle extras = new Bundle();
355             extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
356             ContentResolver.requestSyncAsUser(account, authority, mUserHandle.getIdentifier(),
357                     extras);
358         } else {
359             ContentResolver.cancelSyncAsUser(account, authority, mUserHandle.getIdentifier());
360         }
361     }
362
363     private boolean isSyncing(List<SyncInfo> currentSyncs, Account account, String authority) {
364         for (SyncInfo syncInfo : currentSyncs) {
365             if (syncInfo.account.equals(account) && syncInfo.authority.equals(authority)) {
366                 return true;
367             }
368         }
369         return false;
370     }
371
372     @Override
373     protected void onSyncStateUpdated() {
374         if (!isResumed()) return;
375         setFeedsState();
376         final Activity activity = getActivity();
377         if (activity != null) {
378             activity.invalidateOptionsMenu();
379         }
380     }
381
382     private void setFeedsState() {
383         // iterate over all the preferences, setting the state properly for each
384         Date date = new Date();
385         final int userId = mUserHandle.getIdentifier();
386         List<SyncInfo> currentSyncs = ContentResolver.getCurrentSyncsAsUser(userId);
387         boolean syncIsFailing = false;
388
389         // Refresh the sync status switches - some syncs may have become active.
390         updateAccountSwitches();
391
392         for (int i = 0, count = getPreferenceScreen().getPreferenceCount(); i < count; i++) {
393             Preference pref = getPreferenceScreen().getPreference(i);
394             if (! (pref instanceof SyncStateSwitchPreference)) {
395                 continue;
396             }
397             SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) pref;
398
399             String authority = syncPref.getAuthority();
400             Account account = syncPref.getAccount();
401
402             SyncStatusInfo status = ContentResolver.getSyncStatusAsUser(account, authority, userId);
403             boolean syncEnabled = ContentResolver.getSyncAutomaticallyAsUser(account, authority,
404                     userId);
405             boolean authorityIsPending = status == null ? false : status.pending;
406             boolean initialSync = status == null ? false : status.initialize;
407
408             boolean activelySyncing = isSyncing(currentSyncs, account, authority);
409             boolean lastSyncFailed = status != null
410                     && status.lastFailureTime != 0
411                     && status.getLastFailureMesgAsInt(0)
412                        != ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS;
413             if (!syncEnabled) lastSyncFailed = false;
414             if (lastSyncFailed && !activelySyncing && !authorityIsPending) {
415                 syncIsFailing = true;
416             }
417             if (Log.isLoggable(TAG, Log.VERBOSE)) {
418                 Log.d(TAG, "Update sync status: " + account + " " + authority +
419                         " active = " + activelySyncing + " pend =" +  authorityIsPending);
420             }
421
422             final long successEndTime = (status == null) ? 0 : status.lastSuccessTime;
423             if (!syncEnabled) {
424                 syncPref.setSummary(R.string.sync_disabled);
425             } else if (activelySyncing) {
426                 syncPref.setSummary(R.string.sync_in_progress);
427             } else if (successEndTime != 0) {
428                 date.setTime(successEndTime);
429                 final String timeString = formatSyncDate(date);
430                 syncPref.setSummary(getResources().getString(R.string.last_synced, timeString));
431             } else {
432                 syncPref.setSummary("");
433             }
434             int syncState = ContentResolver.getIsSyncableAsUser(account, authority, userId);
435
436             syncPref.setActive(activelySyncing && (syncState >= 0) &&
437                     !initialSync);
438             syncPref.setPending(authorityIsPending && (syncState >= 0) &&
439                     !initialSync);
440
441             syncPref.setFailed(lastSyncFailed);
442             final boolean oneTimeSyncMode = !ContentResolver.getMasterSyncAutomaticallyAsUser(
443                 userId);
444             syncPref.setOneTimeSyncMode(oneTimeSyncMode);
445             syncPref.setChecked(oneTimeSyncMode || syncEnabled);
446         }
447         mErrorInfoView.setVisibility(syncIsFailing ? View.VISIBLE : View.GONE);
448     }
449
450     @Override
451     public void onAccountsUpdate(final UserHandle userHandle) {
452         super.onAccountsUpdate(userHandle);
453         if (!accountExists(mAccount)) {
454             // The account was deleted
455             finish();
456             return;
457         }
458         updateAccountSwitches();
459         onSyncStateUpdated();
460     }
461
462     private boolean accountExists(Account account) {
463         if (account == null) return false;
464
465         Account[] accounts = AccountManager.get(getActivity()).getAccountsByTypeAsUser(
466                 account.type, mUserHandle);
467         final int count = accounts.length;
468         for (int i = 0; i < count; i++) {
469             if (accounts[i].equals(account)) {
470                 return true;
471             }
472         }
473         return false;
474     }
475
476     private void updateAccountSwitches() {
477         mInvisibleAdapters.clear();
478
479         SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypesAsUser(
480                 mUserHandle.getIdentifier());
481         ArrayList<String> authorities = new ArrayList<String>();
482         for (int i = 0, n = syncAdapters.length; i < n; i++) {
483             final SyncAdapterType sa = syncAdapters[i];
484             // Only keep track of sync adapters for this account
485             if (!sa.accountType.equals(mAccount.type)) continue;
486             if (sa.isUserVisible()) {
487                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
488                     Log.d(TAG, "updateAccountSwitches: added authority " + sa.authority
489                             + " to accountType " + sa.accountType);
490                 }
491                 authorities.add(sa.authority);
492             } else {
493                 // keep track of invisible sync adapters, so sync now forces
494                 // them to sync as well.
495                 mInvisibleAdapters.add(sa);
496             }
497         }
498
499         for (int i = 0, n = mSwitches.size(); i < n; i++) {
500             getPreferenceScreen().removePreference(mSwitches.get(i));
501         }
502         mSwitches.clear();
503
504         if (Log.isLoggable(TAG, Log.VERBOSE)) {
505             Log.d(TAG, "looking for sync adapters that match account " + mAccount);
506         }
507         for (int j = 0, m = authorities.size(); j < m; j++) {
508             final String authority = authorities.get(j);
509             // We could check services here....
510             int syncState = ContentResolver.getIsSyncableAsUser(mAccount, authority,
511                     mUserHandle.getIdentifier());
512             if (Log.isLoggable(TAG, Log.VERBOSE)) {
513                 Log.d(TAG, "  found authority " + authority + " " + syncState);
514             }
515             if (syncState > 0) {
516                 addSyncStateSwitch(mAccount, authority);
517             }
518         }
519
520         Collections.sort(mSwitches);
521         for (int i = 0, n = mSwitches.size(); i < n; i++) {
522             getPreferenceScreen().addPreference(mSwitches.get(i));
523         }
524     }
525
526     /**
527      * Updates the titlebar with an icon for the provider type.
528      */
529     @Override
530     protected void onAuthDescriptionsUpdated() {
531         super.onAuthDescriptionsUpdated();
532         getPreferenceScreen().removeAll();
533         if (mAccount != null) {
534             mProviderIcon.setImageDrawable(getDrawableForType(mAccount.type));
535             mProviderId.setText(getLabelForType(mAccount.type));
536         }
537         addPreferencesFromResource(R.xml.account_sync_settings);
538     }
539
540     @Override
541     protected int getHelpResource() {
542         return R.string.help_url_accounts;
543     }
544 }