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