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 static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_PERSONAL_ACCOUNT_TITLE;
20 import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_WORK_ACCOUNT_TITLE;
21 
22 import android.accounts.Account;
23 import android.accounts.AccountManager;
24 import android.app.Activity;
25 import android.app.Dialog;
26 import android.app.admin.DevicePolicyManager;
27 import android.app.settings.SettingsEnums;
28 import android.content.ContentResolver;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.IntentSender;
32 import android.content.SyncAdapterType;
33 import android.content.SyncInfo;
34 import android.content.SyncStatusInfo;
35 import android.content.pm.PackageManager;
36 import android.content.pm.ProviderInfo;
37 import android.content.pm.UserInfo;
38 import android.os.Binder;
39 import android.os.Bundle;
40 import android.os.UserHandle;
41 import android.os.UserManager;
42 import android.text.TextUtils;
43 import android.text.format.DateUtils;
44 import android.util.Log;
45 import android.view.Menu;
46 import android.view.MenuInflater;
47 import android.view.MenuItem;
48 
49 import androidx.annotation.VisibleForTesting;
50 import androidx.appcompat.app.AlertDialog;
51 import androidx.preference.Preference;
52 
53 import com.android.settings.R;
54 import com.android.settings.Utils;
55 import com.android.settings.widget.EntityHeaderController;
56 import com.android.settingslib.widget.FooterPreference;
57 
58 import com.google.android.collect.Lists;
59 
60 import java.util.ArrayList;
61 import java.util.Date;
62 import java.util.HashMap;
63 import java.util.List;
64 
65 public class AccountSyncSettings extends AccountPreferenceBase {
66 
67     public static final String ACCOUNT_KEY = "account";
68     private static final int MENU_SYNC_NOW_ID = Menu.FIRST;
69     private static final int MENU_SYNC_CANCEL_ID = Menu.FIRST + 1;
70     private static final int CANT_DO_ONETIME_SYNC_DIALOG = 102;
71     private static final String UID_REQUEST_KEY = "uid_request_code";
72 
73     private Account mAccount;
74     private ArrayList<SyncAdapterType> mInvisibleAdapters = Lists.newArrayList();
75     private HashMap<Integer, Integer> mUidRequestCodeMap = new HashMap<>();
76 
77     @Override
onCreateDialog(final int id)78     public Dialog onCreateDialog(final int id) {
79         Dialog dialog = null;
80         if (id == CANT_DO_ONETIME_SYNC_DIALOG) {
81             dialog = new AlertDialog.Builder(getActivity())
82                     .setTitle(R.string.cant_sync_dialog_title)
83                     .setMessage(R.string.cant_sync_dialog_message)
84                     .setPositiveButton(android.R.string.ok, null)
85                     .create();
86         }
87         return dialog;
88     }
89 
90     @Override
getMetricsCategory()91     public int getMetricsCategory() {
92         return SettingsEnums.ACCOUNTS_ACCOUNT_SYNC;
93     }
94 
95     @Override
getDialogMetricsCategory(int dialogId)96     public int getDialogMetricsCategory(int dialogId) {
97         switch (dialogId) {
98             case CANT_DO_ONETIME_SYNC_DIALOG:
99                 return SettingsEnums.DIALOG_ACCOUNT_SYNC_CANNOT_ONETIME_SYNC;
100             default:
101                 return 0;
102         }
103     }
104 
105     @Override
onCreate(Bundle icicle)106     public void onCreate(Bundle icicle) {
107         super.onCreate(icicle);
108         addPreferencesFromResource(R.xml.account_sync_settings);
109         getPreferenceScreen().setOrderingAsAdded(false);
110         setAccessibilityTitle();
111     }
112 
113     @Override
onSaveInstanceState(Bundle outState)114     public void onSaveInstanceState(Bundle outState) {
115         super.onSaveInstanceState(outState);
116         if (!mUidRequestCodeMap.isEmpty()) {
117             outState.putSerializable(UID_REQUEST_KEY, mUidRequestCodeMap);
118         }
119     }
120 
121     @Override
onActivityCreated(Bundle savedInstanceState)122     public void onActivityCreated(Bundle savedInstanceState) {
123         super.onActivityCreated(savedInstanceState);
124 
125         Bundle arguments = getArguments();
126         if (arguments == null) {
127             Log.e(TAG, "No arguments provided when starting intent. ACCOUNT_KEY needed.");
128             finish();
129             return;
130         }
131         mAccount = arguments.getParcelable(ACCOUNT_KEY);
132         if (!accountExists(mAccount)) {
133             Log.e(TAG, "Account provided does not exist: " + mAccount);
134             finish();
135             return;
136         }
137         if (Log.isLoggable(TAG, Log.VERBOSE)) {
138             Log.v(TAG, "Got account: " + mAccount);
139         }
140         final Activity activity = getActivity();
141         final Preference pref = EntityHeaderController
142                 .newInstance(activity, this, null /* header */)
143                 .setIcon(getDrawableForType(mAccount.type))
144                 .setLabel(mAccount.name)
145                 .setSummary(getLabelForType(mAccount.type))
146                 .done(getPrefContext());
147         pref.setOrder(0);
148         getPreferenceScreen().addPreference(pref);
149         if (savedInstanceState != null && savedInstanceState.containsKey(UID_REQUEST_KEY)) {
150             mUidRequestCodeMap = (HashMap<Integer, Integer>) savedInstanceState.getSerializable(
151                     UID_REQUEST_KEY);
152         }
153     }
154 
setAccessibilityTitle()155     private void setAccessibilityTitle() {
156         final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
157         UserInfo user = um.getUserInfo(mUserHandle.getIdentifier());
158         boolean isWorkProfile = user != null ? user.isManagedProfile() : false;
159         CharSequence currentTitle = getActivity().getTitle();
160 
161         DevicePolicyManager devicePolicyManager = getSystemService(DevicePolicyManager.class);
162 
163         String accessibilityTitle =
164                 isWorkProfile
165                         ? devicePolicyManager.getResources().getString(
166                                 ACCESSIBILITY_WORK_ACCOUNT_TITLE,
167                                 () -> getString(R.string.accessibility_work_account_title,
168                                         currentTitle), currentTitle)
169                                 : devicePolicyManager.getResources().getString(
170                                         ACCESSIBILITY_PERSONAL_ACCOUNT_TITLE,
171                                         () -> getString(
172                                                 R.string.accessibility_personal_account_title,
173                                                 currentTitle), currentTitle);
174 
175         getActivity().setTitle(Utils.createAccessibleSequence(currentTitle, accessibilityTitle));
176     }
177 
178     @Override
onResume()179     public void onResume() {
180         mAuthenticatorHelper.listenToAccountUpdates();
181         updateAuthDescriptions();
182         onAccountsUpdate(Binder.getCallingUserHandle());
183         super.onResume();
184     }
185 
186     @Override
onPause()187     public void onPause() {
188         super.onPause();
189         mAuthenticatorHelper.stopListeningToAccountUpdates();
190     }
191 
addSyncStateSwitch(Account account, String authority, String packageName, int uid)192     private void addSyncStateSwitch(Account account, String authority,
193             String packageName, int uid) {
194         SyncStateSwitchPreference item = (SyncStateSwitchPreference) getCachedPreference(authority);
195         if (item == null) {
196             item = new SyncStateSwitchPreference(getPrefContext(), account, authority,
197                     packageName, uid);
198             getPreferenceScreen().addPreference(item);
199         } else {
200             item.setup(account, authority, packageName, uid);
201         }
202         final PackageManager packageManager = getPackageManager();
203         item.setPersistent(false);
204         final ProviderInfo providerInfo = packageManager.resolveContentProviderAsUser(
205                 authority, 0, mUserHandle.getIdentifier());
206         if (providerInfo == null) {
207             return;
208         }
209         final CharSequence providerLabel = providerInfo.loadLabel(packageManager);
210         if (TextUtils.isEmpty(providerLabel)) {
211             Log.e(TAG, "Provider needs a label for authority '" + authority + "'");
212             return;
213         }
214         item.setTitle(providerLabel);
215         item.setKey(authority);
216     }
217 
218     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)219     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
220         MenuItem syncNow = menu.add(0, MENU_SYNC_NOW_ID, 0,
221                 getString(R.string.sync_menu_sync_now));
222         MenuItem syncCancel = menu.add(0, MENU_SYNC_CANCEL_ID, 0,
223                 getString(R.string.sync_menu_sync_cancel));
224 
225         syncNow.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER |
226                 MenuItem.SHOW_AS_ACTION_WITH_TEXT);
227         syncCancel.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER |
228                 MenuItem.SHOW_AS_ACTION_WITH_TEXT);
229 
230         super.onCreateOptionsMenu(menu, inflater);
231     }
232 
233     @Override
onPrepareOptionsMenu(Menu menu)234     public void onPrepareOptionsMenu(Menu menu) {
235         super.onPrepareOptionsMenu(menu);
236         // Note that this also counts accounts that are not currently displayed
237         boolean syncActive = !ContentResolver.getCurrentSyncsAsUser(
238                 mUserHandle.getIdentifier()).isEmpty();
239         menu.findItem(MENU_SYNC_NOW_ID).setVisible(!syncActive).setEnabled(enabledSyncNowMenu());
240         menu.findItem(MENU_SYNC_CANCEL_ID).setVisible(syncActive);
241     }
242 
243     @Override
onOptionsItemSelected(MenuItem item)244     public boolean onOptionsItemSelected(MenuItem item) {
245         switch (item.getItemId()) {
246             case MENU_SYNC_NOW_ID:
247                 startSyncForEnabledProviders();
248                 return true;
249             case MENU_SYNC_CANCEL_ID:
250                 cancelSyncForEnabledProviders();
251                 return true;
252         }
253         return super.onOptionsItemSelected(item);
254     }
255 
256     @Override
onActivityResult(int requestCode, int resultCode, Intent data)257     public void onActivityResult(int requestCode, int resultCode, Intent data) {
258         if (resultCode == Activity.RESULT_OK) {
259             final int count = getPreferenceScreen().getPreferenceCount();
260             for (int i = 0; i < count; i++) {
261                 Preference preference = getPreferenceScreen().getPreference(i);
262                 if (preference instanceof SyncStateSwitchPreference) {
263                     SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) preference;
264                     if (getRequestCodeByUid(syncPref.getUid()) == requestCode) {
265                         onPreferenceTreeClick(syncPref);
266                         return;
267                     }
268                 }
269             }
270         }
271     }
272 
273     @Override
onPreferenceTreeClick(Preference preference)274     public boolean onPreferenceTreeClick(Preference preference) {
275         if (getActivity() == null) {
276             return false;
277         }
278         if (preference instanceof SyncStateSwitchPreference) {
279             SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) preference;
280             final String authority = syncPref.getAuthority();
281             if (TextUtils.isEmpty(authority)) {
282                 return false;
283             }
284             final Account account = syncPref.getAccount();
285             final int userId = mUserHandle.getIdentifier();
286             final String packageName = syncPref.getPackageName();
287 
288             boolean syncAutomatically = ContentResolver.getSyncAutomaticallyAsUser(account,
289                     authority, userId);
290             if (syncPref.isOneTimeSyncMode()) {
291                 // If the sync adapter doesn't have access to the account we either
292                 // request access by starting an activity if possible or kick off the
293                 // sync which will end up posting an access request notification.
294                 if (requestAccountAccessIfNeeded(packageName)) {
295                     return true;
296                 }
297                 requestOrCancelSync(account, authority, true);
298             } else {
299                 boolean syncOn = syncPref.isChecked();
300                 boolean oldSyncState = syncAutomatically;
301                 if (syncOn != oldSyncState) {
302                     // Toggling this switch triggers sync but we may need a user approval.
303                     // If the sync adapter doesn't have access to the account we either
304                     // request access by starting an activity if possible or kick off the
305                     // sync which will end up posting an access request notification.
306                     if (syncOn && requestAccountAccessIfNeeded(packageName)) {
307                         return true;
308                     }
309                     // if we're enabling sync, this will request a sync as well
310                     ContentResolver.setSyncAutomaticallyAsUser(account, authority, syncOn, userId);
311                     // if the primary sync switch is off, the request above will
312                     // get dropped.  when the user clicks on this toggle,
313                     // we want to force the sync, however.
314                     if (!ContentResolver.getMasterSyncAutomaticallyAsUser(userId) || !syncOn) {
315                         requestOrCancelSync(account, authority, syncOn);
316                     }
317                 }
318             }
319             return true;
320         } else {
321             return super.onPreferenceTreeClick(preference);
322         }
323     }
324 
requestAccountAccessIfNeeded(String packageName)325     private boolean requestAccountAccessIfNeeded(String packageName) {
326         if (packageName == null) {
327             return false;
328         }
329 
330         final int uid;
331         try {
332             uid = getContext().getPackageManager().getPackageUidAsUser(
333                     packageName, mUserHandle.getIdentifier());
334         } catch (PackageManager.NameNotFoundException e) {
335             Log.e(TAG, "Invalid sync ", e);
336             return false;
337         }
338 
339         AccountManager accountManager = getContext().getSystemService(AccountManager.class);
340         if (!accountManager.hasAccountAccess(mAccount, packageName, mUserHandle)) {
341             IntentSender intent = accountManager.createRequestAccountAccessIntentSenderAsUser(
342                     mAccount, packageName, mUserHandle);
343             if (intent != null) {
344                 try {
345                     final int requestCode = addUidAndGenerateRequestCode(uid);
346                     startIntentSenderForResult(intent, requestCode, null /* fillInIntent */, 0, 0,
347                             0, null /* options */);
348                     return true;
349                 } catch (IntentSender.SendIntentException e) {
350                     Log.e(TAG, "Error requesting account access", e);
351                 }
352             }
353         }
354         return false;
355     }
356 
startSyncForEnabledProviders()357     private void startSyncForEnabledProviders() {
358         requestOrCancelSyncForEnabledProviders(true /* start them */);
359         final Activity activity = getActivity();
360         if (activity != null) {
361             activity.invalidateOptionsMenu();
362         }
363     }
364 
cancelSyncForEnabledProviders()365     private void cancelSyncForEnabledProviders() {
366         requestOrCancelSyncForEnabledProviders(false /* cancel them */);
367         final Activity activity = getActivity();
368         if (activity != null) {
369             activity.invalidateOptionsMenu();
370         }
371     }
372 
requestOrCancelSyncForEnabledProviders(boolean startSync)373     private void requestOrCancelSyncForEnabledProviders(boolean startSync) {
374         // sync everything that the user has enabled
375         int count = getPreferenceScreen().getPreferenceCount();
376         for (int i = 0; i < count; i++) {
377             Preference pref = getPreferenceScreen().getPreference(i);
378             if (!(pref instanceof SyncStateSwitchPreference)) {
379                 continue;
380             }
381             SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) pref;
382             if (!syncPref.isChecked()) {
383                 continue;
384             }
385             requestOrCancelSync(syncPref.getAccount(), syncPref.getAuthority(), startSync);
386         }
387         // plus whatever the system needs to sync, e.g., invisible sync adapters
388         if (mAccount != null) {
389             for (SyncAdapterType syncAdapter : mInvisibleAdapters) {
390                 requestOrCancelSync(mAccount, syncAdapter.authority, startSync);
391             }
392         }
393     }
394 
requestOrCancelSync(Account account, String authority, boolean flag)395     private void requestOrCancelSync(Account account, String authority, boolean flag) {
396         if (flag) {
397             Bundle extras = new Bundle();
398             extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
399             ContentResolver.requestSyncAsUser(account, authority, mUserHandle.getIdentifier(),
400                     extras);
401         } else {
402             ContentResolver.cancelSyncAsUser(account, authority, mUserHandle.getIdentifier());
403         }
404     }
405 
isSyncing(List<SyncInfo> currentSyncs, Account account, String authority)406     private boolean isSyncing(List<SyncInfo> currentSyncs, Account account, String authority) {
407         for (SyncInfo syncInfo : currentSyncs) {
408             if (syncInfo.account.equals(account) && syncInfo.authority.equals(authority)) {
409                 return true;
410             }
411         }
412         return false;
413     }
414 
415     @Override
onSyncStateUpdated()416     protected void onSyncStateUpdated() {
417         if (!isResumed()) return;
418         setFeedsState();
419         final Activity activity = getActivity();
420         if (activity != null) {
421             activity.invalidateOptionsMenu();
422         }
423     }
424 
setFeedsState()425     private void setFeedsState() {
426         // iterate over all the preferences, setting the state properly for each
427         Date date = new Date();
428         final int userId = mUserHandle.getIdentifier();
429         List<SyncInfo> currentSyncs = ContentResolver.getCurrentSyncsAsUser(userId);
430         boolean syncIsFailing = false;
431 
432         // Refresh the sync status switches - some syncs may have become active.
433         updateAccountSwitches();
434 
435         for (int i = 0, count = getPreferenceScreen().getPreferenceCount(); i < count; i++) {
436             Preference pref = getPreferenceScreen().getPreference(i);
437             if (!(pref instanceof SyncStateSwitchPreference)) {
438                 continue;
439             }
440             SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) pref;
441 
442             String authority = syncPref.getAuthority();
443             Account account = syncPref.getAccount();
444 
445             SyncStatusInfo status = ContentResolver.getSyncStatusAsUser(account, authority, userId);
446             boolean syncEnabled = ContentResolver.getSyncAutomaticallyAsUser(account, authority,
447                     userId);
448             boolean authorityIsPending = status == null ? false : status.pending;
449             boolean initialSync = status == null ? false : status.initialize;
450 
451             boolean activelySyncing = isSyncing(currentSyncs, account, authority);
452             boolean lastSyncFailed = status != null
453                     && status.lastFailureTime != 0
454                     && status.getLastFailureMesgAsInt(0)
455                     != ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS;
456             if (!syncEnabled) lastSyncFailed = false;
457             if (lastSyncFailed && !activelySyncing && !authorityIsPending) {
458                 syncIsFailing = true;
459             }
460             if (Log.isLoggable(TAG, Log.DEBUG)) {
461                 Log.d(TAG, "Update sync status: " + account + " " + authority +
462                         " active = " + activelySyncing + " pend =" + authorityIsPending);
463             }
464 
465             final long successEndTime = (status == null) ? 0 : status.lastSuccessTime;
466             if (!syncEnabled) {
467                 syncPref.setSummary(R.string.sync_disabled);
468             } else if (activelySyncing) {
469                 syncPref.setSummary(R.string.sync_in_progress);
470             } else if (successEndTime != 0) {
471                 date.setTime(successEndTime);
472                 final String timeString = formatSyncDate(getContext(), date);
473                 syncPref.setSummary(getResources().getString(R.string.last_synced, timeString));
474             } else {
475                 syncPref.setSummary("");
476             }
477             int syncState = ContentResolver.getIsSyncableAsUser(account, authority, userId);
478 
479             syncPref.setActive(activelySyncing && (syncState >= 0) &&
480                     !initialSync);
481             syncPref.setPending(authorityIsPending && (syncState >= 0) &&
482                     !initialSync);
483 
484             syncPref.setFailed(lastSyncFailed);
485             final boolean oneTimeSyncMode = !ContentResolver.getMasterSyncAutomaticallyAsUser(
486                     userId);
487             syncPref.setOneTimeSyncMode(oneTimeSyncMode);
488             syncPref.setChecked(oneTimeSyncMode || syncEnabled);
489         }
490         if (syncIsFailing) {
491             getPreferenceScreen().addPreference(new FooterPreference.Builder(
492                     getActivity()).setTitle(R.string.sync_is_failing).build());
493         }
494     }
495 
496     @Override
onAccountsUpdate(final UserHandle userHandle)497     public void onAccountsUpdate(final UserHandle userHandle) {
498         super.onAccountsUpdate(userHandle);
499         if (!accountExists(mAccount)) {
500             // The account was deleted
501             finish();
502             return;
503         }
504         updateAccountSwitches();
505         onSyncStateUpdated();
506     }
507 
accountExists(Account account)508     private boolean accountExists(Account account) {
509         if (account == null) return false;
510 
511         Account[] accounts = AccountManager.get(getActivity()).getAccountsByTypeAsUser(
512                 account.type, mUserHandle);
513         final int count = accounts.length;
514         for (int i = 0; i < count; i++) {
515             if (accounts[i].equals(account)) {
516                 return true;
517             }
518         }
519         return false;
520     }
521 
updateAccountSwitches()522     private void updateAccountSwitches() {
523         mInvisibleAdapters.clear();
524 
525         SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypesAsUser(
526                 mUserHandle.getIdentifier());
527         ArrayList<SyncAdapterType> authorities = new ArrayList<>();
528         for (int i = 0, n = syncAdapters.length; i < n; i++) {
529             final SyncAdapterType sa = syncAdapters[i];
530             // Only keep track of sync adapters for this account
531             if (!sa.accountType.equals(mAccount.type)) continue;
532             if (sa.isUserVisible()) {
533                 if (Log.isLoggable(TAG, Log.DEBUG)) {
534                     Log.d(TAG, "updateAccountSwitches: added authority " + sa.authority
535                             + " to accountType " + sa.accountType);
536                 }
537                 authorities.add(sa);
538             } else {
539                 // keep track of invisible sync adapters, so sync now forces
540                 // them to sync as well.
541                 mInvisibleAdapters.add(sa);
542             }
543         }
544 
545         if (Log.isLoggable(TAG, Log.DEBUG)) {
546             Log.d(TAG, "looking for sync adapters that match account " + mAccount);
547         }
548 
549         cacheRemoveAllPrefs(getPreferenceScreen());
550         getCachedPreference(EntityHeaderController.PREF_KEY_APP_HEADER);
551         for (int j = 0, m = authorities.size(); j < m; j++) {
552             final SyncAdapterType syncAdapter = authorities.get(j);
553             // We could check services here....
554             int syncState = ContentResolver.getIsSyncableAsUser(mAccount, syncAdapter.authority,
555                     mUserHandle.getIdentifier());
556             if (Log.isLoggable(TAG, Log.DEBUG)) {
557                 Log.d(TAG, "  found authority " + syncAdapter.authority + " " + syncState);
558             }
559             if (syncState > 0) {
560                 final int uid;
561                 try {
562                     uid = getContext().getPackageManager().getPackageUidAsUser(
563                             syncAdapter.getPackageName(), mUserHandle.getIdentifier());
564                     addSyncStateSwitch(mAccount, syncAdapter.authority,
565                             syncAdapter.getPackageName(), uid);
566                 } catch (PackageManager.NameNotFoundException e) {
567                     Log.e(TAG, "No uid for package" + syncAdapter.getPackageName(), e);
568                 }
569             }
570         }
571         removeCachedPrefs(getPreferenceScreen());
572     }
573 
574     @Override
getHelpResource()575     public int getHelpResource() {
576         return R.string.help_url_accounts;
577     }
578 
579     @VisibleForTesting
enabledSyncNowMenu()580     boolean enabledSyncNowMenu() {
581         boolean enabled = false;
582         for (int i = 0, count = getPreferenceScreen().getPreferenceCount(); i < count; i++) {
583             final Preference pref = getPreferenceScreen().getPreference(i);
584             if (!(pref instanceof SyncStateSwitchPreference)) {
585                 continue;
586             }
587             final SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) pref;
588             if (syncPref.isChecked()) {
589                 enabled = true;
590                 break;
591             }
592         }
593         return enabled;
594     }
595 
formatSyncDate(Context context, Date date)596     private static String formatSyncDate(Context context, Date date) {
597         return DateUtils.formatDateTime(context, date.getTime(),
598                 DateUtils.FORMAT_SHOW_DATE
599                         | DateUtils.FORMAT_SHOW_YEAR
600                         | DateUtils.FORMAT_SHOW_TIME);
601     }
602 
addUidAndGenerateRequestCode(int uid)603     private int addUidAndGenerateRequestCode(int uid) {
604         if (mUidRequestCodeMap.containsKey(uid)) {
605             return mUidRequestCodeMap.get(uid);
606         }
607         final int requestCode = mUidRequestCodeMap.size() + 1;
608         mUidRequestCodeMap.put(uid, requestCode);
609         return requestCode;
610     }
611 
getRequestCodeByUid(int uid)612     private int getRequestCodeByUid(int uid) {
613         if (!mUidRequestCodeMap.containsKey(uid)) {
614             return -1;
615         }
616         return mUidRequestCodeMap.get(uid);
617     }
618 }
619