// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.signin;
import android.accounts.Account;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.os.Build;
import android.os.Bundle;
import android.os.UserManager;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.text.TextUtils;
import android.util.Pair;
import org.chromium.base.ContextUtils;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeApplication;
import org.chromium.chrome.browser.childaccounts.ChildAccountService;
import org.chromium.chrome.browser.preferences.ChromeBasePreference;
import org.chromium.chrome.browser.preferences.ManagedPreferenceDelegate;
import org.chromium.chrome.browser.preferences.PrefServiceBridge;
import org.chromium.chrome.browser.preferences.Preferences;
import org.chromium.chrome.browser.preferences.PreferencesLauncher;
import org.chromium.chrome.browser.preferences.SyncPreference;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.profiles.ProfileAccountManagementMetrics;
import org.chromium.chrome.browser.profiles.ProfileDownloader;
import org.chromium.chrome.browser.signin.SignOutDialogFragment.SignOutDialogListener;
import org.chromium.chrome.browser.signin.SigninManager.SignInStateObserver;
import org.chromium.chrome.browser.sync.ProfileSyncService;
import org.chromium.chrome.browser.sync.ProfileSyncService.SyncStateChangedListener;
import org.chromium.chrome.browser.sync.ui.SyncCustomizationFragment;
import org.chromium.components.signin.AccountManagerHelper;
import org.chromium.components.signin.ChromeSigninController;
import java.util.ArrayList;
import java.util.HashMap;
/**
* The settings screen with information and settings related to the user's accounts.
*
* This shows which accounts the user is signed in with, allows the user to sign out of Chrome,
* links to sync settings, has links to add accounts and go incognito, and shows parental settings
* if a child account is in use.
*
* Note: This can be triggered from a web page, e.g. a GAIA sign-in page.
*/
public class AccountManagementFragment extends PreferenceFragment
implements SignOutDialogListener, ProfileDownloader.Observer,
SyncStateChangedListener, SignInStateObserver,
ConfirmManagedSyncDataDialog.Listener {
private static final String TAG = "AcctManagementPref";
public static final String SIGN_OUT_DIALOG_TAG = "sign_out_dialog_tag";
private static final String CLEAR_DATA_PROGRESS_DIALOG_TAG = "clear_data_progress";
/**
* The key for an integer value in
* {@link Preferences#EXTRA_SHOW_FRAGMENT_ARGUMENTS} bundle to
* specify the correct GAIA service that has triggered the dialog.
* If the argument is not set, GAIA_SERVICE_TYPE_NONE is used as the origin of the dialog.
*/
public static final String SHOW_GAIA_SERVICE_TYPE_EXTRA = "ShowGAIAServiceType";
/**
* Account name preferences will be ordered sequentially, starting with this "order" value.
* This ensures that the account name preferences appear in the correct location in the
* preference fragment. See account_management_preferences.xml for details.
*/
private static final int FIRST_ACCOUNT_PREF_ORDER = 100;
/**
* SharedPreference name for the preference that disables signing out of Chrome.
* Signing out is forever disabled once Chrome signs the user in automatically
* if the device has a child account or if the device is an Android EDU device.
*/
private static final String SIGN_OUT_ALLOWED = "auto_signed_in_school_account";
private static final HashMap<String, Pair<String, Bitmap>> sToNamePicture =
new HashMap<String, Pair<String, Bitmap>>();
private static String sChildAccountId = null;
private static Bitmap sCachedBadgedPicture = null;
public static final String PREF_SIGN_OUT = "sign_out";
public static final String PREF_ADD_ACCOUNT = "add_account";
public static final String PREF_PARENTAL_SETTINGS = "parental_settings";
public static final String PREF_PARENT_ACCOUNTS = "parent_accounts";
public static final String PREF_CHILD_CONTENT = "child_content";
public static final String PREF_CHILD_SAFE_SITES = "child_safe_sites";
public static final String PREF_GOOGLE_ACTIVITY_CONTROLS = "google_activity_controls";
public static final String PREF_SYNC_SETTINGS = "sync_settings";
private int mGaiaServiceType;
private ArrayList<Preference> mAccountsListPreferences = new ArrayList<Preference>();
@Override
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
// Prevent sync from starting if it hasn't already to give the user a chance to change
// their sync settings.
ProfileSyncService syncService = ProfileSyncService.get();
if (syncService != null) {
syncService.setSetupInProgress(true);
}
mGaiaServiceType = AccountManagementScreenHelper.GAIA_SERVICE_TYPE_NONE;
if (getArguments() != null) {
mGaiaServiceType =
getArguments().getInt(SHOW_GAIA_SERVICE_TYPE_EXTRA, mGaiaServiceType);
}
AccountManagementScreenHelper.logEvent(
ProfileAccountManagementMetrics.VIEW,
mGaiaServiceType);
startFetchingAccountsInformation(getActivity(), Profile.getLastUsedProfile());
}
@Override
public void onResume() {
super.onResume();
SigninManager.get(getActivity()).addSignInStateObserver(this);
ProfileDownloader.addObserver(this);
ProfileSyncService syncService = ProfileSyncService.get();
if (syncService != null) {
syncService.addSyncStateChangedListener(this);
}
update();
}
@Override
public void onPause() {
super.onPause();
SigninManager.get(getActivity()).removeSignInStateObserver(this);
ProfileDownloader.removeObserver(this);
ProfileSyncService syncService = ProfileSyncService.get();
if (syncService != null) {
syncService.removeSyncStateChangedListener(this);
}
}
@Override
public void onDestroy() {
super.onDestroy();
// Allow sync to begin syncing if it hasn't yet.
ProfileSyncService syncService = ProfileSyncService.get();
if (syncService != null) {
syncService.setSetupInProgress(false);
}
}
/**
* Initiate fetching the user accounts data (images and the full name).
* Fetched data will be sent to observers of ProfileDownloader.
*
* @param profile Profile to use.
*/
private static void startFetchingAccountsInformation(Context context, Profile profile) {
Account[] accounts = AccountManagerHelper.get(context).getGoogleAccounts();
for (int i = 0; i < accounts.length; i++) {
startFetchingAccountInformation(context, profile, accounts[i].name);
}
}
public void update() {
final Context context = getActivity();
if (context == null) return;
if (getPreferenceScreen() != null) getPreferenceScreen().removeAll();
ChromeSigninController signInController = ChromeSigninController.get(context);
if (!signInController.isSignedIn()) {
// The AccountManagementFragment can only be shown when the user is signed in. If the
// user is signed out, exit the fragment.
getActivity().finish();
return;
}
addPreferencesFromResource(R.xml.account_management_preferences);
String signedInAccountName =
ChromeSigninController.get(getActivity()).getSignedInAccountName();
String fullName = getCachedUserName(signedInAccountName);
if (TextUtils.isEmpty(fullName)) {
fullName = ProfileDownloader.getCachedFullName(Profile.getLastUsedProfile());
}
if (TextUtils.isEmpty(fullName)) fullName = signedInAccountName;
getActivity().setTitle(fullName);
configureSignOutSwitch();
configureAddAccountPreference();
configureChildAccountPreferences();
configureSyncSettings();
configureGoogleActivityControls();
updateAccountsList();
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private boolean canAddAccounts() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return true;
UserManager userManager = (UserManager) getActivity()
.getSystemService(Context.USER_SERVICE);
return !userManager.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS);
}
private void configureSignOutSwitch() {
boolean isChildAccount = ChildAccountService.isChildAccount();
Preference signOutSwitch = findPreference(PREF_SIGN_OUT);
if (isChildAccount) {
getPreferenceScreen().removePreference(signOutSwitch);
} else {
signOutSwitch.setEnabled(getSignOutAllowedPreferenceValue(getActivity()));
signOutSwitch.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
if (!isVisible() || !isResumed()) return false;
if (ChromeSigninController.get(getActivity()).isSignedIn()
&& getSignOutAllowedPreferenceValue(getActivity())) {
AccountManagementScreenHelper.logEvent(
ProfileAccountManagementMetrics.TOGGLE_SIGNOUT,
mGaiaServiceType);
String managementDomain =
SigninManager.get(getActivity()).getManagementDomain();
if (managementDomain != null) {
// Show the 'You are signing out of a managed account' dialog.
ConfirmManagedSyncDataDialog.showSignOutFromManagedAccountDialog(
AccountManagementFragment.this, getFragmentManager(),
getResources(), managementDomain);
} else {
// Show the 'You are signing out' dialog.
SignOutDialogFragment signOutFragment = new SignOutDialogFragment();
Bundle args = new Bundle();
args.putInt(SHOW_GAIA_SERVICE_TYPE_EXTRA, mGaiaServiceType);
signOutFragment.setArguments(args);
signOutFragment.setTargetFragment(AccountManagementFragment.this, 0);
signOutFragment.show(getFragmentManager(), SIGN_OUT_DIALOG_TAG);
}
return true;
}
return false;
}
});
}
}
private void configureSyncSettings() {
final Preferences preferences = (Preferences) getActivity();
final Account account = ChromeSigninController.get(getActivity()).getSignedInUser();
findPreference(PREF_SYNC_SETTINGS)
.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
if (!isVisible() || !isResumed()) return false;
if (ProfileSyncService.get() == null) return true;
Bundle args = new Bundle();
args.putString(SyncCustomizationFragment.ARGUMENT_ACCOUNT, account.name);
preferences.startFragment(SyncCustomizationFragment.class.getName(), args);
return true;
}
});
}
private void configureGoogleActivityControls() {
Preference pref = findPreference(PREF_GOOGLE_ACTIVITY_CONTROLS);
pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
Activity activity = getActivity();
((ChromeApplication) (activity.getApplicationContext()))
.createGoogleActivityController()
.openWebAndAppActivitySettings(activity,
ChromeSigninController.get(activity).getSignedInAccountName());
RecordUserAction.record("Signin_AccountSettings_GoogleActivityControlsClicked");
return true;
}
});
}
private void configureAddAccountPreference() {
ChromeBasePreference addAccount = (ChromeBasePreference) findPreference(PREF_ADD_ACCOUNT);
if (ChildAccountService.isChildAccount()) {
getPreferenceScreen().removePreference(addAccount);
} else {
addAccount.setTitle(getResources().getString(
R.string.account_management_add_account_title));
addAccount.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
if (!isVisible() || !isResumed()) return false;
AccountManagementScreenHelper.logEvent(
ProfileAccountManagementMetrics.ADD_ACCOUNT,
mGaiaServiceType);
AccountAdder.getInstance().addAccount(
getActivity(), AccountAdder.ADD_ACCOUNT_RESULT);
// Return to the last opened tab if triggered from the content area.
if (mGaiaServiceType != AccountManagementScreenHelper.GAIA_SERVICE_TYPE_NONE) {
if (isAdded()) getActivity().finish();
}
return true;
}
});
addAccount.setManagedPreferenceDelegate(new ManagedPreferenceDelegate() {
@Override
public boolean isPreferenceControlledByPolicy(Preference preference) {
return !canAddAccounts();
}
});
}
}
private void configureChildAccountPreferences() {
Preference parentAccounts = findPreference(PREF_PARENT_ACCOUNTS);
Preference childContent = findPreference(PREF_CHILD_CONTENT);
Preference childSafeSites = findPreference(PREF_CHILD_SAFE_SITES);
if (ChildAccountService.isChildAccount()) {
Resources res = getActivity().getResources();
PrefServiceBridge prefService = PrefServiceBridge.getInstance();
String firstParent = prefService.getSupervisedUserCustodianEmail();
String secondParent = prefService.getSupervisedUserSecondCustodianEmail();
String parentText;
if (!secondParent.isEmpty()) {
parentText = res.getString(R.string.account_management_two_parent_names,
firstParent, secondParent);
} else if (!firstParent.isEmpty()) {
parentText = res.getString(R.string.account_management_one_parent_name,
firstParent);
} else {
parentText = res.getString(R.string.account_management_no_parental_data);
}
parentAccounts.setSummary(parentText);
parentAccounts.setSelectable(false);
final boolean unapprovedContentBlocked =
prefService.getDefaultSupervisedUserFilteringBehavior()
== PrefServiceBridge.SUPERVISED_USER_FILTERING_BLOCK;
final String contentText = res.getString(
unapprovedContentBlocked ? R.string.account_management_child_content_approved
: R.string.account_management_child_content_all);
childContent.setSummary(contentText);
childContent.setSelectable(false);
final String safeSitesText = res.getString(
prefService.isSupervisedUserSafeSitesEnabled()
? R.string.text_on : R.string.text_off);
childSafeSites.setSummary(safeSitesText);
childSafeSites.setSelectable(false);
} else {
PreferenceScreen prefScreen = getPreferenceScreen();
prefScreen.removePreference(findPreference(PREF_PARENTAL_SETTINGS));
prefScreen.removePreference(parentAccounts);
prefScreen.removePreference(childContent);
prefScreen.removePreference(childSafeSites);
}
}
private void updateAccountsList() {
PreferenceScreen prefScreen = getPreferenceScreen();
if (prefScreen == null) return;
for (int i = 0; i < mAccountsListPreferences.size(); i++) {
prefScreen.removePreference(mAccountsListPreferences.get(i));
}
mAccountsListPreferences.clear();
final Preferences activity = (Preferences) getActivity();
Account[] accounts = AccountManagerHelper.get(activity).getGoogleAccounts();
int nextPrefOrder = FIRST_ACCOUNT_PREF_ORDER;
for (Account account : accounts) {
ChromeBasePreference pref = new ChromeBasePreference(activity);
pref.setSelectable(false);
pref.setTitle(account.name);
boolean isChildAccount = ChildAccountService.isChildAccount();
pref.setIcon(new BitmapDrawable(getResources(),
isChildAccount ? getBadgedUserPicture(account.name, getResources()) :
getUserPicture(account.name, getResources())));
pref.setOrder(nextPrefOrder++);
prefScreen.addPreference(pref);
mAccountsListPreferences.add(pref);
}
}
// ProfileDownloader.Observer implementation:
@Override
public void onProfileDownloaded(String accountId, String fullName, String givenName,
Bitmap bitmap) {
updateUserNamePictureCache(accountId, fullName, bitmap);
updateAccountsList();
}
// SignOutDialogListener implementation:
/**
* This class must be public and static. Otherwise an exception will be thrown when Android
* recreates the fragment (e.g. after a configuration change).
*/
public static class ClearDataProgressDialog extends DialogFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Don't allow the dialog to be recreated by Android, since it wouldn't ever be
// dismissed after recreation.
if (savedInstanceState != null) dismiss();
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
setCancelable(false);
ProgressDialog dialog = new ProgressDialog(getActivity());
dialog.setTitle(getString(R.string.wiping_profile_data_title));
dialog.setMessage(getString(R.string.wiping_profile_data_message));
dialog.setIndeterminate(true);
return dialog;
}
}
@Override
public void onSignOutClicked() {
// In case the user reached this fragment without being signed in, we guard the sign out so
// we do not hit a native crash.
if (!ChromeSigninController.get(getActivity()).isSignedIn()) return;
final Activity activity = getActivity();
final DialogFragment clearDataProgressDialog = new ClearDataProgressDialog();
SigninManager.get(activity).signOut(null, new SigninManager.WipeDataHooks() {
@Override
public void preWipeData() {
clearDataProgressDialog.show(
activity.getFragmentManager(), CLEAR_DATA_PROGRESS_DIALOG_TAG);
}
@Override
public void postWipeData() {
if (clearDataProgressDialog.isAdded()) {
clearDataProgressDialog.dismissAllowingStateLoss();
}
}
});
AccountManagementScreenHelper.logEvent(
ProfileAccountManagementMetrics.SIGNOUT_SIGNOUT,
mGaiaServiceType);
}
@Override
public void onSignOutDialogDismissed(boolean signOutClicked) {
if (!signOutClicked) {
AccountManagementScreenHelper.logEvent(
ProfileAccountManagementMetrics.SIGNOUT_CANCEL,
mGaiaServiceType);
}
}
// ConfirmManagedSyncDataDialog.Listener implementation
@Override
public void onConfirm() {
onSignOutClicked();
}
@Override
public void onCancel() {
onSignOutDialogDismissed(false);
}
// ProfileSyncServiceListener implementation:
@Override
public void syncStateChanged() {
SyncPreference pref = (SyncPreference) findPreference(PREF_SYNC_SETTINGS);
if (pref != null) {
pref.updateSyncSummaryAndIcon();
}
// TODO(crbug/557784): Show notification for sync error
}
// SignInStateObserver implementation:
@Override
public void onSignedIn() {
update();
}
@Override
public void onSignedOut() {
update();
}
/**
* Open the account management UI.
* @param applicationContext An application context.
* @param profile A user profile.
* @param serviceType A signin::GAIAServiceType that triggered the dialog.
*/
public static void openAccountManagementScreen(
Context applicationContext, Profile profile, int serviceType) {
Intent intent = PreferencesLauncher.createIntentForSettingsPage(applicationContext,
AccountManagementFragment.class.getName());
Bundle arguments = new Bundle();
arguments.putInt(SHOW_GAIA_SERVICE_TYPE_EXTRA, serviceType);
intent.putExtra(Preferences.EXTRA_SHOW_FRAGMENT_ARGUMENTS, arguments);
applicationContext.startActivity(intent);
}
/**
* Converts a square user picture to a round user picture.
* @param bitmap A bitmap to convert.
* @return A rounded picture bitmap.
*/
public static Bitmap makeRoundUserPicture(Bitmap bitmap) {
if (bitmap == null) return null;
Bitmap output = Bitmap.createBitmap(
bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
canvas.drawARGB(0, 0, 0, 0);
paint.setAntiAlias(true);
paint.setColor(0xFFFFFFFF);
canvas.drawCircle(bitmap.getWidth() * 0.5f, bitmap.getHeight() * 0.5f,
bitmap.getWidth() * 0.5f, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}
/**
* Creates a new image with the picture overlaid by the badge.
* @param userPicture A bitmap to overlay on.
* @param badge A bitmap to overlay with.
* @return A bitmap with the badge overlaying the {@code userPicture}.
*/
private static Bitmap overlayChildBadgeOnUserPicture(
Bitmap userPicture, Bitmap badge, Resources resources) {
assert userPicture.getWidth() == resources.getDimensionPixelSize(R.dimen.user_picture_size);
int borderSize = resources.getDimensionPixelOffset(R.dimen.badge_border_size);
int badgeRadius = resources.getDimensionPixelOffset(R.dimen.badge_radius);
// Create a larger image to accommodate the badge which spills the original picture.
int badgedPictureWidth =
resources.getDimensionPixelOffset(R.dimen.badged_user_picture_width);
int badgedPictureHeight =
resources.getDimensionPixelOffset(R.dimen.badged_user_picture_height);
Bitmap badgedPicture = Bitmap.createBitmap(badgedPictureWidth, badgedPictureHeight,
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(badgedPicture);
canvas.drawBitmap(userPicture, 0, 0, null);
// Cut a transparent hole through the background image.
// This will serve as a border to the badge being overlaid.
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
int badgeCenterX = badgedPictureWidth - badgeRadius;
int badgeCenterY = badgedPictureHeight - badgeRadius;
canvas.drawCircle(badgeCenterX, badgeCenterY, badgeRadius + borderSize, paint);
// Draw the badge
canvas.drawBitmap(badge, badgeCenterX - badgeRadius, badgeCenterY - badgeRadius, null);
return badgedPicture;
}
/**
* Updates the user name and picture in the cache.
* @param accountId User's account id.
* @param fullName User name.
* @param bitmap User picture.
*/
public static void updateUserNamePictureCache(
String accountId, String fullName, Bitmap bitmap) {
sChildAccountId = null;
sCachedBadgedPicture = null;
sToNamePicture.put(accountId,
new Pair<String, Bitmap>(fullName, makeRoundUserPicture(bitmap)));
}
/**
* @param accountId An account.
* @return A cached user name for a given account.
*/
public static String getCachedUserName(String accountId) {
Pair<String, Bitmap> pair = sToNamePicture.get(accountId);
return pair != null ? pair.first : null;
}
/**
* Gets the user picture for the account from the cache, or returns the default picture if
* unavailable.
*
* @param accountId A child account.
* @return A user picture with badge for a given child account.
*/
public static Bitmap getBadgedUserPicture(String accountId, Resources res) {
if (sChildAccountId != null) {
assert TextUtils.equals(accountId, sChildAccountId);
return sCachedBadgedPicture;
}
sChildAccountId = accountId;
Bitmap picture = getUserPicture(accountId, res);
Bitmap badge = BitmapFactory.decodeResource(res, R.drawable.ic_account_child);
sCachedBadgedPicture = overlayChildBadgeOnUserPicture(picture, badge, res);
return sCachedBadgedPicture;
}
/**
* Gets the user picture for the account from the cache, or returns the default picture if
* unavailable.
*
* @param accountId An account.
* @param resources The collection containing the application resources.
* @return A user picture for a given account.
*/
public static Bitmap getUserPicture(String accountId, Resources resources) {
Pair<String, Bitmap> pair = sToNamePicture.get(accountId);
return pair != null ? pair.second : BitmapFactory.decodeResource(resources,
R.drawable.account_management_no_picture);
}
/**
* Initiate fetching of an image and a picture of a given account. Fetched data will be sent to
* observers of ProfileDownloader.
*
* @param context A context.
* @param profile A profile.
* @param accountName An account name.
*/
public static void startFetchingAccountInformation(
Context context, Profile profile, String accountName) {
if (TextUtils.isEmpty(accountName)) return;
if (sToNamePicture.get(accountName) != null) return;
final int imageSidePixels =
context.getResources().getDimensionPixelOffset(R.dimen.user_picture_size);
ProfileDownloader.startFetchingAccountInfoFor(
context, profile, accountName, imageSidePixels, false);
}
/**
* Prefetch the primary account image and name.
*
* @param context A context to use.
* @param profile A profile to use.
*/
public static void prefetchUserNamePicture(Context context, Profile profile) {
final String accountName = ChromeSigninController.get(context).getSignedInAccountName();
if (TextUtils.isEmpty(accountName)) return;
if (sToNamePicture.get(accountName) != null) return;
ProfileDownloader.addObserver(new ProfileDownloader.Observer() {
@Override
public void onProfileDownloaded(String accountId, String fullName, String givenName,
Bitmap bitmap) {
if (TextUtils.equals(accountName, accountId)) {
updateUserNamePictureCache(accountId, fullName, bitmap);
ProfileDownloader.removeObserver(this);
}
}
});
startFetchingAccountInformation(context, profile, accountName);
}
/**
* @param context A context
* @return Whether the sign out is not disabled due to a child/EDU account.
*/
private static boolean getSignOutAllowedPreferenceValue(Context context) {
return ContextUtils.getAppSharedPreferences()
.getBoolean(SIGN_OUT_ALLOWED, true);
}
/**
* Sets the sign out allowed preference value.
*
* @param context A context
* @param isAllowed True if the sign out is not disabled due to a child/EDU account
*/
public static void setSignOutAllowedPreferenceValue(Context context, boolean isAllowed) {
ContextUtils.getAppSharedPreferences()
.edit()
.putBoolean(SIGN_OUT_ALLOWED, isAllowed)
.apply();
}
}