1 /* 2 * Copyright (C) 2017 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 package com.android.car.settings.accounts; 17 18 import android.accounts.Account; 19 import android.accounts.AccountManager; 20 import android.accounts.AccountManagerCallback; 21 import android.accounts.AuthenticatorException; 22 import android.accounts.OperationCanceledException; 23 import android.app.Activity; 24 import android.app.Dialog; 25 import android.content.Context; 26 import android.content.DialogInterface; 27 import android.content.Intent; 28 import android.content.pm.UserInfo; 29 import android.os.Bundle; 30 import android.os.UserHandle; 31 32 import androidx.annotation.VisibleForTesting; 33 import androidx.annotation.XmlRes; 34 import androidx.fragment.app.Fragment; 35 36 import com.android.car.settings.R; 37 import com.android.car.settings.common.ErrorDialog; 38 import com.android.car.settings.common.Logger; 39 import com.android.car.settings.common.SettingsFragment; 40 import com.android.car.settings.users.UserHelper; 41 import com.android.car.ui.AlertDialogBuilder; 42 import com.android.car.ui.preference.CarUiDialogFragment; 43 import com.android.car.ui.toolbar.MenuItem; 44 import com.android.settingslib.accounts.AuthenticatorHelper; 45 46 import java.io.IOException; 47 import java.util.Arrays; 48 import java.util.Collections; 49 import java.util.List; 50 51 /** 52 * Shows account details, and delete account option. 53 */ 54 public class AccountDetailsFragment extends SettingsFragment implements 55 AuthenticatorHelper.OnAccountsUpdateListener { 56 public static final String EXTRA_ACCOUNT = "extra_account"; 57 public static final String EXTRA_ACCOUNT_LABEL = "extra_account_label"; 58 public static final String EXTRA_USER_INFO = "extra_user_info"; 59 60 private Account mAccount; 61 private UserInfo mUserInfo; 62 private AuthenticatorHelper mAuthenticatorHelper; 63 private MenuItem mRemoveButton; 64 65 /** 66 * Creates a new AccountDetailsFragment. 67 * 68 * <p>Passes the provided account, label, and user info to the fragment via fragment arguments. 69 */ newInstance(Account account, CharSequence label, UserInfo userInfo)70 public static AccountDetailsFragment newInstance(Account account, CharSequence label, 71 UserInfo userInfo) { 72 AccountDetailsFragment 73 accountDetailsFragment = new AccountDetailsFragment(); 74 Bundle bundle = new Bundle(); 75 bundle.putParcelable(EXTRA_ACCOUNT, account); 76 bundle.putCharSequence(EXTRA_ACCOUNT_LABEL, label); 77 bundle.putParcelable(EXTRA_USER_INFO, userInfo); 78 accountDetailsFragment.setArguments(bundle); 79 return accountDetailsFragment; 80 } 81 82 @Override 83 @XmlRes getPreferenceScreenResId()84 protected int getPreferenceScreenResId() { 85 return R.xml.account_details_fragment; 86 } 87 88 @Override getToolbarMenuItems()89 protected List<MenuItem> getToolbarMenuItems() { 90 return Collections.singletonList(mRemoveButton); 91 } 92 93 @Override onAttach(Context context)94 public void onAttach(Context context) { 95 super.onAttach(context); 96 97 mAccount = getArguments().getParcelable(EXTRA_ACCOUNT); 98 mUserInfo = getArguments().getParcelable(EXTRA_USER_INFO); 99 100 use(AccountDetailsPreferenceController.class, R.string.pk_account_details) 101 .setAccount(mAccount); 102 use(AccountDetailsPreferenceController.class, R.string.pk_account_details) 103 .setUserHandle(mUserInfo.getUserHandle()); 104 105 use(AccountSyncPreferenceController.class, R.string.pk_account_sync) 106 .setAccount(mAccount); 107 use(AccountDetailsSettingController.class, R.string.pk_account_settings) 108 .setAccount(mAccount); 109 use(AccountSyncPreferenceController.class, R.string.pk_account_sync) 110 .setUserHandle(mUserInfo.getUserHandle()); 111 } 112 113 @Override onCreate(Bundle savedInstanceState)114 public void onCreate(Bundle savedInstanceState) { 115 super.onCreate(savedInstanceState); 116 117 boolean canModifyAccount = UserHelper.getInstance(getContext()) 118 .canCurrentProcessModifyAccounts(); 119 120 mRemoveButton = new MenuItem.Builder(getContext()) 121 .setTitle(R.string.remove_button) 122 .setOnClickListener(i -> onRemoveAccountClicked()) 123 .setVisible(canModifyAccount) 124 .build(); 125 } 126 127 @Override onActivityCreated(Bundle savedInstanceState)128 public void onActivityCreated(Bundle savedInstanceState) { 129 super.onActivityCreated(savedInstanceState); 130 131 // Set the fragment's title 132 getToolbar().setTitle(getArguments().getCharSequence(EXTRA_ACCOUNT_LABEL)); 133 } 134 135 @Override onStart()136 public void onStart() { 137 super.onStart(); 138 139 mAuthenticatorHelper = new AuthenticatorHelper(getContext(), mUserInfo.getUserHandle(), 140 this); 141 mAuthenticatorHelper.listenToAccountUpdates(); 142 } 143 144 @Override onStop()145 public void onStop() { 146 super.onStop(); 147 mAuthenticatorHelper.stopListeningToAccountUpdates(); 148 } 149 150 @Override onAccountsUpdate(UserHandle userHandle)151 public void onAccountsUpdate(UserHandle userHandle) { 152 if (!accountExists()) { 153 // The account was deleted. Pop back. 154 goBack(); 155 } 156 } 157 158 /** Returns whether the account being shown by this fragment still exists. */ 159 @VisibleForTesting accountExists()160 boolean accountExists() { 161 if (mAccount == null) { 162 return false; 163 } 164 165 Account[] accounts = AccountManager.get(getContext()).getAccountsByTypeAsUser(mAccount.type, 166 mUserInfo.getUserHandle()); 167 168 return Arrays.asList(accounts).contains(mAccount); 169 } 170 onRemoveAccountClicked()171 private void onRemoveAccountClicked() { 172 ConfirmRemoveAccountDialogFragment.show(this, mAccount, mUserInfo.getUserHandle()); 173 } 174 175 /** 176 * Dialog to confirm with user about account removal 177 */ 178 public static class ConfirmRemoveAccountDialogFragment extends CarUiDialogFragment { 179 private static final String KEY_ACCOUNT = "account"; 180 private static final String DIALOG_TAG = "confirmRemoveAccount"; 181 private static final Logger LOG = new Logger(ConfirmRemoveAccountDialogFragment.class); 182 private final AccountManagerCallback<Bundle> mCallback = 183 future -> { 184 // If already out of this screen, don't proceed. 185 if (!getTargetFragment().isResumed()) { 186 return; 187 } 188 189 boolean success = false; 190 try { 191 success = 192 future.getResult().getBoolean( 193 AccountManager.KEY_BOOLEAN_RESULT); 194 } catch (OperationCanceledException | IOException | AuthenticatorException e) { 195 LOG.v("removeAccount error: " + e); 196 } 197 final Activity activity = getTargetFragment().getActivity(); 198 if (!success && activity != null && !activity.isFinishing()) { 199 ErrorDialog.show(getTargetFragment(), 200 R.string.remove_account_error_title); 201 } else { 202 getTargetFragment().getFragmentManager().popBackStack(); 203 } 204 }; 205 private Account mAccount; 206 private UserHandle mUserHandle; 207 show( Fragment parent, Account account, UserHandle userHandle)208 public static void show( 209 Fragment parent, Account account, UserHandle userHandle) { 210 final ConfirmRemoveAccountDialogFragment dialog = 211 new ConfirmRemoveAccountDialogFragment(); 212 Bundle bundle = new Bundle(); 213 bundle.putParcelable(KEY_ACCOUNT, account); 214 bundle.putParcelable(Intent.EXTRA_USER, userHandle); 215 dialog.setArguments(bundle); 216 dialog.setTargetFragment(parent, 0); 217 dialog.show(parent.getFragmentManager(), DIALOG_TAG); 218 } 219 220 @Override onCreate(Bundle savedInstanceState)221 public void onCreate(Bundle savedInstanceState) { 222 super.onCreate(savedInstanceState); 223 final Bundle arguments = getArguments(); 224 mAccount = arguments.getParcelable(KEY_ACCOUNT); 225 mUserHandle = arguments.getParcelable(Intent.EXTRA_USER); 226 } 227 228 @Override onCreateDialog(Bundle savedInstanceState)229 public Dialog onCreateDialog(Bundle savedInstanceState) { 230 return new AlertDialogBuilder(getContext()) 231 .setTitle(R.string.really_remove_account_title) 232 .setMessage(R.string.really_remove_account_message) 233 .setNegativeButton(android.R.string.cancel, null) 234 .setPositiveButton(R.string.remove_account_title, this) 235 .create(); 236 } 237 238 @Override onClick(DialogInterface dialog, int which)239 public void onClick(DialogInterface dialog, int which) { 240 Activity activity = getTargetFragment().getActivity(); 241 AccountManager.get(activity).removeAccountAsUser( 242 mAccount, activity, mCallback, null, mUserHandle); 243 dialog.dismiss(); 244 } 245 246 @Override onDialogClosed(boolean positiveResult)247 public void onDialogClosed(boolean positiveResult) {} 248 } 249 } 250